Bounds-check out.sh_name < shstrtab.sh_size in ForEachSection. Root Cause: ForEachSection in symbolize_elf.inc added out.sh_name to shstrtab_off without verifying out.sh_name < shstrtab.sh_size, reading past the section header string table on truncated or corrupted ELF headers. PiperOrigin-RevId: 1001701235
diff --git a/absl/debugging/symbolize_elf.inc b/absl/debugging/symbolize_elf.inc index 07b1167..6720c82 100644 --- a/absl/debugging/symbolize_elf.inc +++ b/absl/debugging/symbolize_elf.inc
@@ -625,13 +625,18 @@ if (!file.ReadFromOffsetExact(&out, sizeof(out), section_header_offset)) { return false; } + if (out.sh_name >= shstrtab.sh_size) { + return false; + } off_t name_offset = static_cast<off_t>(shstrtab.sh_offset) + out.sh_name; char header_name[kMaxSectionNameLen]; + const size_t max_name_len = + std::min<size_t>(kMaxSectionNameLen, shstrtab.sh_size - out.sh_name); ssize_t n_read = - file.ReadFromOffset(&header_name, kMaxSectionNameLen, name_offset); + file.ReadFromOffset(&header_name, max_name_len, name_offset); if (n_read < 0) { return false; - } else if (n_read > kMaxSectionNameLen) { + } else if (static_cast<size_t>(n_read) > max_name_len) { // Long read? return false; }
diff --git a/absl/debugging/symbolize_test.cc b/absl/debugging/symbolize_test.cc index 9ee0abd..16d2139 100644 --- a/absl/debugging/symbolize_test.cc +++ b/absl/debugging/symbolize_test.cc
@@ -559,6 +559,44 @@ EXPECT_FALSE(absl::Symbolize(static_cast<char*>(region) + 8, symbol_buf, sizeof(symbol_buf))); } + +TEST(Symbolize, ForEachSectionBoundsChecksShstrtab) { + ElfW(Ehdr) ehdr = {}; + memcpy(ehdr.e_ident, ELFMAG, SELFMAG); + ehdr.e_ident[EI_CLASS] = (sizeof(void*) == 8) ? ELFCLASS64 : ELFCLASS32; + ehdr.e_ident[EI_DATA] = ELFDATA2LSB; + ehdr.e_ident[EI_VERSION] = EV_CURRENT; + ehdr.e_type = ET_DYN; + ehdr.e_version = EV_CURRENT; + ehdr.e_shoff = sizeof(ElfW(Ehdr)); + ehdr.e_shentsize = sizeof(ElfW(Shdr)); + ehdr.e_shnum = 2; + ehdr.e_shstrndx = 0; + + constexpr absl::string_view kStrtabAndTrailing( + "\0.sec_trailing_bytes", sizeof("\0.sec_trailing_bytes")); + ElfW(Shdr) shstrtab = {}; + shstrtab.sh_type = SHT_STRTAB; + shstrtab.sh_offset = sizeof(ElfW(Ehdr)) + 2 * sizeof(ElfW(Shdr)); + shstrtab.sh_size = 5; // Only "\0.sec" is inside .shstrtab. + + ElfW(Shdr) sec = {}; + sec.sh_name = 5; // Out of bounds: sh_name >= shstrtab.sh_size. + sec.sh_type = SHT_PROGBITS; + + std::string image; + image.append(reinterpret_cast<const char*>(&ehdr), sizeof(ehdr)); + image.append(reinterpret_cast<const char*>(&shstrtab), sizeof(shstrtab)); + image.append(reinterpret_cast<const char*>(&sec), sizeof(sec)); + image.append(kStrtabAndTrailing.data(), kStrtabAndTrailing.size()); + + TempFile file(image); + int fd = open(file.path(), O_RDONLY); + ASSERT_NE(fd, -1); + EXPECT_FALSE(absl::debugging_internal::ForEachSection( + fd, [](absl::string_view, const ElfW(Shdr) &) { return true; })); + close(fd); +} #endif // ABSL_INTERNAL_HAVE_ELF_SYMBOLIZE #endif // !ABSL_INTERNAL_HAVE_DARWIN_SYMBOLIZE && // !ABSL_INTERNAL_HAVE_EMSCRIPTEN_SYMBOLIZE