As reported on the binutils bugzilla .
No response yet, but should be easy to fix.
Details follow (same as in bug report but easier to read).
tc-i386-intel.c contains undefined behavior in
recent binutils versions (including latest at time of writing).
The error occurs in multiple places
(for example line 432 , reproduced below) and is incorrect
as it assumes unsigned integer wrapping semantics for
pointer arithmetic on the variable
scale in a number of
places. In particular, the check:
432 if (ret && scale && (scale + 1))
Gets optimized to
if (ret && scale) because it is impossible for
scale + 1 to evaluate to
NULL without invoking undefined behavior. Note that the earlier decrement from
NULL is also invalid, and possibly other constructs in related code.
This is is a problem as it results in the conditional being taken when scale is
(int*)-sizeof(int), which leads to an invalid pointer being dereferenced in
Steps to reproduce
- Obtain and unpack binutils 2.22 or latest via git (tested with 0b0b7b5).
- Obtain clang 3.3 or latest trunk (from your package manager or build) and modify PATH as appropriate.
- Configure similar to the following:
$ CC=clang CXX=clang++ ./configure --disable-werror --enable-ld=no
$ make -j
- Run the just-built ‘as’ using the following program from the testsuite:
$ valgrind gas/as-new --32 gas/testsuite/gas/i386/intelbad.s
- Observe segfault, see referenced valgrind.log for the output of the above command.
Presently prevents building a functional binutils with recent versions of clang, and is a time-bomb for breaking future builds. Compilers (including gcc and clang) are known to increasingly take advantage of undefined behavior in newer versions and so this may be an issue in the future even with compilers/platforms that safely build this today.
Valgrind log from step #5 above:
==80032== Memcheck, a memory error detector ==80032== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. ==80032== Using Valgrind-3.8.0.SVN and LibVEX; rerun with -h for copyright info ==80032== Command: gas/as-new --32 gas/testsuite/gas/i386/intelbad.s ==80032== ... ==80032== Invalid read of size 8 ==80032== at 0x40D881: resolve_expression (expr.c:2026) ==80032== by 0x42EA9F: i386_intel_simplify (tc-i386-intel.c:415) ==80032== by 0x42E8A2: i386_intel_simplify (tc-i386-intel.c:297) ==80032== by 0x42E5F7: i386_intel_simplify (tc-i386-intel.c:297) ==80032== by 0x427835: md_assemble (tc-i386-intel.c:537) ==80032== by 0x415E1E: read_a_source_file (read.c:950) ==80032== by 0x404606: main (as.c:1089) ==80032== Address 0xffffffffffffffe0 is not stack'd, malloc'd or (recently) free'd ==80032== ==80032== ==80032== Process terminating with default action of signal 11 (SIGSEGV) ==80032== Access not within mapped region at address 0xFFFFFFFFFFFFFFE0 ==80032== at 0x40D881: resolve_expression (expr.c:2026) ==80032== by 0x42EA9F: i386_intel_simplify (tc-i386-intel.c:415) ==80032== by 0x42E8A2: i386_intel_simplify (tc-i386-intel.c:297) ==80032== by 0x42E5F7: i386_intel_simplify (tc-i386-intel.c:297) ==80032== by 0x427835: md_assemble (tc-i386-intel.c:537) ==80032== by 0x415E1E: read_a_source_file (read.c:950) ==80032== by 0x404606: main (as.c:1089) ==80032== If you believe this happened as a result of a stack ==80032== overflow in your program's main thread (unlikely but ==80032== possible), you can try to increase the size of the ==80032== main thread stack using the --main-stacksize= flag. ==80032== The main thread stack size used in this run was 10485760. ==80032== ==80032== HEAP SUMMARY: ==80032== in use at exit: 3,931,677 bytes in 1,740 blocks ==80032== total heap usage: 1,894 allocs, 154 frees, 3,963,363 bytes allocated ==80032== ==80032== LEAK SUMMARY: ==80032== definitely lost: 0 bytes in 0 blocks ==80032== indirectly lost: 0 bytes in 0 blocks ==80032== possibly lost: 0 bytes in 0 blocks ==80032== still reachable: 3,931,677 bytes in 1,740 blocks ==80032== suppressed: 0 bytes in 0 blocks ==80032== Rerun with --leak-check=full to see details of leaked memory ==80032== ==80032== For counts of detected and suppressed errors, rerun with: -v ==80032== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)