Hi,

The attached patch fixes an out-of-bounds read in eu-readelf's
print_form_data when a DW_FORM_strx attribute is printed with a
str_offsets_base larger than the .debug_str_offsets section.

The check data->d_size - str_offsets_base < val * offset_len wraps when
str_offsets_base, which comes from the file, exceeds d_size. strreadp then
points past the section, and the following (size_t) (strreadendp -
strreadp) < offset_len check wraps too, so readelf reads at a
file-controlled offset. val * offset_len can also overflow for a large
ULEB128 index. libdw's dwarf_formstring and readelf's get_indexed_addr
already check the base against the section size; the patch does the same
and compares the index with the number of entries instead of multiplying.
This covers both .debug_macro and DWARF 5 .debug_line, which share
print_form_data.

Reproducible with eu-readelf --debug-dump=macro on a crafted ELF whose
DW_AT_str_offsets_base exceeds the size of .debug_str_offsets; ASAN reports
a SEGV in print_form_data reading far past the section. The patch (git
format-patch, applies on HEAD 10b728d8) is attached. I can send the
reproducer file, or add it as a test case, if that helps.

Thanks,
Sujal Tuladhar
From ff94bb0cda82a242bf6d11b62f2adfd298066b36 Mon Sep 17 00:00:00 2001
From: Sujal Tuladhar <[email protected]>
Date: Thu, 5 Nov 2026 08:08:23 +0000
Subject: [PATCH] readelf: fix out-of-bounds read of DW_FORM_strx offsets in
 print_form_data

For the DW_FORM_strx forms print_form_data looks up the string offset
in .debug_str_offsets after checking

    if (data == NULL
        || data->d_size - str_offsets_base < val * offset_len)
      str = "???";

str_offsets_base is read from the file and is never checked against
the section size.  When it is larger than d_size the subtraction wraps,
the check passes and strreadp points past the section.  The following
(size_t) (strreadendp - strreadp) < offset_len check does not catch it
either, because the negative difference wraps too, so readelf reads
offset_len bytes at a file-controlled offset from the section data.
val * offset_len can also overflow for a large ULEB128 index.

libdw's own strx lookup in dwarf_formstring and readelf's
get_indexed_addr already check the base against the section size.  Do
the same here, and compare the index against the number of entries
instead of multiplying.  print_form_data is used for .debug_macro and
for DWARF 5 .debug_line, so both are covered.

Reproducible with eu-readelf --debug-dump=macro on a crafted ELF whose
DW_AT_str_offsets_base is larger than its .debug_str_offsets section.
AddressSanitizer reports a SEGV reading far past the section in
print_form_data.

	* src/readelf.c (print_form_data): Check str_offsets_base and the
	strx index against the .debug_str_offsets size.

Signed-off-by: Sujal Tuladhar <[email protected]>
---
 src/readelf.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/readelf.c b/src/readelf.c
index 9ca2d278..f632be0a 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -9252,7 +9252,8 @@ print_form_data (Dwarf *dbg, int form, const unsigned char *readp,
     strx_val:
       data = dbg->sectiondata[IDX_debug_str_offsets];
       if (data == NULL
-	  || data->d_size - str_offsets_base < val * offset_len)
+	  || str_offsets_base > data->d_size
+	  || val > (data->d_size - str_offsets_base) / offset_len)
 	str = "???";
       else
 	{
-- 
2.43.0

Reply via email to