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

            Bug ID: 34770
           Summary: DWARF1 parse_die accepts an AT_name without a
                    terminating NUL; addr2line prints the next DIE
           Product: binutils
           Version: 2.48 (HEAD)
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: binutils
          Assignee: unassigned at sourceware dot org
          Reporter: martin.mirchev at u dot nus.edu
  Target Milestone: ---

DWARF 1.1 requires a FORM_STRING to be NUL-terminated.  parse_die stores
an AT_name pointer before stepping over the string:

        case FORM_STRING:
          if (attr == AT_name)
            aDieInfo->name = (char *) xptr;
          xptr += strnlen ((char *) xptr, aDiePtrEnd - xptr) + 1;
          break;

When the string has no NUL within the DIE, strnlen stops at the end of
the DIE (the bound added for PR 22202), xptr steps one byte past it, and
parse_die still returns true.  The unterminated name is copied into the
function (or unit) record by parse_functions_in_unit, returned by
_bfd_dwarf1_find_nearest_line, and printed by addr2line with %s.

Two objects that differ in one byte, the last byte of a subroutine
name: "BADX" with no NUL, against "BAD\0".  The next DIE is 65 bytes
long, so it starts with the bytes 'A', NUL:

$ gcc -c t.s -o t.o && gcc -c t-control.s -o t-control.o
$ addr2line -f -e t.o 0
BADXA
unit.c:7
$ addr2line -f -e t-control.o 0
BAD
unit.c:7

The trailing 'A' is read from the next DIE.  When no NUL happens to
follow, the read runs past the section buffer.  With asan-input.s (the
name "F" is the last byte of .debug), an ASan build of master gives:

==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6060000000bc
READ of size 2 at 0x6060000000bc thread T0
    #0 printf_common sanitizer_common_interceptors_format.inc:546
    #1 __interceptor_vprintf sanitizer_common_interceptors.inc:1600
    #2 __interceptor_printf sanitizer_common_interceptors.inc:1658
    #3 translate_addresses binutils/addr2line.c:372
    #4 process_file binutils/addr2line.c:482
    #5 main binutils/addr2line.c:591
0x6060000000bc is located 0 bytes to the right of 60-byte region
allocated by thread T0 here:
    #0 malloc
    #1 bfd_malloc bfd/libbfd.c:291
    #2 bfd_get_full_section_contents bfd/compress.c:763
    #3 bfd_simple_get_relocated_section_contents bfd/simple.c:221

Suggested fix: reject a string that is not terminated within its die,
as parse_die already does for other malformed input.

diff --git a/bfd/dwarf1.c b/bfd/dwarf1.c
index 2a34b88765b..86f0d44f871 100644
--- a/bfd/dwarf1.c
+++ b/bfd/dwarf1.c
@@ -290,9 +290,16 @@ parse_die (const struct dwarf1_debug *stash,
          xptr += 4;
          break;
        case FORM_STRING:
-         if (attr == AT_name)
-           aDieInfo->name = (char *) xptr;
-         xptr += strnlen ((char *) xptr, aDiePtrEnd - xptr) + 1;
+         {
+           size_t len = strnlen ((char *) xptr, aDiePtrEnd - xptr);
+
+           /* The string must be terminated within the die.  */
+           if (len == (size_t) (aDiePtrEnd - xptr))
+             return false;
+           if (attr == AT_name)
+             aDieInfo->name = (char *) xptr;
+           xptr += len + 1;
+         }
          break;
        }
     }

With it, the malformed object's DWARF1 info is rejected and addr2line
falls back to the symbol table ("witness_symbol", "??:?"); the control
objects print as before, and the ASan reproducer no longer overflows.
I could not run the DejaGnu testsuite here, and found no existing
DWARF1 tests.

Seen on master (0339bf9812d).

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