[patch] Arithmetic undef behaviour: sscanf, memeq, scale array, fmt exit status
I've been running a build of tor configured to detect undefined behaviours at runtime (see end for flags).
I have discovered and patched the following commonly executed undefined behaviours in arithmetic operations. I have also expanded the existing unit tests to ensure they test the cases fixed by these patches. (The unit test changes are part of each patch.)
**Behavioural Changes**
Assuming the original version was compiled using un-optimised (!), wrapping, 2's complement arithmetic; the changes in behaviour after each patch are:
* 01 no overflows, same results (fix test typo)
* 02 no overflows, same results
* 03 avoid creating, multiplying by, and rounding NaNs when n_entries or total are 0; no floating point exceptions, no unspecified results
* 04 no overflows, same results
**Code Changes**
01 tor_sscanf (util.c 2837 & 2875):
* check for overflow before potentially overflowing computation (post-overflow checks could be optimised out by a compiler: use -fwrapv with gcc/clang to avoid this)
* perform negation without overflowing (scan_signed util.c 2875)
* consolidate and expand unit tests to test formats: u, d, x; flags: (none), l; bit widths: consistently test 4/8 byte ints/longs.
* fix typo in textual UINT32_MAX + 1 in unit tests
02 tor_memeq (di_ops.c 120):
* perform subtraction without underflowing by casting to int32_t first
* update comments to explain why this works regardless of sign-extension on right-shifts of signed negative integers, as long as sizeof(any_difference) > 1
* expand unit tests to exhaustively white-box test all 1-byte bit-pattern differences (256) for tor_memeq (this ensures the cast hasn't broken anything)
* while we're at it, exhaustively test all 1-byte bit-pattern differences (2x256) for tor_memcmp
03 scale_array_elements_to_u64 (routerlist.c 1806):
* if total is 0.0, don't divide by it (this avoids creating, multiplying by, and rounding NaNs - rounding result is unspecified)
* expand unit tests to test cases when n_entries is 0, and when all entries are 0.0 (and therefore total is 0)
* while we're at it, check that we don't read any entries when n_entries is 0 (is this paranoid?)
04 format_helper_exit_status (util.c 3577 [line # after patch 01]):
* perform negation without overflowing (similar to 01 scan_signed util.c 2875)
* expand tests to test 8 byte ints.
**Testing**
I've tested these changes by running tor as a directory cache; as a client; with a controller (TBB & Vidalia); and as a router (but not an authority). However, I'm convinced there is still some undefined behaviour lurking in less commonly used code. To comprehensively test, we'd need broader test coverage, a tor fuzzer, and/or broad usage with undefined behaviour logging. (Or an excellent, constraint-checking static analyser?)
FYI - these issues were discovered using a tor built with:
clang -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -ftrapv
Version:
tor 0.2.6.?-alpha
git 781b477bc8af868661450473cdebb5d70312fd61
issue