]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add more validity checks to the isc_time API
authorOndřej Surý <ondrej@isc.org>
Wed, 26 Jan 2022 11:08:10 +0000 (12:08 +0100)
committerMartin Basti <mbasti@isc.org>
Wed, 22 Jul 2026 09:17:36 +0000 (11:17 +0200)
This commits backport few things from the 64-bit time branch:

* Add ISC_VALID_TIME() and ISC_VALID_INTERVAL() macros that checks
  the valid range for nanoseconds

* Add more specific buffer sizes for formatting the dates and require
  the buffers passed to the formatting functions to be at least that
  big.

* When error happens when formatting the string, always return a valid
  buffer with formatted mock datetime string instead of garbage.
  Because the formatting functions doesn't return status, the caller
  could print garbage (unterminated string).

lib/isc/include/isc/time.h
lib/isc/time.c

index 9fa025c9f4a32f738316a66d8b7b01705c263368..dcc2285de1e0075d312004c8b862f0dc58ac20c3 100644 (file)
@@ -30,6 +30,15 @@ constexpr unsigned int US_PER_SEC = 1000 * 1000;
 constexpr unsigned int NS_PER_MS = 1000 * 1000;
 constexpr unsigned int NS_PER_SEC = 1000 * 1000 * 1000;
 
+#define ISC_FORMATTIMESTAMP_SIZE      sizeof("99-Bad-9999 99:99:99.999")
+#define ISC_FORMATISO8601L_SIZE              sizeof("9999-99-99T99:99:99")
+#define ISC_FORMATISO8601LMS_SIZE     sizeof("9999-99-99T99:99:99.999")
+#define ISC_FORMATISO8601_SIZE       sizeof("9999-99-99T99:99:99Z")
+#define ISC_FORMATISO8601MS_SIZE      sizeof("9999-99-99T99:99:99.999Z")
+#define ISC_FORMATISO8601US_SIZE      sizeof("9999-99-99T99:99:99.999999Z")
+#define ISC_FORMATISO8601TZMS_SIZE    sizeof("9999-99-99T99:99:99.999+99:99")
+#define ISC_FORMATSHORTTIMESTAMP_SIZE sizeof("99999999999999999")
+
 /*
  * ISC_FORMATHTTPTIMESTAMP_SIZE needs to be 30 in C locale and potentially
  * more for other locales to handle longer national abbreviations when
index 90afabfc4205e6d66cb3cb46a21e5eae3fa4b727..9806298e676df2a38b10d7d5453e515342b6c95d 100644 (file)
 #include <isc/tm.h>
 #include <isc/util.h>
 
+#define ISC_VALID_TIME(t) (t != NULL && t->nanoseconds < NS_PER_SEC)
+
+#define ISC_VALID_INTERVAL ISC_VALID_TIME
+
 #if defined(CLOCK_REALTIME)
 #define CLOCKSOURCE_HIRES CLOCK_REALTIME
 #endif /* #if defined(CLOCK_REALTIME) */
