libdw: A crafted .debug_macro section with opcode 0 makes (opcode - 1)
wrap to 0xFFFFFFFF in the unsigned array index, writing a 16-byte struct
past the 255-element op_protos array. Reject opcode == 0 and add the
missing readp >= endp checks before reading the count and opcode bytes.

libdwfl: When rebuilding an ELF from process memory, segments_end used
the last PT_LOAD segment's end rather than the largest, so a later
smaller segment could shrink contents_size below an earlier segment's
start, underflowing end - start into a huge length and overflowing the
heap buffer. Skip any segment that starts past contents_size, matching
the guard already used in dwfl_segment_report_module.

        * libdw/dwarf_getmacros.c (get_macinfo_table): Reject opcode 0,
        add bounds checks.
        * libdwfl/elf-from-memory.c (elf_from_remote_memory): Skip
        out-of-range segment.
        * tests/testfile-macros-opcode0.bz2: New test input.
        * tests/run-dwarf-getmacros.sh: Test it.
        * tests/Makefile.am (EXTRA_DIST): Add new test file.

Signed-off-by: Sayed Kaif <[email protected]>
---
 libdw/dwarf_getmacros.c           |  12 ++++++++++++
 libdwfl/elf-from-memory.c         |   8 ++++++++
 tests/Makefile.am                 |   2 +-
 tests/run-dwarf-getmacros.sh      |   8 ++++++++
 tests/testfile-macros-opcode0.bz2 | Bin 0 -> 166 bytes
 5 files changed, 29 insertions(+), 1 deletion(-)
 create mode 100644 tests/testfile-macros-opcode0.bz2

diff --git a/libdw/dwarf_getmacros.c b/libdw/dwarf_getmacros.c
index d7ed7b58..bb2272ea 100644
--- a/libdw/dwarf_getmacros.c
+++ b/libdw/dwarf_getmacros.c
@@ -255,11 +255,23 @@ get_table_for_offset (Dwarf *dbg, Dwarf_Word macoff,
 
   if ((flags & 0x4) != 0)
     {
+      if (readp >= endp)
+       goto invalid_dwarf;
       unsigned count = *readp++;
       for (unsigned i = 0; i < count; ++i)
        {
+         if (readp >= endp)
+           goto invalid;
          unsigned opcode = *readp++;
 
+         /* Opcode 0 is not allocated (and 0xff means "not stored").
+            Reject it here: without this check the unsigned expression
+            opcode - 1 wraps to UINT_MAX for opcode == 0, and the
+            assignment below would write a Dwarf_Macro_Op_Proto far out
+            of the bounds of the op_protos[255] stack array.  */
+         if (opcode == 0)
+           goto invalid;
+
          Dwarf_Macro_Op_Proto e;
          if (readp >= endp)
            goto invalid;
diff --git a/libdwfl/elf-from-memory.c b/libdwfl/elf-from-memory.c
index e9b330fd..14b2f5c8 100644
--- a/libdwfl/elf-from-memory.c
+++ b/libdwfl/elf-from-memory.c
@@ -304,6 +304,14 @@ elf_from_remote_memory (GElf_Addr ehdr_vma,
 
       GElf_Off start = offset & -pagesize;
       GElf_Off end = (offset + filesz + pagesize - 1) & -pagesize;
+      /* The final contents_size is the trimmed last segment's end, which
+        may be smaller than an earlier segment's start (segments_end above
+        tracks the last PT_LOAD, not the maximum).  Skip any segment that
+        falls entirely past it: otherwise buffer + start would be out of
+        bounds and end - start would underflow into a huge read length.
+        Mirrors the file_trimmed_end check in dwfl_segment_report_module.  */
+      if (start >= (GElf_Off) contents_size)
+        continue;
       if (end > (GElf_Off) contents_size)
         end = contents_size;
       nread = (*read_memory) (arg, buffer + start,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8ae7d126..2881989d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -411,7 +411,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
             testfile45.S.bz2 testfile45.expect.bz2 run-disasm-x86-64.sh \
             testfile46.bz2 testfile47.bz2 testfile48.bz2 testfile48.debug.bz2 \
             testfile49.bz2 testfile50.bz2 testfile51.bz2 \
-            testfile-macros-0xff.bz2 \
+            testfile-macros-0xff.bz2 testfile-macros-opcode0.bz2 \
             run-readelf-macro.sh testfilemacro.bz2 testfileclangmacro.bz2 \
             run-readelf-loc.sh testfileloc.bz2 \
             splitdwarf4-not-split4.dwo.bz2 \
diff --git a/tests/run-dwarf-getmacros.sh b/tests/run-dwarf-getmacros.sh
index 220c2426..bda6690d 100755
--- a/tests/run-dwarf-getmacros.sh
+++ b/tests/run-dwarf-getmacros.sh
@@ -707,6 +707,14 @@ file /home/petr/proj/elfutils/master/elfutils/x.c
 /file
 EOF
 
+# A .debug_macro opcode_operands_table that defines opcode 0 used to make
+# get_table_for_offset() compute op_protos[(unsigned)0 - 1] and write far
+# out of bounds.  It must now be rejected as invalid DWARF.
+testfiles testfile-macros-opcode0
+testrun_compare ${abs_builddir}/dwarf-getmacros testfile-macros-opcode0 0xb 
<<\EOF
+invalid DWARF
+EOF
+
 # See testfile-dwp.source.
 testfiles testfile-dwp-5 testfile-dwp-5.dwp
 testfiles testfile-dwp-4-strict testfile-dwp-4-strict.dwp
diff --git a/tests/testfile-macros-opcode0.bz2 
b/tests/testfile-macros-opcode0.bz2
new file mode 100644
index 
0000000000000000000000000000000000000000..5e4cb8684065c20b00b0cbde317cf733c8e7a0e9
GIT binary patch
literal 166
zcmZ>Y%CIzaj8qGbRLPmZ%D_<ie?|Sf2OSKK0?ffI4hs7p&u3V`z|f$;FiBuR1_KD(
zY8IL`tE$<ds^@~T0|QgBe`;^e{*tG&%sXesEOc`gVcW4$Erf$<3&&MnAtp(|yESS1
zV|4@gn65uudF+WtQ?()Ei?+$xjRq_ixOEN1A~kF{KYmWwJ<+}JN!^c@8BCTlt$QYQ
S&xl#X`Cq)q)goX5$Xx(&Fh3Ol

literal 0
HcmV?d00001

-- 
2.52.0.windows.1

Reply via email to