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