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

            Bug ID: 34778
           Summary: objdump -P function_starts: Mach-O function-starts
                    ULEB128 decoded with an int shift, and the partial
                    value of a truncated stream printed as a start
           Product: binutils
           Version: 2.47
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: binutils
          Assignee: unassigned at sourceware dot org
          Reporter: i at mi11ion dot io
  Target Milestone: ---
              Host: aarch64-apple-darwin

Created attachment 17102
  --> https://sourceware.org/bugzilla/attachment.cgi?id=17102&action=edit
fs-2g, fs-wide, fs-trunc: arm64 Mach-O files with crafted LC_FUNCTION_STARTS
data

Environment: GNU objdump (GNU Binutils) 2.47.20260726 and 2.46.0.20260210,
Homebrew binutils on macOS 27.0 (arm64). The code is unchanged on master at
21cffe7c0955c6bdb0c14ded96939d409f3c21f0.

Reproducer: the attached function-starts.tar.gz holds fs-2g, fs-wide and
fs-trunc (about 4 KiB each), arm64 MH_EXECUTE files with a __TEXT segment at
vmaddr 0x100000000 and an LC_FUNCTION_STARTS at file offset 0x1000 holding:

fs-2g     80 40 80 80 80 80 08 00                     deltas 0x2000,
0x80000000, terminator
fs-wide   80 40 80 80 80 80 80 80 80 80 80 80 01 00   deltas 0x2000, 2^70,
terminator
fs-trunc  80 40 04 84                                 deltas 0x2000, 4, then
0x84 at the end of the region

Command: objdump -P function_starts fs-2g fs-wide fs-trunc

Actual:

fs-2g:
    0000000100002000
    0000000080002000
fs-wide:
    0000000100002000
    0000000100002040
fs-trunc:
    0000000100002000
    0000000100002004
   [truncated]
    0000000100002008

Expected: fs-2g 0x100002000 and 0x180002000 (0x80000000 is a valid ULEB128
delta). fs-wide 0x100002000 and a note that the next value does not fit 64
bits. fs-trunc 0x100002000, 0x100002004 and [truncated], with no start from the
incomplete value. dyld_info and llvm-objdump print these starts.

Analysis: dump_function_starts in binutils/od-macho.c (line 1347) accumulates
delta |= (b & 0x7f) << shift;. b & 0x7f has type int, so the shift is done in
32 bits: a group of 8 or more at shift 28 overflows int and is sign-extended
when OR'd into the 64-bit delta, and a shift of 32 or more is undefined (C11
6.5.7p3); on arm64 the count is taken modulo 32. UBSan reports "left shift of 8
by 28 places cannot be represented in type 'int'" on fs-2g. When the region
ends inside a value, the inner loop prints [truncated] and breaks, and line
1358 still adds the partial value to addr and prints it. Line 1341, if (*p == 0
|| p == end_buf), also reads *p before checking p == end_buf, one byte past the
buffer when the stream has no terminator.

Decoding with the read_leb128 helper the same file already uses for the dyld
info opcodes, and stopping on its truncation and overflow status, would avoid
all three.

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

Reply via email to