diff options
Diffstat (limited to 'src/common/util.c')
-rw-r--r-- | src/common/util.c | 130 |
1 files changed, 87 insertions, 43 deletions
diff --git a/src/common/util.c b/src/common/util.c index c6a39898d9..1359776b21 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2013, The Tor Project, Inc. */ + * Copyright (c) 2007-2014, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -195,33 +195,40 @@ tor_malloc_zero_(size_t size DMALLOC_PARAMS) return result; } +/* The square root of SIZE_MAX + 1. If a is less than this, and b is less + * than this, then a*b is less than SIZE_MAX. (For example, if size_t is + * 32 bits, then SIZE_MAX is 0xffffffff and this value is 0x10000. If a and + * b are less than this, then their product is at most (65535*65535) == + * 0xfffe0001. */ +#define SQRT_SIZE_MAX_P1 (((size_t)1) << (sizeof(size_t)*4)) + +/** Return non-zero if and only if the product of the arguments is exact. */ +static INLINE int +size_mul_check(const size_t x, const size_t y) +{ + /* This first check is equivalent to + (x < SQRT_SIZE_MAX_P1 && y < SQRT_SIZE_MAX_P1) + + Rationale: if either one of x or y is >= SQRT_SIZE_MAX_P1, then it + will have some bit set in its most significant half. + */ + return ((x|y) < SQRT_SIZE_MAX_P1 || + y == 0 || + x <= SIZE_MAX / y); +} + /** Allocate a chunk of <b>nmemb</b>*<b>size</b> bytes of memory, fill * the memory with zero bytes, and return a pointer to the result. * Log and terminate the process on error. (Same as * calloc(<b>nmemb</b>,<b>size</b>), but never returns NULL.) - * - * XXXX This implementation probably asserts in cases where it could - * work, because it only tries dividing SIZE_MAX by size (according to - * the calloc(3) man page, the size of an element of the nmemb-element - * array to be allocated), not by nmemb (which could in theory be - * smaller than size). Don't do that then. + * The second argument (<b>size</b>) should preferably be non-zero + * and a compile-time constant. */ void * tor_calloc_(size_t nmemb, size_t size DMALLOC_PARAMS) { - /* You may ask yourself, "wouldn't it be smart to use calloc instead of - * malloc+memset? Perhaps libc's calloc knows some nifty optimization trick - * we don't!" Indeed it does, but its optimizations are only a big win when - * we're allocating something very big (it knows if it just got the memory - * from the OS in a pre-zeroed state). We don't want to use tor_malloc_zero - * for big stuff, so we don't bother with calloc. */ - void *result; - size_t max_nmemb = (size == 0) ? SIZE_MAX : SIZE_MAX/size; - - tor_assert(nmemb < max_nmemb); - - result = tor_malloc_zero_((nmemb * size) DMALLOC_FN_ARGS); - return result; + tor_assert(size_mul_check(nmemb, size)); + return tor_malloc_zero_((nmemb * size) DMALLOC_FN_ARGS); } /** Change the size of the memory block pointed to by <b>ptr</b> to <b>size</b> @@ -264,7 +271,7 @@ tor_reallocarray_(void *ptr, size_t sz1, size_t sz2 DMALLOC_PARAMS) { /* XXXX we can make this return 0, but we would need to check all the * reallocarray users. */ - tor_assert(sz2 == 0 || sz1 < SIZE_T_CEILING / sz2); + tor_assert(size_mul_check(sz1, sz2)); return tor_realloc(ptr, (sz1 * sz2) DMALLOC_FN_ARGS); } @@ -1438,7 +1445,8 @@ n_leapdays(int y1, int y2) --y2; return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400); } -/** Number of days per month in non-leap year; used by tor_timegm. */ +/** Number of days per month in non-leap year; used by tor_timegm and + * parse_rfc1123_time. */ static const int days_per_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; @@ -1452,10 +1460,32 @@ tor_timegm(const struct tm *tm, time_t *time_out) * It's way more brute-force than fiddling with tzset(). */ time_t year, days, hours, minutes, seconds; - int i; - year = tm->tm_year + 1900; - if (year < 1970 || tm->tm_mon < 0 || tm->tm_mon > 11 || - tm->tm_year >= INT32_MAX-1900) { + int i, invalid_year, dpm; + /* avoid int overflow on addition */ + if (tm->tm_year < INT32_MAX-1900) { + year = tm->tm_year + 1900; + } else { + /* clamp year */ + year = INT32_MAX; + } + invalid_year = (year < 1970 || tm->tm_year >= INT32_MAX-1900); + + if (tm->tm_mon >= 0 && tm->tm_mon <= 11) { + dpm = days_per_month[tm->tm_mon]; + if (tm->tm_mon == 1 && !invalid_year && IS_LEAPYEAR(tm->tm_year)) { + dpm = 29; + } + } else { + /* invalid month - default to 0 days per month */ + dpm = 0; + } + + if (invalid_year || + tm->tm_mon < 0 || tm->tm_mon > 11 || + tm->tm_mday < 1 || tm->tm_mday > dpm || + tm->tm_hour < 0 || tm->tm_hour > 23 || + tm->tm_min < 0 || tm->tm_min > 59 || + tm->tm_sec < 0 || tm->tm_sec > 60) { log_warn(LD_BUG, "Out-of-range argument to tor_timegm"); return -1; } @@ -1519,8 +1549,9 @@ parse_rfc1123_time(const char *buf, time_t *t) struct tm tm; char month[4]; char weekday[4]; - int i, m; + int i, m, invalid_year; unsigned tm_mday, tm_year, tm_hour, tm_min, tm_sec; + unsigned dpm; if (strlen(buf) != RFC1123_TIME_LEN) return -1; @@ -1533,18 +1564,6 @@ parse_rfc1123_time(const char *buf, time_t *t) tor_free(esc); return -1; } - if (tm_mday < 1 || tm_mday > 31 || tm_hour > 23 || tm_min > 59 || - tm_sec > 60 || tm_year >= INT32_MAX || tm_year < 1970) { - char *esc = esc_for_log(buf); - log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc); - tor_free(esc); - return -1; - } - tm.tm_mday = (int)tm_mday; - tm.tm_year = (int)tm_year; - tm.tm_hour = (int)tm_hour; - tm.tm_min = (int)tm_min; - tm.tm_sec = (int)tm_sec; m = -1; for (i = 0; i < 12; ++i) { @@ -1561,6 +1580,26 @@ parse_rfc1123_time(const char *buf, time_t *t) } tm.tm_mon = m; + invalid_year = (tm_year >= INT32_MAX || tm_year < 1970); + tor_assert(m >= 0 && m <= 11); + dpm = days_per_month[m]; + if (m == 1 && !invalid_year && IS_LEAPYEAR(tm_year)) { + dpm = 29; + } + + if (invalid_year || tm_mday < 1 || tm_mday > dpm || + tm_hour > 23 || tm_min > 59 || tm_sec > 60) { + char *esc = esc_for_log(buf); + log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc); + tor_free(esc); + return -1; + } + tm.tm_mday = (int)tm_mday; + tm.tm_year = (int)tm_year; + tm.tm_hour = (int)tm_hour; + tm.tm_min = (int)tm_min; + tm.tm_sec = (int)tm_sec; + if (tm.tm_year < 1970) { char *esc = esc_for_log(buf); log_warn(LD_GENERAL, @@ -1732,7 +1771,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) { @@ -3484,8 +3527,9 @@ format_win_cmdline_argument(const char *arg) smartlist_add(arg_chars, (void*)&backslash); /* Allocate space for argument, quotes (if needed), and terminator */ - formatted_arg = tor_calloc(sizeof(char), - (smartlist_len(arg_chars) + (need_quotes ? 2 : 0) + 1)); + const size_t formatted_arg_len = smartlist_len(arg_chars) + + (need_quotes ? 2 : 0) + 1; + formatted_arg = tor_malloc_zero(formatted_arg_len); /* Add leading quote */ i=0; @@ -5123,7 +5167,7 @@ tor_check_port_forwarding(const char *filename, for each smartlist element (one for "-p" and one for the ports), and one for the final NULL. */ args_n = 1 + 2*smartlist_len(ports_to_forward) + 1; - argv = tor_calloc(sizeof(char *), args_n); + argv = tor_calloc(args_n, sizeof(char *)); argv[argv_index++] = filename; SMARTLIST_FOREACH_BEGIN(ports_to_forward, const char *, port) { |