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
