From 6f744558643c225e69b98b3f110db1b9da222979 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Mon, 28 Sep 2026 21:03:33 +0200 Subject: [PATCH] Reject negative and out-of-range values in non-negative integer parsing --- cmdline/snapraid.c | 20 +++------ cmdline/state.c | 4 +- cmdline/str.c | 105 +++++++++++++++++++++++++++++++++++++++++++++ cmdline/str.h | 9 ++++ cmdline/support.c | 6 +-- cmdline/unixapp.c | 10 ++--- 6 files changed, 131 insertions(+), 23 deletions(-) diff --git a/cmdline/snapraid.c b/cmdline/snapraid.c index 3980abb3..2d806fb9 100644 --- a/cmdline/snapraid.c +++ b/cmdline/snapraid.c @@ -487,15 +487,9 @@ static int parse_option_size(const char* arg, uint64_t* out_size) char* e; uint64_t mult = 1; - while (isspace((unsigned char)*arg)) - ++arg; - - if (*arg == '-') - return -1; - /* parse the number part */ errno = 0; - uint64_t size = strtoull(arg, &e, 10); + uint64_t size = strtonll(arg, &e, 10); if (e == arg || errno == ERANGE) return -1; @@ -701,7 +695,7 @@ int snapraid_main(int argc, char* argv[]) } break; case 'o' : - olderthan = strtou(optarg, &e, 10); + olderthan = strton(optarg, &e, 10); if (e == optarg || *e || olderthan > 1000) { /* LCOV_EXCL_START */ log_fatal(EUSER, "Invalid number of days '%s'\n", optarg); @@ -726,7 +720,7 @@ int snapraid_main(int argc, char* argv[]) break; case 'S' : blockstart_set = 1; - blockstart = strtoull(optarg, &e, 0); + blockstart = strtonll(optarg, &e, 0); if (e == optarg || *e) { /* LCOV_EXCL_START */ log_fatal(EUSER, "Invalid start position '%s'\n", optarg); @@ -736,7 +730,7 @@ int snapraid_main(int argc, char* argv[]) break; case 'B' : blockcount_set = 1; - blockcount = strtoull(optarg, &e, 0); + blockcount = strtonll(optarg, &e, 0); if (e == optarg || *e) { /* LCOV_EXCL_START */ log_fatal(EUSER, "Invalid count number '%s'\n", optarg); @@ -745,7 +739,7 @@ int snapraid_main(int argc, char* argv[]) } break; case 'L' : - opt.io_error_limit = strtou(optarg, &e, 0); + opt.io_error_limit = strton(optarg, &e, 0); if (e == optarg || *e) { /* LCOV_EXCL_START */ log_fatal(EUSER, "Invalid error limit number '%s'\n", optarg); @@ -857,7 +851,7 @@ int snapraid_main(int argc, char* argv[]) opt.gui_touch_before = 1; break; case OPT_GUI_THRESHOLD_REMOVES : - opt.gui_threshold_removes = strtou(optarg, &e, 0); + opt.gui_threshold_removes = strton(optarg, &e, 0); if (e == optarg || *e) { /* LCOV_EXCL_START */ log_fatal(EUSER, "Invalid threshold '%s'\n", optarg); @@ -866,7 +860,7 @@ int snapraid_main(int argc, char* argv[]) } break; case OPT_GUI_THRESHOLD_UPDATES : - opt.gui_threshold_updates = strtou(optarg, &e, 0); + opt.gui_threshold_updates = strton(optarg, &e, 0); if (e == optarg || *e) { /* LCOV_EXCL_START */ log_fatal(EUSER, "Invalid threshold '%s'\n", optarg); diff --git a/cmdline/state.c b/cmdline/state.c index 4d0f3fa3..c3798beb 100644 --- a/cmdline/state.c +++ b/cmdline/state.c @@ -1444,7 +1444,7 @@ void state_config(struct snapraid_state* state, const char* path, const char* co /* LCOV_EXCL_STOP */ } - int val = strtou(buffer, &e, 0); + int val = strton(buffer, &e, 0); if (e && !*e) { /* numerical form */ @@ -1593,7 +1593,7 @@ void state_config(struct snapraid_state* state, const char* path, const char* co /* LCOV_EXCL_STOP */ } - state->autosave = strtoul(buffer, &e, 0); + state->autosave = strtonl(buffer, &e, 0); if (!e || *e) { /* LCOV_EXCL_START */ diff --git a/cmdline/str.c b/cmdline/str.c index 3d147529..aac50593 100644 --- a/cmdline/str.c +++ b/cmdline/str.c @@ -299,6 +299,111 @@ unsigned strtou(const char* nptr, char** endptr, int base) return (unsigned)val; } +unsigned strton(const char* nptr, char** endptr, int base) +{ + const char* p = nptr; + unsigned val; + int save_errno = errno; + + while (isspace((unsigned char)*p)) { + ++p; + } + + if (*p == '-') { + if (endptr) { + *endptr = (char*)nptr; + } + errno = ERANGE; + return (unsigned)-1; + } + + errno = 0; + val = strtou(nptr, endptr, base); + + if (errno == ERANGE) { + if (endptr) { + *endptr = (char*)nptr; + } + return (unsigned)-1; + } + + if (errno == 0) { + errno = save_errno; + } + + return val; +} + +unsigned long strtonl(const char* nptr, char** endptr, int base) +{ + const char* p = nptr; + unsigned long val; + int save_errno = errno; + + while (isspace((unsigned char)*p)) { + ++p; + } + + if (*p == '-') { + if (endptr) { + *endptr = (char*)nptr; + } + errno = ERANGE; + return (unsigned long)-1; + } + + errno = 0; + val = strtoul(nptr, endptr, base); + + if (errno == ERANGE) { + if (endptr) { + *endptr = (char*)nptr; + } + return (unsigned long)-1; + } + + if (errno == 0) { + errno = save_errno; + } + + return val; +} + +unsigned long long strtonll(const char* nptr, char** endptr, int base) +{ + const char* p = nptr; + unsigned long long val; + int save_errno = errno; + + while (isspace((unsigned char)*p)) { + ++p; + } + + if (*p == '-') { + if (endptr) { + *endptr = (char*)nptr; + } + errno = ERANGE; + return (unsigned long long)-1; + } + + errno = 0; + val = strtoull(nptr, endptr, base); + + if (errno == ERANGE) { + if (endptr) { + *endptr = (char*)nptr; + } + return (unsigned long long)-1; + } + + if (errno == 0) { + errno = save_errno; + } + + return val; +} + /****************************************************************************/ /* wnmatch */ diff --git a/cmdline/str.h b/cmdline/str.h index e8cf9014..a11c8a0c 100644 --- a/cmdline/str.h +++ b/cmdline/str.h @@ -115,6 +115,15 @@ int strtoi(const char* nptr, char** endptr, int base); */ unsigned strtou(const char* nptr, char** endptr, int base); +/** + * Convert strings to non-negative unsigned integers. + * Negative or out-of-range values are rejected by returning (type)-1, + * setting errno to ERANGE, and setting endptr to nptr. + */ +unsigned strton(const char* nptr, char** endptr, int base); +unsigned long strtonl(const char* nptr, char** endptr, int base); +unsigned long long strtonll(const char* nptr, char** endptr, int base); + /****************************************************************************/ /* match */ diff --git a/cmdline/support.c b/cmdline/support.c index 50dfdc40..ae01598c 100644 --- a/cmdline/support.c +++ b/cmdline/support.c @@ -2356,21 +2356,21 @@ int smartctl_attribute(OS_FILE* f, const char* file, const char* name, struct sm smart[id].raw = raw; - v = strtoul(norm, &e, 10); + v = strtonl(norm, &e, 10); if (e != norm || *e == 0) { smart[id].norm = v; } else { smart[id].norm = SMART_UNASSIGNED; } - v = strtoul(worst, &e, 10); + v = strtonl(worst, &e, 10); if (e != worst || *e == 0) { smart[id].worst = v; } else { smart[id].worst = SMART_UNASSIGNED; } - v = strtoul(thresh, &e, 10); + v = strtonl(thresh, &e, 10); if (e != thresh || *e == 0) { smart[id].thresh = v; } else { diff --git a/cmdline/unixapp.c b/cmdline/unixapp.c index de8b0ba0..f82cb771 100644 --- a/cmdline/unixapp.c +++ b/cmdline/unixapp.c @@ -520,7 +520,7 @@ static unsigned parse_cpulist(const char* str, unsigned* cpus, unsigned max_cpus break; char* end; - unsigned long start = strtoul(p, &end, 10); + unsigned long start = strtonl(p, &end, 10); if (end == p) break; @@ -528,7 +528,7 @@ static unsigned parse_cpulist(const char* str, unsigned* cpus, unsigned max_cpus p = end; if (*p == '-') { ++p; - stop = strtoul(p, &end, 10); + stop = strtonl(p, &end, 10); if (end == p) break; p = end; @@ -3297,7 +3297,7 @@ static dev_t devread(const char* path) buf[len] = 0; - ma = strtou(buf, &e, 10); + ma = strton(buf, &e, 10); if (*e != ':') { /* LCOV_EXCL_START */ log_error(EEXTERNAL, "Invalid format in '%s' for '%s'.\n", path, buf); @@ -3305,7 +3305,7 @@ static dev_t devread(const char* path) /* LCOV_EXCL_STOP */ } - mi = strtou(e + 1, &e, 10); + mi = strton(e + 1, &e, 10); if (*e != 0 && !isspace((unsigned char)*e)) { /* LCOV_EXCL_START */ log_error(EEXTERNAL, "Invalid format in '%s' for '%s'.\n", path, buf); @@ -3495,7 +3495,7 @@ static int devstat(dev_t device, uint64_t* count) break; *i++ = 0; /* put a terminator */ - v = strtoull(n, &e, 10); + v = strtonll(n, &e, 10); if (*e != 0) { /* LCOV_EXCL_START */ break;