From 1e8bd036c1452004360837b5c3f34a8a633278f9 Mon Sep 17 00:00:00 2001 From: martijn Date: Sat, 30 Jan 2021 08:44:42 +0000 Subject: [PATCH] Fix delay parsing by stealing from strtonum and returning a proper error to the user when an invalid value is entered instead of silently falling back to the default 5s. While here I also capped the upper limit to UINT32_MAX / 1000000 to prevent useconds_t overflow. This hard limits us to 4294s, instead of the current soft limit which just make systat go berserk if you go over it. Reported and original diff by Nick Gasson nick nickg me uk OK cheloha@ Tweaks and OK bluhm@ --- usr.bin/systat/main.c | 76 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/usr.bin/systat/main.c b/usr.bin/systat/main.c index 5a3321ea3aa..31092ec3f9a 100644 --- a/usr.bin/systat/main.c +++ b/usr.bin/systat/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.72 2020/01/12 20:51:08 martijn Exp $ */ +/* $OpenBSD: main.c,v 1.73 2021/01/30 08:44:42 martijn Exp $ */ /* * Copyright (c) 2001, 2007 Can Erkin Acar * Copyright (c) 2001 Daniel Hartmeier @@ -40,9 +40,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -73,6 +75,7 @@ char uloadbuf[TIMEPOS]; int ucount(void); void usage(void); +double strtodnum(const char *, double, double, const char **); /* command prompt */ @@ -323,9 +326,14 @@ void cmd_delay(const char *buf) { double del; - del = atof(buf); + const char *errstr; - if (del > 0) { + if (buf[0] == '\0') + return; + del = strtodnum(buf, 0, UINT32_MAX / 1000000, &errstr); + if (errstr != NULL) + error("s: \"%s\": delay value is %s", buf, errstr); + else { udelay = (useconds_t)(del * 1000000); gotsig_alarm = 1; naptime = del; @@ -414,6 +422,48 @@ gethz(void) hz = cinf.hz; } +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +double +strtodnum(const char *nptr, double minval, double maxval, const char **errstrp) +{ + double d = 0; + int error = 0; + char *ep; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) { + error = INVALID; + } else { + d = strtod(nptr, &ep); + if (nptr == ep || *ep != '\0') + error = INVALID; + else if ((d == -HUGE_VAL && errno == ERANGE) || d < minval) + error = TOOSMALL; + else if ((d == HUGE_VAL && errno == ERANGE) || d > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + d = 0; + + return (d); +} + int main(int argc, char *argv[]) { @@ -421,7 +471,7 @@ main(int argc, char *argv[]) const char *errstr; extern char *optarg; extern int optind; - double delay = 5; + double delay = 5, del; char *viewstr = NULL; @@ -475,9 +525,11 @@ main(int argc, char *argv[]) nflag = 1; break; case 's': - delay = atof(optarg); - if (delay <= 0) - delay = 5; + delay = strtodnum(optarg, 0, UINT32_MAX / 1000000, + &errstr); + if (errstr != NULL) + errx(1, "-s \"%s\": delay value is %s", optarg, + errstr); break; case 'w': rawwidth = strtonum(optarg, 1, MAX_LINE_BUF-1, &errstr); @@ -497,16 +549,16 @@ main(int argc, char *argv[]) argv += optind; if (argc == 1) { - double del = atof(argv[0]); - if (del == 0) + del = strtodnum(argv[0], 0, UINT32_MAX / 1000000, &errstr); + if (errstr != NULL) viewstr = argv[0]; else delay = del; } else if (argc == 2) { viewstr = argv[0]; - delay = atof(argv[1]); - if (delay <= 0) - delay = 5; + delay = strtodnum(argv[1], 0, UINT32_MAX / 1000000, &errstr); + if (errstr != NULL) + errx(1, "\"%s\": delay value is %s", argv[1], errstr); } udelay = (useconds_t)(delay * 1000000.0); -- 2.20.1