An issue was discovered in the Binary File Descriptor (BFD) library (aka libbfd), as distributed in GNU Binutils through 2.31. There is a heap-based buffer overflow in bfd_elf32_swap_phdr_in in elfcode.h because the number of program headers is not restricted. Upstream issue: https://sourceware.org/bugzilla/show_bug.cgi?id=23942 Upstream patch: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=5f60af5d24d181371d67534fa273dd221df20c07
This does not appear to impact 64bit nm. ``` (gdb) print sizeof(*i_phdr) $5 = 64 (gdb) print i_ehdrp->e_phnum $6 = 536870912 ``` On 32-bit, 536870912 * 64 = 0. Presumably bfd_alloc returns success on an allocation of 0, and a later write triggers the heap out-of-bounds write. On 64-bit, the calculation does not overflow with this poc. I originally didn't understand where e_phnum is coming from though. If we run readelf on the binary: ``` Number of program headers: 65535 (536870912) ``` I'd expect 65535, instead we have this: ``` (gdb) print i_ehdrp->e_phnum $9 = 536870912 ``` which matches readelfs argument after the number of program headers. However: ``` /* * Extended Numbering * * If the real number of program header table entries is larger than * or equal to PN_XNUM(0xffff), it is set to sh_info field of the * section header at index 0, and PN_XNUM is set to e_phnum * field. Otherwise, the section header at index 0 is zero * initialized, if it exists. * * Specifications are available in: * * - Oracle: Linker and Libraries. * Part No: 817–1984–19, August 2011. * http://docs.oracle.com/cd/E18752_01/pdf/817-1984.pdf * * - System V ABI AMD64 Architecture Processor Supplement * Draft Version 0.99.4, * January 13, 2010. * http://www.cs.washington.edu/education/courses/cse351/12wi/supp-docs/abi.pdf */ ``` readelf source shows that this is a special case: ``` if (section_headers != NULL && elf_header.e_phnum == PN_XNUM && section_headers[0].sh_info != 0) printf (" (%ld)", (long) section_headers[0].sh_info); ```
and it looks like sh_info is 32bits, hence, we can't set it to a value that would overflow on 64bit platforms.
Also, here's a run of the 32-bit crash. ``` nm poc Segmentation fault (core dumped) ==28303== Memcheck, a memory error detector ==28303== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==28303== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==28303== Command: nm poc ==28303== ==28303== Invalid write of size 4 ==28303== at 0xC39A52: bfd_elf32_swap_phdr_in (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0xC3C98E: bfd_elf32_object_p (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0xC18549: bfd_check_format_matches (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0x804B6DE: ??? (in /usr/bin/nm) ==28303== by 0x804BCA7: ??? (in /usr/bin/nm) ==28303== by 0x9FED35: (below main) (in /lib/libc-2.12.so) ==28303== Address 0x40201b8 is 0 bytes after a block of size 4,064 alloc'd ==28303== at 0x40072B2: malloc (vg_replace_malloc.c:270) ==28303== by 0xCA0D45: objalloc_create (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0xC1AD67: _bfd_new_bfd (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0xC1B07C: bfd_fopen (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0xC1B266: bfd_openr (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0x804B601: ??? (in /usr/bin/nm) ==28303== by 0x804BCA7: ??? (in /usr/bin/nm) ==28303== by 0x9FED35: (below main) (in /lib/libc-2.12.so) ... ==28303== Process terminating with default action of signal 11 (SIGSEGV) ==28303== Access not within mapped region at address 0x9066 ==28303== at 0xA63311: __GI_memcpy (in /lib/libc-2.12.so) ==28303== by 0xA561F7: _IO_sgetn (in /lib/libc-2.12.so) ==28303== by 0xA496AD: fread (in /lib/libc-2.12.so) ==28303== by 0xC143D0: ??? (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0xC1392A: bfd_bread (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0xC3C9C8: bfd_elf32_object_p (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0xC18549: bfd_check_format_matches (in /usr/lib/libbfd-2.20.51.0.2-5.44.el6.so) ==28303== by 0x804B6DE: ??? (in /usr/bin/nm) ==28303== by 0x804BCA7: ??? (in /usr/bin/nm) ==28303== by 0x9FED35: (below main) (in /lib/libc-2.12.so) ==28303== If you believe this happened as a result of a stack ==28303== overflow in your program's main thread (unlikely but ==28303== possible), you can try to increase the size of the ==28303== main thread stack using the --main-stacksize= flag. ==28303== The main thread stack size used in this run was 10485760. ``` 64-bit run: ``` $ nm poc nm: poc: memory exhausted ```
Is it permissible to close this BZ ? I would consider the problem itself to be very low priority. It only affects specially crafted, corrupt 32-bit ELF binaries, and the only denial of service is that tools like nm and objdump will fail, and then only if these tools themselves are 32-bit executables. The bug has been fixed upstream and will be in the next official release of the GNU binutils. This in turn will make its way into Fedora rawhide, and eventually some future RHEL release. Backporting the patch to other versions of Fedora and RHEL is possible, but it does not seem like a profitable use of time.