]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
time-util: make parse_sec() not accept "12.34.56"
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 6 Nov 2018 03:36:54 +0000 (12:36 +0900)
committerLennart Poettering <lennart@poettering.net>
Tue, 6 Nov 2018 16:24:47 +0000 (19:24 +0300)
This also changes the rational number treatment.
So, the limitations introduced by 8079c90333422bbc008b68a9b7cefbdb8a15a4e9
and f6a178e91dd5fccf43f659eca887788fd5dcdccf are relaxed.

Fixes #10619.

src/basic/time-util.c
src/test/test-time-util.c

index e3516842193f9a9b14b9a7cef366b2ae0b50c48b..5dc3a7990ba35cea2a41c18b5b75d4c30a86deb7 100644 (file)
@@ -868,7 +868,7 @@ int parse_timestamp(const char *t, usec_t *usec) {
         return tmp.return_value;
 }
 
-static char* extract_multiplier(char *p, usec_t *multiplier) {
+static const char* extract_multiplier(const char *p, usec_t *multiplier) {
         static const struct {
                 const char *suffix;
                 usec_t usec;
@@ -942,8 +942,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
 
         for (;;) {
                 usec_t multiplier = default_unit, k;
-                long long l, z = 0;
-                unsigned n = 0;
+                long long l;
                 char *e;
 
                 p += strspn(p, WHITESPACE);
@@ -966,46 +965,47 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
                         return -ERANGE;
 
                 if (*e == '.') {
-                        char *b = e + 1;
-
-                        /* Don't allow "0.-0", "3.+1" or "3. 1" */
-                        if (IN_SET(*b, '-', '+') || isspace(*b))
-                                return -EINVAL;
-
-                        errno = 0;
-                        z = strtoll(b, &e, 10);
-                        if (errno > 0)
-                                return -errno;
-                        if (z < 0)
-                                return -ERANGE;
-                        if (e == b)
-                                return -EINVAL;
-
-                        n = e - b;
-
+                        p = e + 1;
+                        p += strspn(p, DIGITS);
                 } else if (e == p)
                         return -EINVAL;
+                else
+                        p = e;
 
-                e += strspn(e, WHITESPACE);
-                p = extract_multiplier(e, &multiplier);
+                s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier);
+                if (s == p && *s != '\0')
+                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
+                        return -EINVAL;
 
-                something = true;
+                p = s;
 
+                if ((usec_t) l >= USEC_INFINITY / multiplier)
+                        return -ERANGE;
 
-                k = ((usec_t) -1) / multiplier;
-                if ((usec_t) l + 1 >= k || (usec_t) z >= k)
+                k = (usec_t) l * multiplier;
+                if (k >= USEC_INFINITY - r)
                         return -ERANGE;
 
-                k = (usec_t) z * multiplier;
+                r += k;
 
-                for (; n > 0; n--)
-                        k /= 10;
+                something = true;
 
-                k += (usec_t) l * multiplier;
-                if (k >= ((usec_t) -1) - r)
-                        return -ERANGE;
+                if (*e == '.') {
+                        usec_t m = multiplier / 10;
+                        const char *b;
 
-                r += k;
+                        for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
+                                k = (usec_t) (*b - '0') * m;
+                                if (k >= USEC_INFINITY - r)
+                                        return -ERANGE;
+
+                                r += k;
+                        }
+
+                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
+                        if (b == e + 1)
+                                return -EINVAL;
+                }
         }
 
         *usec = r;
@@ -1032,44 +1032,60 @@ int parse_sec_fix_0(const char *t, usec_t *ret) {
         return r;
 }
 
-int parse_nsec(const char *t, nsec_t *nsec) {
+static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) {
         static const struct {
                 const char *suffix;
                 nsec_t nsec;
         } table[] = {
-                { "seconds", NSEC_PER_SEC },
-                { "second", NSEC_PER_SEC },
-                { "sec", NSEC_PER_SEC },
-                { "s", NSEC_PER_SEC },
+                { "seconds", NSEC_PER_SEC    },
+                { "second",  NSEC_PER_SEC    },
+                { "sec",     NSEC_PER_SEC    },
+                { "s",       NSEC_PER_SEC    },
                 { "minutes", NSEC_PER_MINUTE },
-                { "minute", NSEC_PER_MINUTE },
-                { "min", NSEC_PER_MINUTE },
-                { "months", NSEC_PER_MONTH },
-                { "month", NSEC_PER_MONTH },
-                { "msec", NSEC_PER_MSEC },
-                { "ms", NSEC_PER_MSEC },
-                { "m", NSEC_PER_MINUTE },
-                { "hours", NSEC_PER_HOUR },
-                { "hour", NSEC_PER_HOUR },
-                { "hr", NSEC_PER_HOUR },
-                { "h", NSEC_PER_HOUR },
-                { "days", NSEC_PER_DAY },
-                { "day", NSEC_PER_DAY },
-                { "d", NSEC_PER_DAY },
-                { "weeks", NSEC_PER_WEEK },
-                { "week", NSEC_PER_WEEK },
-                { "w", NSEC_PER_WEEK },
-                { "years", NSEC_PER_YEAR },
-                { "year", NSEC_PER_YEAR },
-                { "y", NSEC_PER_YEAR },
-                { "usec", NSEC_PER_USEC },
-                { "us", NSEC_PER_USEC },
-                { "µs", NSEC_PER_USEC },
-                { "nsec", 1ULL },
-                { "ns", 1ULL },
-                { "", 1ULL }, /* default is nsec */
+                { "minute",  NSEC_PER_MINUTE },
+                { "min",     NSEC_PER_MINUTE },
+                { "months",  NSEC_PER_MONTH  },
+                { "month",   NSEC_PER_MONTH  },
+                { "M",       NSEC_PER_MONTH  },
+                { "msec",    NSEC_PER_MSEC   },
+                { "ms",      NSEC_PER_MSEC   },
+                { "m",       NSEC_PER_MINUTE },
+                { "hours",   NSEC_PER_HOUR   },
+                { "hour",    NSEC_PER_HOUR   },
+                { "hr",      NSEC_PER_HOUR   },
+                { "h",       NSEC_PER_HOUR   },
+                { "days",    NSEC_PER_DAY    },
+                { "day",     NSEC_PER_DAY    },
+                { "d",       NSEC_PER_DAY    },
+                { "weeks",   NSEC_PER_WEEK   },
+                { "week",    NSEC_PER_WEEK   },
+                { "w",       NSEC_PER_WEEK   },
+                { "years",   NSEC_PER_YEAR   },
+                { "year",    NSEC_PER_YEAR   },
+                { "y",       NSEC_PER_YEAR   },
+                { "usec",    NSEC_PER_USEC   },
+                { "us",      NSEC_PER_USEC   },
+                { "µs",      NSEC_PER_USEC   },
+                { "nsec",    1ULL            },
+                { "ns",      1ULL            },
+                { "",        1ULL            }, /* default is nsec */
         };
+        size_t i;
+
+        for (i = 0; i < ELEMENTSOF(table); i++) {
+                char *e;
+
+                e = startswith(p, table[i].suffix);
+                if (e) {
+                        *multiplier = table[i].nsec;
+                        return e;
+                }
+        }
+
+        return p;
+}
 
+int parse_nsec(const char *t, nsec_t *nsec) {
         const char *p, *s;
         nsec_t r = 0;
         bool something = false;
@@ -1091,8 +1107,8 @@ int parse_nsec(const char *t, nsec_t *nsec) {
         }
 
         for (;;) {
-                long long l, z = 0;
-                size_t n = 0, i;
+                nsec_t multiplier = 1, k;
+                long long l;
                 char *e;
 
                 p += strspn(p, WHITESPACE);
@@ -1104,7 +1120,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
                         break;
                 }
 
-                if (*p == '-')
+                if (*p == '-') /* Don't allow "-0" */
                         return -ERANGE;
 
                 errno = 0;
@@ -1115,54 +1131,47 @@ int parse_nsec(const char *t, nsec_t *nsec) {
                         return -ERANGE;
 
                 if (*e == '.') {
-                        char *b = e + 1;
-
-                        if (IN_SET(*b, '-', '+') || isspace(*b))
-                                return -EINVAL;
-
-                        errno = 0;
-                        z = strtoll(b, &e, 10);
-                        if (errno > 0)
-                                return -errno;
-                        if (z < 0)
-                                return -ERANGE;
-                        if (e == b)
-                                return -EINVAL;
-
-                        n = e - b;
-
+                        p = e + 1;
+                        p += strspn(p, DIGITS);
                 } else if (e == p)
                         return -EINVAL;
+                else
+                        p = e;
 
-                e += strspn(e, WHITESPACE);
+                s = extract_nsec_multiplier(p + strspn(p, WHITESPACE), &multiplier);
+                if (s == p && *s != '\0')
+                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
+                        return -EINVAL;
 
-                for (i = 0; i < ELEMENTSOF(table); i++)
-                        if (startswith(e, table[i].suffix)) {
-                                nsec_t k;
+                p = s;
 
-                                k = ((nsec_t) -1) / table[i].nsec;
-                                if ((nsec_t) l + 1 >= k || (nsec_t) z >= k)
-                                        return -ERANGE;
+                if ((nsec_t) l >= NSEC_INFINITY / multiplier)
+                        return -ERANGE;
+
+                k = (nsec_t) l * multiplier;
+                if (k >= NSEC_INFINITY - r)
+                        return -ERANGE;
 
-                                k = (nsec_t) z * table[i].nsec;
+                r += k;
+
+                something = true;
 
-                                for (; n > 0; n--)
-                                        k /= 10;
+                if (*e == '.') {
+                        nsec_t m = multiplier / 10;
+                        const char *b;
 
-                                k += (nsec_t) l * table[i].nsec;
-                                if (k >= ((nsec_t) -1) - r)
+                        for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
+                                k = (nsec_t) (*b - '0') * m;
+                                if (k >= NSEC_INFINITY - r)
                                         return -ERANGE;
 
                                 r += k;
-                                p = e + strlen(table[i].suffix);
-
-                                something = true;
-                                break;
                         }
 
-                if (i >= ELEMENTSOF(table))
-                        return -EINVAL;
-
+                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
+                        if (b == e + 1)
+                                return -EINVAL;
+                }
         }
 
         *nsec = r;
index 00051eb434e5820aa3761e8a3ddbed7af878d80e..2ec2ade3f18e155d85501e276b54d4adf4a5137e 100644 (file)
@@ -39,6 +39,14 @@ static void test_parse_sec(void) {
         assert_se(u == USEC_INFINITY);
         assert_se(parse_sec("+3.1s", &u) >= 0);
         assert_se(u == 3100 * USEC_PER_MSEC);
+        assert_se(parse_sec("3.1s.2", &u) >= 0);
+        assert_se(u == 3300 * USEC_PER_MSEC);
+        assert_se(parse_sec("3.1 .2", &u) >= 0);
+        assert_se(u == 3300 * USEC_PER_MSEC);
+        assert_se(parse_sec("3.1 sec .2 sec", &u) >= 0);
+        assert_se(u == 3300 * USEC_PER_MSEC);
+        assert_se(parse_sec("3.1 sec 1.2 sec", &u) >= 0);
+        assert_se(u == 4300 * USEC_PER_MSEC);
 
         assert_se(parse_sec(" xyz ", &u) < 0);
         assert_se(parse_sec("", &u) < 0);
@@ -56,6 +64,10 @@ static void test_parse_sec(void) {
         assert_se(parse_sec("3.+1s", &u) < 0);
         assert_se(parse_sec("3. 1s", &u) < 0);
         assert_se(parse_sec("3.s", &u) < 0);
+        assert_se(parse_sec("12.34.56", &u) < 0);
+        assert_se(parse_sec("12..34", &u) < 0);
+        assert_se(parse_sec("..1234", &u) < 0);
+        assert_se(parse_sec("1234..", &u) < 0);
 }
 
 static void test_parse_sec_fix_0(void) {
@@ -97,7 +109,7 @@ static void test_parse_time(void) {
         assert_se(u == 5 * USEC_PER_SEC);
 
         assert_se(parse_time("11111111111111y", &u, 1) == -ERANGE);
-        assert_se(parse_time("1.1111111111111y", &u, 1) == -ERANGE);
+        assert_se(parse_time("1.1111111111111y", &u, 1) >= 0);
 }
 
 static void test_parse_nsec(void) {
@@ -129,6 +141,14 @@ static void test_parse_nsec(void) {
         assert_se(u == NSEC_INFINITY);
         assert_se(parse_nsec("+3.1s", &u) >= 0);
         assert_se(u == 3100 * NSEC_PER_MSEC);
+        assert_se(parse_nsec("3.1s.2", &u) >= 0);
+        assert_se(u == 3100 * NSEC_PER_MSEC);
+        assert_se(parse_nsec("3.1 .2s", &u) >= 0);
+        assert_se(u == 200 * NSEC_PER_MSEC + 3);
+        assert_se(parse_nsec("3.1 sec .2 sec", &u) >= 0);
+        assert_se(u == 3300 * NSEC_PER_MSEC);
+        assert_se(parse_nsec("3.1 sec 1.2 sec", &u) >= 0);
+        assert_se(u == 4300 * NSEC_PER_MSEC);
 
         assert_se(parse_nsec(" xyz ", &u) < 0);
         assert_se(parse_nsec("", &u) < 0);
@@ -148,8 +168,12 @@ static void test_parse_nsec(void) {
         assert_se(parse_nsec("3.+1s", &u) < 0);
         assert_se(parse_nsec("3. 1s", &u) < 0);
         assert_se(parse_nsec("3.s", &u) < 0);
+        assert_se(parse_nsec("12.34.56", &u) < 0);
+        assert_se(parse_nsec("12..34", &u) < 0);
+        assert_se(parse_nsec("..1234", &u) < 0);
+        assert_se(parse_nsec("1234..", &u) < 0);
         assert_se(parse_nsec("1111111111111y", &u) == -ERANGE);
-        assert_se(parse_nsec("1.111111111111y", &u) == -ERANGE);
+        assert_se(parse_nsec("1.111111111111y", &u) >= 0);
 }
 
 static void test_format_timespan_one(usec_t x, usec_t accuracy) {