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

            Bug ID: 34893
           Summary: ld: eval_symbol does not validate the complex-symbol
                    length ("s-1:x" -> memcpy of SIZE_MAX into a 4096-byte
                    stack buffer)
           Product: binutils
           Version: 2.48 (HEAD)
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: ld
          Assignee: unassigned at sourceware dot org
          Reporter: martin.mirchev at u dot nus.edu
  Target Milestone: ---

eval_symbol() (bfd/elflink.c) parses the length of an "s<len>:<name>" term of
an STT_RELC complex-symbol name with strtol() into the size_t symlen. It then
checks only

    9390   if (symend < sym || symlen + 1 > sizeof (symbuf))

before

    9396   memcpy (symbuf, sym, symlen);

For "s-1:x", symlen is SIZE_MAX and symlen + 1 wraps to 0, so memcpy is called
with SIZE_MAX into the 4096-byte stack buffer symbuf. symlen is also never
compared with the bytes left in the name, so "s4000:x" copies 4000 bytes
starting at a 7-byte name and reads past the string table.

Tested on master 1ec1cee48577c7b1960ddce5a6054b3f9fab6ef5 (GNU ld
2.47.50.20261027). Release: Ubuntu 22.04 gcc 11 default -g -O2 (fortify on).
ASan: -O1 -g -fsanitize=address,undefined.

Attached (all files in ld-complex-symbol-length.tar.gz): complex-symbol.s,
complex-symbol-safe.s, set-symbol-type.c, mk.sh and
the generated objects. gas cannot emit STT_RELC, so set-symbol-type.c sets the
type to 8 after assembling.
  ordinary.o  "s-1:x" as STT_OBJECT  (control)
  safe.o      "#1" as STT_RELC        (control)
  bad.o       "s-1:x" as STT_RELC
  long.o      "s4000:x" as STT_RELC

  $ ld -o bad bad.o
  *** buffer overflow detected ***: terminated        (exit 134)
  $ ld -o long long.o
  ld: undefined symbol reference in complex symbol: x
  ld: final link failed: bad value                    (exit 1)
  $ ld -o o ordinary.o; ld -o s safe.o                (both exit 0)

ASan, bad.o:
  ERROR: AddressSanitizer: unknown-crash on address 0x7fffad58a890
  WRITE of size 18446744073709551615 at 0x7fffad58a890 thread T0
    #0 memcpy bits/string_fortified.h:29
    #1 eval_symbol bfd/elflink.c:9396
    #2 elf_link_input_bfd bfd/elflink.c:11916
    #3 _bfd_elf_final_link bfd/elflink.c:13247
    #4 ldwrite ld/ldwrite.c:548
    #5 main ld/ldmain.c:1001
  Address ... is located in stack of thread T0 at offset 96 in frame
    #0 eval_symbol bfd/elflink.c:9351
    [96, 4192) 'symbuf' (line 9356) <== Memory access at offset 96 is inside
this variable

ASan, long.o:
  READ of size 4000 at 0x521000025c17 thread T0
    #0 memcpy
    #1 eval_symbol bfd/elflink.c:9396
  0x5210000268e0 is located 0 bytes to the right of 4064-byte region
  allocated by: bfd_alloc bfd/libbfd.c:453 <- bfd_section_from_shdr
bfd/elf.c:2896

Both controls link cleanly under ASan. Without _FORTIFY_SOURCE, bad.o is a
plain
stack overwrite. The parsing dates from d93525185ce6 (binutils 2.18) and the
current form of the check from 4b93929bf941 (PR 5160). PR 26469 fixed other
problems in the same function but not this length.

A likely fix is to reject a negative or non-numeric length and require symlen
to be below sizeof (symbuf) and no larger than the text remaining after the
':',
without the wrapping symlen + 1 comparison.

This issue was found by an LLM-Enhanced Static Analysis Engine and verified
manually.

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

Reply via email to