Reject negative and out-of-range values in non-negative integer parsing

This commit is contained in:
Andrea Mazzoleni 2026-09-28 21:03:33 +02:00
parent 99a79b7049
commit 6f74455864
6 changed files with 131 additions and 23 deletions

View file

@ -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);

View file

@ -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 */

View file

@ -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 */

View file

@ -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 */

View file

@ -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 {

View file

@ -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;