[llvm] [DebugInfo] Add bounds checks to debug-info parsers (PR #211797)
Matt Davis via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 24 06:58:40 PDT 2026
https://github.com/enferex created https://github.com/llvm/llvm-project/pull/211797
Fix out-of-bounds accesses reachable from crafted debug info. I ran a LLM security scan on an LLVM derived repo; a number of issues were discovered. This PR is a subset of fixes to address those discovered defects.
- DWARF DWARFUnitIndex: reject row Index > NumUnits before writing Contribs.
- GSYM LineTable: reject LineRange <= 0 to avoid a divide-by-zero (SIGFPE).
- PDB HashTable: reject Present/Deleted bit indices >= Capacity.
- PDB TpiStream: reduce raw hash value mod NumHashBuckets before indexing.
- CodeView TypeStreamMerger: validate the Ref range fits Storage before remapping.
- BTF BTFParser: guard the disengaged-optional section deref in parseRelocInfo.
Assisted by LLMs.
>From 9abb3f1285b4ef0b31fd672eb7e0dcd83fc5e072 Mon Sep 17 00:00:00 2001
From: Matt Davis <mattd at nvidia.com>
Date: Thu, 23 Jul 2026 18:59:55 +0000
Subject: [PATCH] [DebugInfo] Add bounds checks to debug-info parsers
Fix out-of-bounds accesses reachable from crafted debug info in llvm-dwarfdump,
llvm-pdbutil, lld, llvm-symbolizer, and llvm-objdump. Each guard mirrors an
existing idiom in the same or sibling code.
- DWARF DWARFUnitIndex: reject row Index > NumUnits before writing Contribs.
- GSYM LineTable: reject LineRange <= 0 to avoid a divide-by-zero (SIGFPE).
- PDB HashTable: reject Present/Deleted bit indices >= Capacity.
- PDB TpiStream: reduce raw hash value mod NumHashBuckets before indexing.
- CodeView TypeStreamMerger: validate the Ref range fits Storage before remapping.
- BTF BTFParser: guard the disengaged-optional section deref in parseRelocInfo.
Co-Authored-By: Claude Opus 4.8 <noreply at anthropic.com>
---
llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h | 10 ++++++++++
llvm/lib/DebugInfo/BTF/BTFParser.cpp | 5 +++++
llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp | 7 +++++++
llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp | 2 ++
llvm/lib/DebugInfo/GSYM/LineTable.cpp | 4 ++++
llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp | 2 +-
6 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h b/llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h
index efece7a17676e..ba02459cbe6de 100644
--- a/llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h
+++ b/llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h
@@ -134,9 +134,19 @@ class HashTable {
if (Present.count() != H->Size)
return make_error<RawError>(raw_error_code::corrupt_file,
"Present bit vector does not match size!");
+ if (!Present.empty() &&
+ static_cast<uint32_t>(Present.find_last()) >= H->Capacity)
+ return make_error<RawError>(
+ raw_error_code::corrupt_file,
+ "Present bit vector contains out-of-bounds index!");
if (auto EC = readSparseBitVector(Stream, Deleted))
return EC;
+ if (!Deleted.empty() &&
+ static_cast<uint32_t>(Deleted.find_last()) >= H->Capacity)
+ return make_error<RawError>(
+ raw_error_code::corrupt_file,
+ "Deleted bit vector contains out-of-bounds index!");
if (Present.intersects(Deleted))
return make_error<RawError>(raw_error_code::corrupt_file,
"Present bit vector intersects deleted!");
diff --git a/llvm/lib/DebugInfo/BTF/BTFParser.cpp b/llvm/lib/DebugInfo/BTF/BTFParser.cpp
index e22396c50d787..9f71dcb8af988 100644
--- a/llvm/lib/DebugInfo/BTF/BTFParser.cpp
+++ b/llvm/lib/DebugInfo/BTF/BTFParser.cpp
@@ -352,6 +352,11 @@ Error BTFParser::parseRelocInfo(ParseContext &Ctx, DataExtractor &Extractor,
uint32_t NumInfo = Extractor.getU32(C);
StringRef SecName = findString(SecNameOff);
std::optional<SectionRef> Sec = Ctx.findSection(SecName);
+ if (!C)
+ return Err(".BTF.ext", C);
+ if (!Sec)
+ return Err("") << "can't find section '" << SecName
+ << "' while parsing .BTF.ext field reloc info";
BTFRelocVector &Relocs = SectionRelocs[Sec->getIndex()];
for (uint32_t I = 0; C && I < NumInfo; ++I) {
uint64_t RecStart = C.tell();
diff --git a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp
index b9aaa3146c9cc..1274cc1c7b6f8 100644
--- a/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp
+++ b/llvm/lib/DebugInfo/CodeView/TypeStreamMerger.cpp
@@ -403,6 +403,13 @@ TypeStreamMerger::remapIndices(const CVType &OriginalType,
uint8_t *DestContent = Storage.data() + sizeof(RecordPrefix);
for (auto &Ref : Refs) {
+ // Ref.Count can come from a field inside the record, so it may not match
+ // the actual data length. Skip the record if the run of TypeIndex values
+ // would extend past Storage.
+ uint64_t Begin = sizeof(RecordPrefix) + static_cast<uint64_t>(Ref.Offset);
+ if (Begin + static_cast<uint64_t>(Ref.Count) * sizeof(TypeIndex) >
+ Storage.size())
+ return {};
TypeIndex *DestTIs =
reinterpret_cast<TypeIndex *>(DestContent + Ref.Offset);
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp
index 08abcf13d0618..f5d7b1e19dd11 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp
@@ -159,6 +159,8 @@ bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) {
auto Index = IndexData.getU32(&Offset);
if (!Index)
continue;
+ if (Index > Header.NumUnits)
+ return false;
Rows[i].Index = this;
Rows[i].Contributions =
std::make_unique<Entry::SectionContribution[]>(Header.NumColumns);
diff --git a/llvm/lib/DebugInfo/GSYM/LineTable.cpp b/llvm/lib/DebugInfo/GSYM/LineTable.cpp
index 7767cdb943949..f94234a836716 100644
--- a/llvm/lib/DebugInfo/GSYM/LineTable.cpp
+++ b/llvm/lib/DebugInfo/GSYM/LineTable.cpp
@@ -63,6 +63,10 @@ static llvm::Error parse(GsymDataExtractor &Data, uint64_t BaseAddr,
"0x%8.8" PRIx64 ": missing LineTable MaxDelta", Offset);
int64_t MaxDelta = Data.getSLEB128(&Offset);
int64_t LineRange = MaxDelta - MinDelta + 1;
+ if (LineRange <= 0)
+ return createStringError(std::errc::io_error,
+ "0x%8.8" PRIx64 ": invalid LineTable LineRange",
+ Offset);
if (!Data.isValidOffset(Offset))
return createStringError(std::errc::io_error,
"0x%8.8" PRIx64 ": missing LineTable FirstLine", Offset);
diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
index 24d2f9e3360ea..816b4cf4c9006 100644
--- a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
@@ -152,7 +152,7 @@ void TpiStream::buildHashMap() {
TypeIndex TIE{Header->TypeIndexEnd};
while (TIB < TIE) {
uint32_t HV = HashValues[TIB.toArrayIndex()];
- HashMap[HV].push_back(TIB++);
+ HashMap[HV % Header->NumHashBuckets].push_back(TIB++);
}
}
More information about the llvm-commits
mailing list