@@ -70,8 +74,7 @@ isc_time_settoepoch(isc_time_t *t) {
 
 bool
 isc_time_isepoch(const isc_time_t *t) {
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
 
        if (t->seconds == 0 && t->nanoseconds == 0) {
                return true;
@@ -130,27 +133,26 @@ isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i) {
        struct timespec ts;
 
        REQUIRE(t != NULL);
-       REQUIRE(i != NULL);
-       INSIST(i->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_INTERVAL(i));
 
        if (clock_gettime(CLOCKSOURCE, &ts) == -1) {
-               UNEXPECTED_SYSERROR(errno, "clock_gettime()");
+               char strbuf[ISC_STRERRORSIZE];
+               strerror_r(errno, strbuf, sizeof(strbuf));
+               UNEXPECTED_ERROR("%s", strbuf);
                return ISC_R_UNEXPECTED;
        }
 
-       if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= (long)NS_PER_SEC) {
+       if ((ts.tv_sec < 0 || ts.tv_sec > UINT_MAX) ||
+           (ts.tv_nsec < 0 || ts.tv_nsec >= (long)NS_PER_SEC))
+       {
                return ISC_R_UNEXPECTED;
        }
 
        /*
         * Ensure the resulting seconds value fits in the size of an
-        * unsigned int.  (It is written this way as a slight optimization;
-        * note that even if both values == INT_MAX, then when added
-        * and getting another 1 added below the result is UINT_MAX.)
+        * unsigned int.
         */
-       if ((ts.tv_sec > INT_MAX || i->seconds > INT_MAX) &&
-           ((long long)ts.tv_sec + i->seconds > UINT_MAX))
-       {
+       if ((unsigned int)ts.tv_sec > UINT_MAX - i->seconds) {
                return ISC_R_RANGE;
        }
 
@@ -166,8 +168,8 @@ isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i) {
 
 int
 isc_time_compare(const isc_time_t *t1, const isc_time_t *t2) {
-       REQUIRE(t1 != NULL && t2 != NULL);
-       INSIST(t1->nanoseconds < NS_PER_SEC && t2->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t1));
+       REQUIRE(ISC_VALID_TIME(t2));
 
        if (t1->seconds < t2->seconds) {
                return -1;
@@ -186,8 +188,9 @@ isc_time_compare(const isc_time_t *t1, const isc_time_t *t2) {
 
 isc_result_t
 isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result) {
-       REQUIRE(t != NULL && i != NULL && result != NULL);
-       REQUIRE(t->nanoseconds < NS_PER_SEC && i->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
+       REQUIRE(ISC_VALID_INTERVAL(i));
+       REQUIRE(result != NULL);
 
        /* Seconds */
        if (ckd_add(&result->seconds, t->seconds, i->seconds)) {
@@ -210,8 +213,9 @@ isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result) {
 isc_result_t
 isc_time_subtract(const isc_time_t *t, const isc_interval_t *i,
                  isc_time_t *result) {
-       REQUIRE(t != NULL && i != NULL && result != NULL);
-       REQUIRE(t->nanoseconds < NS_PER_SEC && i->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
+       REQUIRE(ISC_VALID_INTERVAL(i));
+       REQUIRE(result != NULL);
 
        /* Seconds */
        if (ckd_sub(&result->seconds, t->seconds, i->seconds)) {
@@ -237,8 +241,8 @@ uint64_t
 isc_time_microdiff(const isc_time_t *t1, const isc_time_t *t2) {
        uint64_t i1, i2, i3;
 
-       REQUIRE(t1 != NULL && t2 != NULL);
-       INSIST(t1->nanoseconds < NS_PER_SEC && t2->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t1));
+       REQUIRE(ISC_VALID_TIME(t2));
 
        i1 = (uint64_t)t1->seconds * NS_PER_SEC + t1->nanoseconds;
        i2 = (uint64_t)t2->seconds * NS_PER_SEC + t2->nanoseconds;
@@ -259,8 +263,7 @@ isc_time_microdiff(const isc_time_t *t1, const isc_time_t *t2) {
 
 uint32_t
 isc_time_seconds(const isc_time_t *t) {
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
 
        return (uint32_t)t->seconds;
 }
@@ -269,8 +272,7 @@ isc_result_t
 isc_time_secondsastimet(const isc_time_t *t, time_t *secondsp) {
        time_t seconds;
 
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
 
        /*
         * Ensure that the number of seconds represented by t->seconds
@@ -305,17 +307,14 @@ isc_time_secondsastimet(const isc_time_t *t, time_t *secondsp) {
 
 uint32_t
 isc_time_nanoseconds(const isc_time_t *t) {
-       REQUIRE(t != NULL);
-
-       ENSURE(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
 
        return (uint32_t)t->nanoseconds;
 }
 
 uint32_t
 isc_time_miliseconds(const isc_time_t *t) {
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
 
        return (t->seconds * MS_PER_SEC) + (t->nanoseconds / NS_PER_MS);
 }
@@ -326,19 +325,17 @@ isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len) {
        unsigned int flen;
        struct tm tm;
 
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
        REQUIRE(buf != NULL);
-       REQUIRE(len > 0);
+       REQUIRE(len >= ISC_FORMATTIMESTAMP_SIZE);
 
        now = (time_t)t->seconds;
        flen = strftime(buf, len, "%d-%b-%Y %X", localtime_r(&now, &tm));
-       INSIST(flen < len);
-       if (flen != 0) {
+       if (flen == 0) {
+               strlcpy(buf, "99-Bad-9999 99:99:99.999", len);
+       } else {
                snprintf(buf + flen, len - flen, ".%03u",
                         t->nanoseconds / NS_PER_MS);
-       } else {
-               strlcpy(buf, "99-Bad-9999 99:99:99.999", len);
        }
 }
 
@@ -348,10 +345,9 @@ isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len) {
        unsigned int flen;
        struct tm tm;
 
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
        REQUIRE(buf != NULL);
-       REQUIRE(len > 0);
+       REQUIRE(len >= ISC_FORMATHTTPTIMESTAMP_SIZE);
 
        /*
         * 5 spaces, 1 comma, 3 GMT, 2 %d, 4 %Y, 8 %H:%M:%S, 3+ %a, 3+
@@ -360,7 +356,9 @@ isc_time_formathttptimestamp(const isc_time_t *t, char *buf, unsigned int len) {
        now = (time_t)t->seconds;
        flen = strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT",
                        gmtime_r(&now, &tm));
-       INSIST(flen < len);
+       if (flen == 0) {
+               strlcpy(buf, "Bad, 99 Bad 9999 99:99:99 GMT", len);
+       }
 }
 
 isc_result_t
@@ -390,15 +388,15 @@ isc_time_formatISO8601Lms(const isc_time_t *t, char *buf, unsigned int len) {
        unsigned int flen;
        struct tm tm;
 
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
        REQUIRE(buf != NULL);
-       REQUIRE(len > 0);
+       REQUIRE(len >= ISC_FORMATISO8601LMS_SIZE);
 
        now = (time_t)t->seconds;
        flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%S", localtime_r(&now, &tm));
-       INSIST(flen < len);
-       if (flen > 0U && len - flen >= 6) {
+       if (flen == 0) {
+               strlcpy(buf, "9999-99-99T99:99:99.999", len);
+       } else {
                snprintf(buf + flen, len - flen, ".%03u",
                         t->nanoseconds / NS_PER_MS);
        }
@@ -412,10 +410,9 @@ isc_time_formatISO8601TZms(const isc_time_t *t, char *buf, unsigned int len) {
        unsigned int flen;
        struct tm tm;
 
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
        REQUIRE(buf != NULL);
-       REQUIRE(len > 0);
+       REQUIRE(len >= ISC_FORMATISO8601TZMS_SIZE);
 
        now = (time_t)t->seconds;
        flen = strftime(strftime_buf, len, "%Y-%m-%dT%H:%M:%S.xxx%z",
@@ -438,14 +435,15 @@ isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len) {
        unsigned int flen;
        struct tm tm;
 
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
        REQUIRE(buf != NULL);
-       REQUIRE(len > 0);
+       REQUIRE(len >= ISC_FORMATISO8601_SIZE);
 
        now = (time_t)t->seconds;
        flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm));
-       INSIST(flen < len);
+       if (flen == 0) {
+               strlcpy(buf, "9999-99-99T99:99:99Z", len);
+       }
 }
 
 void
@@ -454,16 +452,15 @@ isc_time_formatISO8601ms(const isc_time_t *t, char *buf, unsigned int len) {
        unsigned int flen;
        struct tm tm;
 
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
        REQUIRE(buf != NULL);
-       REQUIRE(len > 0);
+       REQUIRE(len >= ISC_FORMATISO8601MS_SIZE);
 
        now = (time_t)t->seconds;
-       flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm));
-       INSIST(flen < len);
-       if (flen > 0U && len - flen >= 5) {
-               flen -= 1; /* rewind one character (Z) */
+       flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%S", gmtime_r(&now, &tm));
+       if (flen == 0) {
+               strlcpy(buf, "9999-99-99T99:99:99.999Z", len);
+       } else {
                snprintf(buf + flen, len - flen, ".%03uZ",
                         t->nanoseconds / NS_PER_MS);
        }
@@ -475,16 +472,15 @@ isc_time_formatISO8601us(const isc_time_t *t, char *buf, unsigned int len) {
        unsigned int flen;
        struct tm tm;
 
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
        REQUIRE(buf != NULL);
-       REQUIRE(len > 0);
+       REQUIRE(len >= ISC_FORMATISO8601US_SIZE);
 
        now = (time_t)t->seconds;
-       flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm));
-       INSIST(flen < len);
-       if (flen > 0U && len - flen >= 5) {
-               flen -= 1; /* rewind one character (Z) */
+       flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%S", gmtime_r(&now, &tm));
+       if (flen == 0) {
+               strlcpy(buf, "9999-99-99T99:99:99.999999Z", len);
+       } else {
                snprintf(buf + flen, len - flen, ".%06uZ",
                         t->nanoseconds / NS_PER_US);
        }
@@ -497,15 +493,15 @@ isc_time_formatshorttimestamp(const isc_time_t *t, char *buf,
        unsigned int flen;
        struct tm tm;
 
-       REQUIRE(t != NULL);
-       INSIST(t->nanoseconds < NS_PER_SEC);
+       REQUIRE(ISC_VALID_TIME(t));
        REQUIRE(buf != NULL);
-       REQUIRE(len > 0);
+       REQUIRE(len >= ISC_FORMATSHORTTIMESTAMP_SIZE);
 
        now = (time_t)t->seconds;
        flen = strftime(buf, len, "%Y%m%d%H%M%S", gmtime_r(&now, &tm));
-       INSIST(flen < len);
-       if (flen > 0U && len - flen >= 5) {
+       if (flen == 0) {
+               strlcpy(buf, "99999999999999999", len);
+       } else {
                snprintf(buf + flen, len - flen, "%03u",
                         t->nanoseconds / NS_PER_MS);
        }