]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: Add convtime_double() that converts a string interval,
authordjm@openbsd.org <djm@openbsd.org>
Fri, 5 Dec 2025 07:43:12 +0000 (07:43 +0000)
committerDamien Miller <djm@mindrot.org>
Fri, 5 Dec 2025 08:19:28 +0000 (19:19 +1100)
such as "3w2d4h5m10.5s", into a floating point number of seconds.

Reimplement the existing convtime() function using convtime_double()
(it just drops the fractional seconds)

lots of feedback deraadt@ / dtucker@; ok deraadt@

OpenBSD-Commit-ID: 053cdd0c72325a20efc6613caa847473fb89e36f

misc.c
misc.h

diff --git a/misc.c b/misc.c
index 15d7fc4dfe3c654c4e5904bd05376546938c7f0b..aff5b3fc38c1dd8798dab048214dc811c08ae703 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.c,v 1.209 2025/11/06 01:31:11 djm Exp $ */
+/* $OpenBSD: misc.c,v 1.210 2025/12/05 07:43:12 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2005-2020 Damien Miller.  All rights reserved.
@@ -622,25 +622,22 @@ a2tun(const char *s, int *remote)
        return (tun);
 }
 
-#define SECONDS                1
+#define SECONDS                1.0
 #define MINUTES                (SECONDS * 60)
 #define HOURS          (MINUTES * 60)
 #define DAYS           (HOURS * 24)
 #define WEEKS          (DAYS * 7)
 
-static char *
-scandigits(char *s)
-{
-       while (isdigit((unsigned char)*s))
-               s++;
-       return s;
-}
-
 /*
- * Convert a time string into seconds; format is
- * a sequence of:
+ * Convert an interval/duration time string into seconds, which may include
+ * fractional seconds.
+ *
+ * The format is a sequence of:
  *      time[qualifier]
  *
+ * This supports fractional values for the seconds value only. All other
+ * values must be integers.
+ *
  * Valid time qualifiers are:
  *      <none>  seconds
  *      s|S     seconds
@@ -650,44 +647,46 @@ scandigits(char *s)
  *      w|W     weeks
  *
  * Examples:
- *      90m     90 minutes
- *      1h30m   90 minutes
- *      2d      2 days
- *      1w      1 week
+ *      90m      90 minutes
+ *      1h30m    90 minutes
+ *      1.5s     1.5 seconds
+ *      2d       2 days
+ *      1w       1 week
  *
- * Return -1 if time string is invalid.
+ * Returns <0.0 if the time string is invalid.
  */
-int
-convtime(const char *s)
+double
+convtime_double(const char *s)
 {
-       int secs, total = 0, multiplier;
-       char *p, *os, *np, c = 0;
-       const char *errstr;
+       double val, total_sec = 0.0, multiplier;
+       const char *p, *start_p;
+       char *endp;
+       int seen_seconds = 0;
 
        if (s == NULL || *s == '\0')
-               return -1;
-       p = os = strdup(s);     /* deal with const */
-       if (os == NULL)
-               return -1;
-
-       while (*p) {
-               np = scandigits(p);
-               if (np) {
-                       c = *np;
-                       *np = '\0';
-               }
-               secs = (int)strtonum(p, 0, INT_MAX, &errstr);
-               if (errstr)
-                       goto fail;
-               *np = c;
-
-               multiplier = 1;
-               switch (c) {
+               return -1.0;
+
+       for (p = s; *p != '\0';) {
+               if (!isdigit((unsigned char)*p) && *p != '.')
+                       return -1.0;
+
+               errno = 0;
+               if ((val = strtod(p, &endp)) < 0 || errno != 0 || p == endp)
+                       return -1.0;
+               /* Allow only decimal forms */
+               if (p + strspn(p, "0123456789.") != endp)
+                       return -1.0;
+               start_p = p;
+               p = endp;
+
+               switch (*p) {
                case '\0':
-                       np--;   /* back up */
-                       break;
+                       /* FALLTHROUGH */
                case 's':
                case 'S':
+                       if (seen_seconds++)
+                               return -1.0;
+                       multiplier = SECONDS;
                        break;
                case 'm':
                case 'M':
@@ -706,23 +705,44 @@ convtime(const char *s)
                        multiplier = WEEKS;
                        break;
                default:
-                       goto fail;
+                       return -1.0;
                }
-               if (secs > INT_MAX / multiplier)
-                       goto fail;
-               secs *= multiplier;
-               if  (total > INT_MAX - secs)
-                       goto fail;
-               total += secs;
-               if (total < 0)
-                       goto fail;
-               p = ++np;
-       }
-       free(os);
-       return total;
-fail:
-       free(os);
-       return -1;
+
+               /* Special handling if this was a decimal */
+               if (memchr(start_p, '.', endp - start_p) != NULL) {
+                       /* Decimal point present */
+                       if (multiplier > 1.0)
+                               return -1.0; /* No fractionals for non-seconds */
+                       /* For seconds, ensure digits follow */
+                       if (!isdigit((unsigned char)*(endp - 1)))
+                               return -1.0;
+               }
+
+               total_sec += val * multiplier;
+
+               if (*p != '\0')
+                       p++;
+       }
+       return total_sec;
+}
+
+/*
+ * Same as convtime_double() above but fractional seconds are ignored.
+ * Return -1 if time string is invalid.
+ */
+int
+convtime(const char *s)
+{
+       double sec_val;
+
+       if ((sec_val = convtime_double(s)) < 0.0)
+               return -1;
+
+       /* Check for overflow into int */
+       if (sec_val < 0 || sec_val > INT_MAX)
+               return -1;
+
+       return (int)sec_val;
 }
 
 #define TF_BUFS        8
diff --git a/misc.h b/misc.h
index f106be18f6381d12cc1aa561d161471a87e21ffd..5b401c5c444cecb40679a37aecaef190e3440cc1 100644 (file)
--- a/misc.h
+++ b/misc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.h,v 1.113 2025/11/06 01:31:11 djm Exp $ */
+/* $OpenBSD: misc.h,v 1.114 2025/12/05 07:43:12 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -81,7 +81,9 @@ char  *colon(char *);
 int     parse_user_host_path(const char *, char **, char **, char **);
 int     parse_user_host_port(const char *, char **, char **, int *);
 int     parse_uri(const char *, const char *, char **, char **, int *, char **);
+double  convtime_double(const char *);
 int     convtime(const char *);
+double  convtime_double(const char *);
 const char *fmt_timeframe(time_t t);
 int     tilde_expand(const char *, uid_t, char **);
 char   *tilde_expand_filename(const char *, uid_t);