commit f51418aabcc2af47f61a97f818b013ade6e45208 Author: teor teor2345@gmail.com Date: Sun Oct 12 20:50:10 2014 +1100
Avoid overflow in format_time_interval, create unit tests
Fix an instance of integer overflow in format_time_interval() when taking the absolute value of the supplied signed interval value. Fixes bug 13393.
Create unit tests for format_time_interval(). --- .../bug13393-format-time-interval-overflow-test | 6 + src/common/util.c | 6 +- src/test/test_util.c | 423 ++++++++++++++++++++ 3 files changed, 434 insertions(+), 1 deletion(-)
diff --git a/changes/bug13393-format-time-interval-overflow-test b/changes/bug13393-format-time-interval-overflow-test new file mode 100644 index 0000000..cc15572 --- /dev/null +++ b/changes/bug13393-format-time-interval-overflow-test @@ -0,0 +1,6 @@ + o Minor bugfixes: + - Fix an instance of integer overflow in format_time_interval(). + Fixes bug 13393. + + o Minor features (test): + - Create unit tests for format_time_interval(). With bug 13393. diff --git a/src/common/util.c b/src/common/util.c index f4d293c..0ea7095 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1670,7 +1670,11 @@ format_time_interval(char *out, size_t out_len, long interval) { /* We only report seconds if there's no hours. */ long sec = 0, min = 0, hour = 0, day = 0; - if (interval < 0) + + /* -LONG_MIN is LONG_MAX + 1, which causes signed overflow */ + if (interval < -LONG_MAX) + interval = LONG_MAX; + else if (interval < 0) interval = -interval;
if (interval >= 86400) { diff --git a/src/test/test_util.c b/src/test/test_util.c index c67aa71..3fed1f7 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -22,6 +22,7 @@ #include <tchar.h> #endif #include <math.h> +#include <ctype.h>
/* XXXX this is a minimal wrapper to make the unit tests compile with the * changed tor_timegm interface. */ @@ -2068,6 +2069,427 @@ test_util_sscanf(void *arg) ; }
+#define tt_char_op(a,op,b) tt_assert_op_type(a,op,b,char,"%c") +#define tt_ci_char_op(a,op,b) tt_char_op(tolower(a),op,tolower(b)) + +static void +test_util_format_time_interval(void *arg) +{ + /* use the same sized buffer and integers as tor uses */ +#define DBUF_SIZE 64 + char dbuf[DBUF_SIZE]; +#define T_ "%ld" + long sec, min, hour, day; + + /* we don't care about the exact spelling of the + * second(s), minute(s), hour(s), day(s) labels */ +#define LABEL_SIZE 21 +#define L_ "%20s" + char label_s[LABEL_SIZE]; + char label_m[LABEL_SIZE]; + char label_h[LABEL_SIZE]; + char label_d[LABEL_SIZE]; + +#define TL_ T_ " " L_ + + int r; + + (void)arg; + + /* In these tests, we're not picky about + * spelling or abbreviations */ + + /* seconds: 0, 1, 9, 10, 59 */ + + /* ignore exact spelling of "second(s)"*/ + format_time_interval(dbuf, sizeof(dbuf), 0); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &sec, label_s); + tt_int_op(r,==, 2); + tt_ci_char_op(label_s[0],==, 's'); + tt_int_op(sec,==, 0); + + format_time_interval(dbuf, sizeof(dbuf), 1); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &sec, label_s); + tt_int_op(r,==, 2); + tt_ci_char_op(label_s[0],==, 's'); + tt_int_op(sec,==, 1); + + format_time_interval(dbuf, sizeof(dbuf), 10); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &sec, label_s); + tt_int_op(r,==, 2); + tt_ci_char_op(label_s[0],==, 's'); + tt_int_op(sec,==, 10); + + format_time_interval(dbuf, sizeof(dbuf), 59); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &sec, label_s); + tt_int_op(r,==, 2); + tt_ci_char_op(label_s[0],==, 's'); + tt_int_op(sec,==, 59); + + /* negative seconds are reported as their absolute value */ + + format_time_interval(dbuf, sizeof(dbuf), -4); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &sec, label_s); + tt_int_op(r,==, 2); + tt_ci_char_op(label_s[0],==, 's'); + tt_int_op(sec,==, 4); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + + format_time_interval(dbuf, sizeof(dbuf), -32); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &sec, label_s); + tt_int_op(r,==, 2); + tt_ci_char_op(label_s[0],==, 's'); + tt_int_op(sec,==, 32); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + + /* minutes: 1:00, 1:01, 1:59, 2:00, 2:01, 59:59 */ + + /* ignore trailing "0 second(s)", if present */ + format_time_interval(dbuf, sizeof(dbuf), 60); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &min, label_m); + tt_int_op(r,==, 2); + tt_ci_char_op(label_m[0],==, 'm'); + tt_int_op(min,==, 1); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + + /* ignore exact spelling of "minute(s)," and "second(s)" */ + format_time_interval(dbuf, sizeof(dbuf), 60 + 1); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &min, label_m, &sec, label_s); + tt_int_op(r,==, 4); + tt_int_op(min,==, 1); + tt_ci_char_op(label_m[0],==, 'm'); + tt_int_op(sec,==, 1); + tt_ci_char_op(label_s[0],==, 's'); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + + format_time_interval(dbuf, sizeof(dbuf), 60*2 - 1); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &min, label_m, &sec, label_s); + tt_int_op(r,==, 4); + tt_int_op(min,==, 1); + tt_ci_char_op(label_m[0],==, 'm'); + tt_int_op(sec,==, 59); + tt_ci_char_op(label_s[0],==, 's'); + + /* ignore trailing "0 second(s)", if present */ + format_time_interval(dbuf, sizeof(dbuf), 60*2); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &min, label_m); + tt_int_op(r,==, 2); + tt_int_op(min,==, 2); + tt_ci_char_op(label_m[0],==, 'm'); + + /* ignore exact spelling of "minute(s)," and "second(s)" */ + format_time_interval(dbuf, sizeof(dbuf), 60*2 + 1); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &min, label_m, &sec, label_s); + tt_int_op(r,==, 4); + tt_int_op(min,==, 2); + tt_ci_char_op(label_m[0],==, 'm'); + tt_int_op(sec,==, 1); + tt_ci_char_op(label_s[0],==, 's'); + + format_time_interval(dbuf, sizeof(dbuf), 60*60 - 1); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &min, label_m, &sec, label_s); + tt_int_op(r,==, 4); + tt_int_op(min,==, 59); + tt_ci_char_op(label_m[0],==, 'm'); + tt_int_op(sec,==, 59); + tt_ci_char_op(label_s[0],==, 's'); + + /* negative minutes are reported as their absolute value */ + + /* ignore trailing "0 second(s)", if present */ + format_time_interval(dbuf, sizeof(dbuf), -3*60); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &min, label_m); + tt_int_op(r,==, 2); + tt_int_op(min,==, 3); + tt_ci_char_op(label_m[0],==, 'm'); + + /* ignore exact spelling of "minute(s)," and "second(s)" */ + format_time_interval(dbuf, sizeof(dbuf), -96); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &min, label_m, &sec, label_s); + tt_int_op(r,==, 4); + tt_int_op(min,==, 1); + tt_ci_char_op(label_m[0],==, 'm'); + tt_int_op(sec,==, 36); + tt_ci_char_op(label_s[0],==, 's'); + + format_time_interval(dbuf, sizeof(dbuf), -2815); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &min, label_m, &sec, label_s); + tt_int_op(r,==, 4); + tt_int_op(min,==, 46); + tt_ci_char_op(label_m[0],==, 'm'); + tt_int_op(sec,==, 55); + tt_ci_char_op(label_s[0],==, 's'); + + /* hours: 1:00, 1:00:01, 1:01, 23:59, 23:59:59 */ + /* always ignore trailing seconds, if present */ + + /* ignore trailing "0 minute(s)" etc., if present */ + format_time_interval(dbuf, sizeof(dbuf), 60*60); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &hour, label_h); + tt_int_op(r,==, 2); + tt_int_op(hour,==, 1); + tt_ci_char_op(label_h[0],==, 'h'); + + format_time_interval(dbuf, sizeof(dbuf), 60*60 + 1); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &hour, label_h); + tt_int_op(r,==, 2); + tt_int_op(hour,==, 1); + tt_ci_char_op(label_h[0],==, 'h'); + + /* ignore exact spelling of "hour(s)," etc. */ + format_time_interval(dbuf, sizeof(dbuf), 60*60 + 60); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &hour, label_h, &min, label_m); + tt_int_op(r,==, 4); + tt_int_op(hour,==, 1); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 1); + tt_ci_char_op(label_m[0],==, 'm'); + + format_time_interval(dbuf, sizeof(dbuf), 24*60*60 - 60); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &hour, label_h, &min, label_m); + tt_int_op(r,==, 4); + tt_int_op(hour,==, 23); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 59); + tt_ci_char_op(label_m[0],==, 'm'); + + format_time_interval(dbuf, sizeof(dbuf), 24*60*60 - 1); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &hour, label_h, &min, label_m); + tt_int_op(r,==, 4); + tt_int_op(hour,==, 23); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 59); + tt_ci_char_op(label_m[0],==, 'm'); + + /* negative hours are reported as their absolute value */ + + /* ignore exact spelling of "hour(s)," etc., if present */ + format_time_interval(dbuf, sizeof(dbuf), -2*60*60); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &hour, label_h); + tt_int_op(r,==, 2); + tt_int_op(hour,==, 2); + tt_ci_char_op(label_h[0],==, 'h'); + + format_time_interval(dbuf, sizeof(dbuf), -75804); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &hour, label_h, &min, label_m); + tt_int_op(r,==, 4); + tt_int_op(hour,==, 21); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 3); + tt_ci_char_op(label_m[0],==, 'm'); + + /* days: 1:00, 1:00:00:01, 1:00:01, 1:01 */ + /* always ignore trailing seconds, if present */ + + /* ignore trailing "0 hours(s)" etc., if present */ + format_time_interval(dbuf, sizeof(dbuf), 24*60*60); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &day, label_d); + tt_int_op(r,==, 2); + tt_int_op(day,==, 1); + tt_ci_char_op(label_d[0],==, 'd'); + + format_time_interval(dbuf, sizeof(dbuf), 24*60*60 + 1); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_, &day, label_d); + tt_int_op(r,==, 2); + tt_int_op(day,==, 1); + tt_ci_char_op(label_d[0],==, 'd'); + + /* ignore exact spelling of "days(s)," etc. */ + format_time_interval(dbuf, sizeof(dbuf), 24*60*60 + 60); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_ " " TL_, + &day, label_d, &hour, label_h, &min, label_m); + if (r == -1) { + /* ignore 0 hours(s), if present */ + r = tor_sscanf(dbuf, TL_ " " TL_, + &day, label_d, &min, label_m); + } + tt_assert(r == 4 || r == 6); + tt_int_op(day,==, 1); + tt_ci_char_op(label_d[0],==, 'd'); + if (r == 6) { + tt_int_op(hour,==, 0); + tt_ci_char_op(label_h[0],==, 'h'); + } + tt_int_op(min,==, 1); + tt_ci_char_op(label_m[0],==, 'm'); + + /* ignore trailing "0 minutes(s)" etc., if present */ + format_time_interval(dbuf, sizeof(dbuf), 24*60*60 + 60*60); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_, + &day, label_d, &hour, label_h); + tt_int_op(r,==, 4); + tt_int_op(day,==, 1); + tt_ci_char_op(label_d[0],==, 'd'); + tt_int_op(hour,==, 1); + tt_ci_char_op(label_h[0],==, 'h'); + + /* negative days are reported as their absolute value */ + + format_time_interval(dbuf, sizeof(dbuf), -21936184); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_ " " TL_, + &day, label_d, &hour, label_h, &min, label_m); + tt_int_op(r,==, 6); + tt_int_op(day,==, 253); + tt_ci_char_op(label_d[0],==, 'd'); + tt_int_op(hour,==, 21); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 23); + tt_ci_char_op(label_m[0],==, 'm'); + + /* periods > 1 year are reported in days (warn?) */ + + /* ignore exact spelling of "days(s)," etc., if present */ + format_time_interval(dbuf, sizeof(dbuf), 758635154); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_ " " TL_, + &day, label_d, &hour, label_h, &min, label_m); + tt_int_op(r,==, 6); + tt_int_op(day,==, 8780); + tt_ci_char_op(label_d[0],==, 'd'); + tt_int_op(hour,==, 11); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 59); + tt_ci_char_op(label_m[0],==, 'm'); + + /* negative periods > 1 year are reported in days (warn?) */ + + format_time_interval(dbuf, sizeof(dbuf), -1427014922); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_ " " TL_, + &day, label_d, &hour, label_h, &min, label_m); + tt_int_op(r,==, 6); + tt_int_op(day,==, 16516); + tt_ci_char_op(label_d[0],==, 'd'); + tt_int_op(hour,==, 9); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 2); + tt_ci_char_op(label_m[0],==, 'm'); + +#if SIZEOF_LONG == 4 || SIZEOF_LONG == 8 + + /* We can try INT32_MIN/MAX */ + /* Always ignore second(s) */ + + /* INT32_MAX */ + format_time_interval(dbuf, sizeof(dbuf), 2147483647); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_ " " TL_, + &day, label_d, &hour, label_h, &min, label_m); + tt_int_op(r,==, 6); + tt_int_op(day,==, 24855); + tt_ci_char_op(label_d[0],==, 'd'); + tt_int_op(hour,==, 3); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 14); + tt_ci_char_op(label_m[0],==, 'm'); + /* and 7 seconds - ignored */ + + /* INT32_MIN: check that we get the absolute value of interval, + * which doesn't actually fit in int32_t. + * We expect INT32_MAX or INT32_MAX + 1 with 64 bit longs */ + format_time_interval(dbuf, sizeof(dbuf), -2147483647L - 1L); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_ " " TL_, + &day, label_d, &hour, label_h, &min, label_m); + tt_int_op(r,==, 6); + tt_int_op(day,==, 24855); + tt_ci_char_op(label_d[0],==, 'd'); + tt_int_op(hour,==, 3); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 14); + tt_ci_char_op(label_m[0],==, 'm'); + /* and 7 or 8 seconds - ignored */ + +#endif + +#if SIZEOF_LONG == 8 + + /* We can try INT64_MIN/MAX */ + /* Always ignore second(s) */ + + /* INT64_MAX */ + format_time_interval(dbuf, sizeof(dbuf), 9223372036854775807L); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_ " " TL_, + &day, label_d, &hour, label_h, &min, label_m); + tt_int_op(r,==, 6); + tt_int_op(day,==, 106751991167300L); + tt_ci_char_op(label_d[0],==, 'd'); + tt_int_op(hour,==, 15); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 30); + tt_ci_char_op(label_m[0],==, 'm'); + /* and 7 seconds - ignored */ + + /* INT64_MIN: check that we get the absolute value of interval, + * which doesn't actually fit in int64_t. + * We expect INT64_MAX */ + format_time_interval(dbuf, sizeof(dbuf), + -9223372036854775807L - 1L); + tt_int_op(strnlen(dbuf, DBUF_SIZE),<=, DBUF_SIZE - 1); + r = tor_sscanf(dbuf, TL_ " " TL_ " " TL_, + &day, label_d, &hour, label_h, &min, label_m); + tt_int_op(r,==, 6); + tt_int_op(day,==, 106751991167300L); + tt_ci_char_op(label_d[0],==, 'd'); + tt_int_op(hour,==, 15); + tt_ci_char_op(label_h[0],==, 'h'); + tt_int_op(min,==, 30); + tt_ci_char_op(label_m[0],==, 'm'); + /* and 7 or 8 seconds - ignored */ + +#endif + + +done: + ; +} + +#undef tt_char_op +#undef tt_ci_char_op +#undef DBUF_SIZE +#undef T_ +#undef LABEL_SIZE +#undef L_ +#undef TL_ + static void test_util_path_is_relative(void *arg) { @@ -4146,6 +4568,7 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(mmap), UTIL_LEGACY(threads), UTIL_LEGACY(sscanf), + UTIL_LEGACY(format_time_interval), UTIL_LEGACY(path_is_relative), UTIL_LEGACY(strtok), UTIL_LEGACY(di_ops),