From 06170b202d9612105d394ffb4e32b2799daf8430 Mon Sep 17 00:00:00 2001
From: "Christian W. Zuckschwerdt" <christian@zuckschwerdt.org>
Date: Tue, 12 Mar 2024 19:00:20 +0100
Subject: [PATCH] Add strict checks to flex argument parsing

---
 conf/DrivewayAlarm_I8-W1901.conf |  2 +-
 src/devices/flex.c               | 94 ++++++++++++++++++++++++--------
 2 files changed, 73 insertions(+), 23 deletions(-)

diff --git a/conf/DrivewayAlarm_I8-W1901.conf b/conf/DrivewayAlarm_I8-W1901.conf
index 63a78481..465eed67 100644
--- a/conf/DrivewayAlarm_I8-W1901.conf
+++ b/conf/DrivewayAlarm_I8-W1901.conf
@@ -49,7 +49,7 @@ decoder {
         s=188,
         l=588,
         r=7000,
-        g=900
+        g=900,
         t=160,
         bits>=26,
         repeats>=3,
diff --git a/src/devices/flex.c b/src/devices/flex.c
index ab1c099d..48e75290 100644
--- a/src/devices/flex.c
+++ b/src/devices/flex.c
@@ -424,6 +424,56 @@ static void help(void)
     exit(0);
 }
 
+static float parse_atoiv(char const *str, int def, char const *error_hint)
+{
+    if (!str) {
+        return def;
+    }
+
+    if (!*str) {
+        return def;
+    }
+
+    char *endptr;
+    int val = strtol(str, &endptr, 10);
+
+    if (str == endptr) {
+        fprintf(stderr, "%sinvalid number argument (%s)\n", error_hint, str);
+        exit(1);
+    }
+
+    return val;
+}
+
+static float parse_float(char const *str, char const *error_hint)
+{
+    if (!str) {
+        fprintf(stderr, "%smissing number argument\n", error_hint);
+        exit(1);
+    }
+
+    if (!*str) {
+        fprintf(stderr, "%sempty number argument\n", error_hint);
+        exit(1);
+    }
+
+    char *endptr;
+    double val = strtod(str, &endptr);
+
+    if (str == endptr) {
+        fprintf(stderr, "%sinvalid number argument (%s)\n", error_hint, str);
+        exit(1);
+    }
+
+    if (*endptr != '\0') {
+        fprintf(stderr, "%strailing characters in number argument (%s)\n", error_hint, str);
+        exit(1);
+    }
+
+    return val;
+
+}
+
 static unsigned parse_modulation(char const *str)
 {
     if (!strcasecmp(str, "OOK_MC_ZEROBIT"))
@@ -619,45 +669,45 @@ r_device *flex_create_device(char *spec)
         else if (!strcasecmp(key, "m") || !strcasecmp(key, "modulation"))
             dev->modulation = parse_modulation(val);
         else if (!strcasecmp(key, "s") || !strcasecmp(key, "short"))
-            dev->short_width = atoi(val);
+            dev->short_width = parse_float(val, "short: ");
         else if (!strcasecmp(key, "l") || !strcasecmp(key, "long"))
-            dev->long_width = atoi(val);
+            dev->long_width = parse_float(val, "long: ");
         else if (!strcasecmp(key, "y") || !strcasecmp(key, "sync"))
-            dev->sync_width = atoi(val);
+            dev->sync_width = parse_float(val, "sync: ");
         else if (!strcasecmp(key, "g") || !strcasecmp(key, "gap"))
-            dev->gap_limit = atoi(val);
+            dev->gap_limit = parse_float(val, "gap: ");
         else if (!strcasecmp(key, "r") || !strcasecmp(key, "reset"))
-            dev->reset_limit = atoi(val);
+            dev->reset_limit = parse_float(val, "reset: ");
         else if (!strcasecmp(key, "t") || !strcasecmp(key, "tolerance"))
-            dev->tolerance = atoi(val);
+            dev->tolerance = parse_float(val, "tolerance: ");
         else if (!strcasecmp(key, "prio") || !strcasecmp(key, "priority"))
-            dev->priority = atoi(val);
+            dev->priority = parse_atoiv(val, 0, "priority: ");
 
         else if (!strcasecmp(key, "bits>"))
-            params->min_bits = val ? atoi(val) : 0;
+            params->min_bits = parse_atoiv(val, 0, "bits: ");
         else if (!strcasecmp(key, "bits<"))
-            params->max_bits = val ? atoi(val) : 0;
+            params->max_bits = parse_atoiv(val, 0, "bits: ");
         else if (!strcasecmp(key, "bits"))
-            params->min_bits = params->max_bits = val ? atoi(val) : 0;
+            params->min_bits = params->max_bits = parse_atoiv(val, 0, "bits:");
 
         else if (!strcasecmp(key, "rows>"))
-            params->min_rows = val ? atoi(val) : 0;
+            params->min_rows = parse_atoiv(val, 0, "rows: ");
         else if (!strcasecmp(key, "rows<"))
-            params->max_rows = val ? atoi(val) : 0;
+            params->max_rows = parse_atoiv(val, 0, "rows: ");
         else if (!strcasecmp(key, "rows"))
-            params->min_rows = params->max_rows = val ? atoi(val) : 0;
+            params->min_rows = params->max_rows = parse_atoiv(val, 0, "rows: ");
 
         else if (!strcasecmp(key, "repeats>"))
-            params->min_repeats = val ? atoi(val) : 0;
+            params->min_repeats = parse_atoiv(val, 0, "repeats: ");
         else if (!strcasecmp(key, "repeats<"))
-            params->max_repeats = val ? atoi(val) : 0;
+            params->max_repeats = parse_atoiv(val, 0, "repeats: ");
         else if (!strcasecmp(key, "repeats"))
-            params->min_repeats = params->max_repeats = val ? atoi(val) : 0;
+            params->min_repeats = params->max_repeats = parse_atoiv(val, 0, "repeats: ");
 
         else if (!strcasecmp(key, "invert"))
-            params->invert = val ? atoi(val) : 1;
+            params->invert = parse_atoiv(val, 1, "invert: ");
         else if (!strcasecmp(key, "reflect"))
-            params->reflect = val ? atoi(val) : 1;
+            params->reflect = parse_atoiv(val, 1, "reflect: ");
 
         else if (!strcasecmp(key, "match"))
             params->match_len = parse_bits(val, params->match_bits);
@@ -666,15 +716,15 @@ r_device *flex_create_device(char *spec)
             params->preamble_len = parse_bits(val, params->preamble_bits);
 
         else if (!strcasecmp(key, "countonly"))
-            params->count_only = val ? atoi(val) : 1;
+            params->count_only = parse_atoiv(val, 1, "countonly: ");
 
         else if (!strcasecmp(key, "unique"))
-            params->unique = val ? atoi(val) : 1;
+            params->unique = parse_atoiv(val, 1, "unique: ");
 
         else if (!strcasecmp(key, "decode_uart"))
-            params->decode_uart = val ? atoi(val) : 1;
+            params->decode_uart = parse_atoiv(val, 1, "decode_uart: ");
         else if (!strcasecmp(key, "decode_dm"))
-            params->decode_dm = val ? atoi(val) : 1;
+            params->decode_dm = parse_atoiv(val, 1, "decode_dm: ");
 
         else if (!strcasecmp(key, "symbol_zero"))
             params->symbol_zero = parse_symbol(val);