https://sourceware.org/bugzilla/show_bug.cgi?id=34317

            Bug ID: 34317
           Summary: eu-readelf: heap-buffer-overflow (OOB read) in
                    print_cu_index_section (.debug_cu_index size table)
           Product: elfutils
           Version: unspecified
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: tools
          Assignee: unassigned at sourceware dot org
          Reporter: sujaltuladhar1231 at gmail dot com
                CC: elfutils-devel at sourceware dot org
  Target Milestone: ---

eu-readelf reads out of bounds (heap-buffer-overflow READ, CWE-125) while
printing the DWARF package index "Size table" of a crafted
.debug_cu_index / .debug_tu_index section.

Affected: git master (HEAD 53336422478ef7698d6555266b5b735d9c5d0250).
Reachable via:  eu-readelf --debug-dump=cu_index FILE   (also: eu-readelf -w
FILE)
on an untrusted third-party ELF / .dwp file.

Root cause (src/readelf.c, print_cu_index_section):

The size table holds one row of section_count 4-byte slots per unit, i.e.
unit_count rows. But the bound is computed for a single row only:

    const unsigned char *lengths = (offsets +
                                    unit_count * section_count * off_bytes);
    const unsigned char *lengths_end = lengths + section_count * 4;  /* one row
*/

while the size-table loop iterates one row per used unit, up to unit_count:

    const unsigned char *prow = lengths + (row - 1) * section_count * 4; /* row
up to unit_count */
    for (size_t j = 0; j < section_count; j++)
        uint32_t off = read_4ubyte_unaligned (dbg, prow + j * 4);        /* OOB
read */

The maximum address read is lengths + unit_count * section_count * 4, but the
sanity check only validated lengths + section_count * 4 <= dataend. So for
unit_count >= 2 the loop reads (unit_count - 1) * section_count * 4 bytes past
the validated end of the section.

The offset table is not affected: its extent
(unit_count * section_count * off_bytes, which equals lengths) is already
validated against dataend. Only the size table omits the unit_count factor.

Reproduction:

A 632-byte PoC ELF (section_count=8, unit_count=2, slot_count=2, with the
.debug_cu_index placed last so its bytes end at EOF) gives a deterministic
AddressSanitizer report when the section data is backed by an exact-sized
buffer (elf_memory over malloc):

  ERROR: AddressSanitizer: heap-buffer-overflow READ of size 4
    #0 read_4ubyte_unaligned_1 libdw/memory-access.h
    #1 print_cu_index_section src/readelf.c:12434
  0 bytes after 632-byte region

(For a normal mmap'd run the small overshoot may read adjacent mapped slack
silently -- still an out-of-bounds read / potential heap info leak, or a crash
when it walks off the mapping.)

Fix (attached as a git format-patch with Signed-off-by; also inline):

  -  const unsigned char *lengths_end = lengths + section_count * 4;
  +  /* The size table has one row of section_count 4-byte slots per unit,
  +     just like the offset table, so it spans unit_count rows (not one).  */
  +  const unsigned char *lengths_end = (lengths +
  +                                      unit_count * section_count * 4);
   ...
  +      || ((unit_count != 0)
  +          && (section_count > SIZE_MAX / (4 * (size_t) unit_count)))

After the fix the crafted file is rejected via the existing goto invalid_data
path; valid .dwp files are unaffected.

Verified at HEAD (gcc 13.3, aarch64): unpatched -> ASan heap-buffer-overflow at
readelf.c:12434; patched -> clean "invalid data"; the real
eu-readelf --debug-dump=cu_index behaves identically after the patch, and
run-readelf-{d,n,zdebug} regression tests pass.

Attachments: the git format-patch, the 632-byte PoC ELF, and the full ASan log.

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Reply via email to