From a457542fb1ca8f4cf1ad77fac3d944d3e224e18f Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Wed, 27 Mar 2024 22:13:50 -0400 Subject: [PATCH] common: validate and properly parse floats Fixes: #1665. --- include/common/parse-double.h | 32 +++++++++++ src/common/meson.build | 1 + src/common/parse-double.c | 99 +++++++++++++++++++++++++++++++++++ src/config/rcxml.c | 5 +- src/config/tablet.c | 4 +- 5 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 include/common/parse-double.h create mode 100644 src/common/parse-double.c diff --git a/include/common/parse-double.h b/include/common/parse-double.h new file mode 100644 index 00000000..86499881 --- /dev/null +++ b/include/common/parse-double.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_PARSE_DOUBLE_H +#define LABWC_PARSE_DOUBLE_H +#include +#include + +/** + * set_double() - Parse double-precision value of string. + * @str: String to parse + * @val: Storage for parsed value + * + * Return: true if string was parsed, false if not + * + * NOTE: If this function returns false, the value at *val will be untouched. + */ +bool set_double(const char *str, double *val); + +static inline bool +set_float(const char *str, float *val) +{ + assert(val); + + double d; + if (set_double(str, &d)) { + *val = d; + return true; + } + + return false; +} + +#endif /* LABWC_PARSE_DOUBLE_H */ diff --git a/src/common/meson.build b/src/common/meson.build index 6a90f9b3..250ca347 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -10,6 +10,7 @@ labwc_sources += files( 'mem.c', 'nodename.c', 'parse-bool.c', + 'parse-double.c', 'scaled_font_buffer.c', 'scaled_scene_buffer.c', 'scene-helpers.c', diff --git a/src/common/parse-double.c b/src/common/parse-double.c new file mode 100644 index 00000000..db440d0e --- /dev/null +++ b/src/common/parse-double.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include "common/mem.h" +#include "common/parse-double.h" + +struct dec_separator { + int index; + bool multiple; +}; + +struct converted_double { + double value; + bool valid; +}; + +static struct dec_separator +find_dec_separator(const char *str) +{ + struct dec_separator loc = { + .index = -1, + .multiple = false + }; + + for (int i = 0; *str; i++, str++) { + switch (*str) { + case ',': + case '.': + if (loc.index >= 0) { + loc.multiple = true; + return loc; + } else { + loc.index = i; + } + break; + } + } + + return loc; +} + +static struct converted_double +convert_double(const char *str) +{ + struct converted_double result = { + .value = 0, + .valid = true, + }; + + char *eptr = NULL; + + errno = 0; + result.value = strtod(str, &eptr); + + if (errno) { + wlr_log(WLR_ERROR, "value '%s' is out of range", str); + result.valid = false; + } + + if (*eptr) { + wlr_log(WLR_ERROR, "value '%s' contains trailing garbage", str); + result.valid = false; + } + + return result; +} + +bool +set_double(const char *str, double *val) +{ + assert(str); + assert(val); + + struct dec_separator dloc = find_dec_separator(str); + if (dloc.multiple) { + wlr_log(WLR_ERROR, + "value '%s' contains multiple decimal markers", str); + return false; + } + + char *lstr = NULL; + + if (dloc.index >= 0) { + lstr = xstrdup(str); + struct lconv *lc = localeconv(); + lstr[dloc.index] = *lc->decimal_point; + str = lstr; + } + + struct converted_double conv = convert_double(str); + if (conv.valid) { + *val = conv.value; + } + + free(lstr); + return conv.valid; +} diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 994498c3..62afdc6c 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -21,6 +21,7 @@ #include "common/mem.h" #include "common/nodename.h" #include "common/parse-bool.h" +#include "common/parse-double.h" #include "common/string-helpers.h" #include "config/default-bindings.h" #include "config/keybind.h" @@ -537,7 +538,7 @@ fill_libinput_category(char *nodename, char *content) } else if (!strcasecmp(nodename, "leftHanded")) { set_bool_as_int(content, ¤t_libinput_category->left_handed); } else if (!strcasecmp(nodename, "pointerSpeed")) { - current_libinput_category->pointer_speed = atof(content); + set_float(content, ¤t_libinput_category->pointer_speed); if (current_libinput_category->pointer_speed < -1) { current_libinput_category->pointer_speed = -1; } else if (current_libinput_category->pointer_speed > 1) { @@ -883,7 +884,7 @@ entry(xmlNode *node, char *nodename, char *content) wlr_log(WLR_ERROR, "invalid doubleClickTime"); } } else if (!strcasecmp(nodename, "scrollFactor.mouse")) { - rc.scroll_factor = atof(content); + set_double(content, &rc.scroll_factor); } else if (!strcasecmp(nodename, "name.context.mouse")) { current_mouse_context = content; current_mousebind = NULL; diff --git a/src/config/tablet.c b/src/config/tablet.c index 95e253cf..bc3527a9 100644 --- a/src/config/tablet.c +++ b/src/config/tablet.c @@ -4,6 +4,7 @@ #include #include #include +#include "common/parse-double.h" #include "config/tablet.h" #include "config/rcxml.h" #include "input/tablet_pad.h" @@ -11,7 +12,8 @@ double tablet_get_dbl_if_positive(const char *content, const char *name) { - double value = atof(content); + double value = 0; + set_double(content, &value); if (value < 0) { wlr_log(WLR_ERROR, "Invalid value for tablet area %s", name); return 0;