Fix delay parsing by stealing from strtonum and returning a proper error to
authormartijn <martijn@openbsd.org>
Sat, 30 Jan 2021 08:44:42 +0000 (08:44 +0000)
committermartijn <martijn@openbsd.org>
Sat, 30 Jan 2021 08:44:42 +0000 (08:44 +0000)
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 <at> nickg <dot> me <dot> uk

OK cheloha@
Tweaks and OK bluhm@

usr.bin/systat/main.c

index 5a3321e..31092ec 100644 (file)
@@ -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
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <math.h>
 #include <netdb.h>
 #include <signal.h>
 #include <stdio.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
@@ -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);