Skip to content

Conversation

@gv
Copy link
Contributor

@gv gv commented Nov 4, 2025

Fix crash when an inline site record in the PDB file contains type index which is out of bounds

@llvmbot
Copy link
Member

llvmbot commented Nov 4, 2025

@llvm/pr-subscribers-lldb

@llvm/pr-subscribers-platform-windows

Author: Vladimir Gorsunov (gv)

Changes

Fix crash when an inline site record in the PDB file contains type index which is out of bounds


Full diff: https://github.com/llvm/llvm-project/pull/166455.diff

3 Files Affected:

  • (modified) lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp (+9-5)
  • (modified) llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h (+1-1)
  • (modified) llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp (+13-5)
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index aaec1600dacff..8be6dd196c07c 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -1719,19 +1719,23 @@ void SymbolFileNativePDB::ParseInlineSite(PdbCompilandSymId id,
   }
 
   // Get the inlined function name.
-  CVType inlinee_cvt = m_index->ipi().getType(inline_site.Inlinee);
   std::string inlinee_name;
-  if (inlinee_cvt.kind() == LF_MFUNC_ID) {
+  llvm::Expected<CVType> inlinee_cvt =
+      m_index->ipi().typeCollection().getTypeOrError(inline_site.Inlinee);
+  if (!inlinee_cvt) {
+    inlinee_name = "[error reading function name: " +
+                   llvm::toString(inlinee_cvt.takeError()) + "]";
+  } else if (inlinee_cvt->kind() == LF_MFUNC_ID) {
     MemberFuncIdRecord mfr;
     cantFail(
-        TypeDeserializer::deserializeAs<MemberFuncIdRecord>(inlinee_cvt, mfr));
+        TypeDeserializer::deserializeAs<MemberFuncIdRecord>(*inlinee_cvt, mfr));
     LazyRandomTypeCollection &types = m_index->tpi().typeCollection();
     inlinee_name.append(std::string(types.getTypeName(mfr.ClassType)));
     inlinee_name.append("::");
     inlinee_name.append(mfr.getName().str());
-  } else if (inlinee_cvt.kind() == LF_FUNC_ID) {
+  } else if (inlinee_cvt->kind() == LF_FUNC_ID) {
     FuncIdRecord fir;
-    cantFail(TypeDeserializer::deserializeAs<FuncIdRecord>(inlinee_cvt, fir));
+    cantFail(TypeDeserializer::deserializeAs<FuncIdRecord>(*inlinee_cvt, fir));
     TypeIndex parent_idx = fir.getParentScope();
     if (!parent_idx.isNoneType()) {
       LazyRandomTypeCollection &ids = m_index->ipi().typeCollection();
diff --git a/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h b/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
index 5b23ac9f862a0..da18339b8662b 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
@@ -70,7 +70,7 @@ class LLVM_ABI LazyRandomTypeCollection : public TypeCollection {
   uint32_t getOffsetOfType(TypeIndex Index);
 
   std::optional<CVType> tryGetType(TypeIndex Index);
-
+  llvm::Expected<CVType> getTypeOrError(TypeIndex Index);
   CVType getType(TypeIndex Index) override;
   StringRef getTypeName(TypeIndex Index) override;
   bool contains(TypeIndex Index) override;
diff --git a/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
index 23ab5344df1ed..760fe0e709ba4 100644
--- a/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
+++ b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
@@ -93,20 +93,28 @@ CVType LazyRandomTypeCollection::getType(TypeIndex Index) {
   return Records[Index.toArrayIndex()].Type;
 }
 
-std::optional<CVType> LazyRandomTypeCollection::tryGetType(TypeIndex Index) {
+llvm::Expected<CVType>
+LazyRandomTypeCollection::getTypeOrError(TypeIndex Index) {
   if (Index.isSimple())
-    return std::nullopt;
+    return llvm::createStringError("Type index too low (%d)", Index.getIndex());
 
   if (auto EC = ensureTypeExists(Index)) {
-    consumeError(std::move(EC));
-    return std::nullopt;
+    return std::move(EC);
   }
 
   if (!contains(Index))
-    return std::nullopt;
+    return llvm::createStringError("Type index too high (%d)",
+                                   Index.getIndex());
   return Records[Index.toArrayIndex()].Type;
 }
 
+std::optional<CVType> LazyRandomTypeCollection::tryGetType(TypeIndex Index) {
+  llvm::Expected<CVType> res = getTypeOrError(Index);
+  if (!res)
+    return std::nullopt;
+  return std::move(*res);
+}
+
 StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) {
   if (Index.isNoneType() || Index.isSimple())
     return TypeIndex::simpleTypeName(Index);

@llvmbot
Copy link
Member

llvmbot commented Nov 4, 2025

@llvm/pr-subscribers-debuginfo

Author: Vladimir Gorsunov (gv)

Changes

Fix crash when an inline site record in the PDB file contains type index which is out of bounds


Full diff: https://github.com/llvm/llvm-project/pull/166455.diff

3 Files Affected:

  • (modified) lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp (+9-5)
  • (modified) llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h (+1-1)
  • (modified) llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp (+13-5)
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index aaec1600dacff..8be6dd196c07c 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -1719,19 +1719,23 @@ void SymbolFileNativePDB::ParseInlineSite(PdbCompilandSymId id,
   }
 
   // Get the inlined function name.
-  CVType inlinee_cvt = m_index->ipi().getType(inline_site.Inlinee);
   std::string inlinee_name;
-  if (inlinee_cvt.kind() == LF_MFUNC_ID) {
+  llvm::Expected<CVType> inlinee_cvt =
+      m_index->ipi().typeCollection().getTypeOrError(inline_site.Inlinee);
+  if (!inlinee_cvt) {
+    inlinee_name = "[error reading function name: " +
+                   llvm::toString(inlinee_cvt.takeError()) + "]";
+  } else if (inlinee_cvt->kind() == LF_MFUNC_ID) {
     MemberFuncIdRecord mfr;
     cantFail(
-        TypeDeserializer::deserializeAs<MemberFuncIdRecord>(inlinee_cvt, mfr));
+        TypeDeserializer::deserializeAs<MemberFuncIdRecord>(*inlinee_cvt, mfr));
     LazyRandomTypeCollection &types = m_index->tpi().typeCollection();
     inlinee_name.append(std::string(types.getTypeName(mfr.ClassType)));
     inlinee_name.append("::");
     inlinee_name.append(mfr.getName().str());
-  } else if (inlinee_cvt.kind() == LF_FUNC_ID) {
+  } else if (inlinee_cvt->kind() == LF_FUNC_ID) {
     FuncIdRecord fir;
-    cantFail(TypeDeserializer::deserializeAs<FuncIdRecord>(inlinee_cvt, fir));
+    cantFail(TypeDeserializer::deserializeAs<FuncIdRecord>(*inlinee_cvt, fir));
     TypeIndex parent_idx = fir.getParentScope();
     if (!parent_idx.isNoneType()) {
       LazyRandomTypeCollection &ids = m_index->ipi().typeCollection();
diff --git a/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h b/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
index 5b23ac9f862a0..da18339b8662b 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
@@ -70,7 +70,7 @@ class LLVM_ABI LazyRandomTypeCollection : public TypeCollection {
   uint32_t getOffsetOfType(TypeIndex Index);
 
   std::optional<CVType> tryGetType(TypeIndex Index);
-
+  llvm::Expected<CVType> getTypeOrError(TypeIndex Index);
   CVType getType(TypeIndex Index) override;
   StringRef getTypeName(TypeIndex Index) override;
   bool contains(TypeIndex Index) override;
diff --git a/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
index 23ab5344df1ed..760fe0e709ba4 100644
--- a/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
+++ b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
@@ -93,20 +93,28 @@ CVType LazyRandomTypeCollection::getType(TypeIndex Index) {
   return Records[Index.toArrayIndex()].Type;
 }
 
-std::optional<CVType> LazyRandomTypeCollection::tryGetType(TypeIndex Index) {
+llvm::Expected<CVType>
+LazyRandomTypeCollection::getTypeOrError(TypeIndex Index) {
   if (Index.isSimple())
-    return std::nullopt;
+    return llvm::createStringError("Type index too low (%d)", Index.getIndex());
 
   if (auto EC = ensureTypeExists(Index)) {
-    consumeError(std::move(EC));
-    return std::nullopt;
+    return std::move(EC);
   }
 
   if (!contains(Index))
-    return std::nullopt;
+    return llvm::createStringError("Type index too high (%d)",
+                                   Index.getIndex());
   return Records[Index.toArrayIndex()].Type;
 }
 
+std::optional<CVType> LazyRandomTypeCollection::tryGetType(TypeIndex Index) {
+  llvm::Expected<CVType> res = getTypeOrError(Index);
+  if (!res)
+    return std::nullopt;
+  return std::move(*res);
+}
+
 StringRef LazyRandomTypeCollection::getTypeName(TypeIndex Index) {
   if (Index.isNoneType() || Index.isSimple())
     return TypeIndex::simpleTypeName(Index);

@JDevlieghere
Copy link
Member

Can you add a test that (used to) reproduce(s) the crash? Presumably this isn't covered when running the API tests with PDB support?

@gv
Copy link
Contributor Author

gv commented Nov 5, 2025

Can you add a test that (used to) reproduce(s) the crash? Presumably this isn't covered when running the API tests with PDB support?

I'll try, although I'm not sure yet how. It will need a PDB file which is broken in a certain way. Right now I have 200MB PDB produced by MSVS that triggered a crash for me, but I don't think there's any way to use that. There probably must be a way to use llbm-pdbutil yaml2pdb to synthesize something small and sufficiently broken...

Copy link
Contributor

@Nerixyz Nerixyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LLDB changes look good to me. Though, it looks like the LLVM tests are failing. It might be easier if you land the LLVM changes first.

Can you add a test that (used to) reproduce(s) the crash? Presumably this isn't covered when running the API tests with PDB support?

This feels similar to #166090 (i.e. an artifact of incremental linking), so testing is a bit more complex. yaml2pdb is probably the way to go here. Currently, that won't work, because the generated PDB is missing the section headers. I'll try to add this, so both PRs can have a test.

Opened #166566 for this.

Comment on lines 112 to 115
llvm::Expected<CVType> res = getTypeOrError(Index);
if (!res)
return std::nullopt;
return *res;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use llvm::expectedToOptional here.

…alue

Fix crash when an inline site record in the PDB file contains type
index which is out of bounds
@gv
Copy link
Contributor Author

gv commented Nov 18, 2025

Opened #166566 for this.

As #166566 is merged I tried to load a PDB generated by llvm-pdbutil into lldb. I took the file inline_sites.test.tmp.pdb produced by lld-link from inline_sites.test and put it through llvm-pdbutil pdb2yaml --all | llvm-pdbutil yaml2pdb. The resulting file can be loaded into the debugger all right (which wasn't the case before) but it can't resolve any address, because it doesn't have any section contributions (that can be confirmed with llvm-pdbutil dump --section-contribs).

AFAICS there is no support for converting section contributions to or from YAML. Until that is changed, llvm-pdbutil can't be used for creating a PDB file that will trigger the crash because the crash is in the address resolution path. I. e.

Thread 1 "lldb" received signal SIGSEGV, Segmentation fault.
0x00007dd8c9160ca1 in llvm::codeview::LazyRandomTypeCollection::getType (this=0x63574517b850, Index=...) at /home/vg/25-llvm/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp:93
93 return Records[Index.toArrayIndex()].Type;
(gdb) bt
#0 0x00007dd8c9160ca1 in llvm::codeview::LazyRandomTypeCollection::getType (this=0x63574517b850, Index=...)
at /home/vg/25-llvm/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp:93
#1 0x00007dd8c7821ac3 in lldb_private::npdb::SymbolFileNativePDB::ParseInlineSite (this=0x6357450af750,
id=..., func_addr=...)
at /home/vg/25-llvm/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp:1624
#2 0x00007dd8c7821ebd in operator() (id=..., kind=, __closure=0x7ffe98c2fac0)
at /home/vg/25-llvm/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp:1350
#3 llvm::function_ref<bool(llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId)>::callback_fn<lldb_private::npdb::SymbolFileNativePDB::ParseLineTable(lldb_private::CompileUnit&)::<lambda(llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId)> >(intptr_t, llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId) (callable=callable@entry=140731461335744, params#0=, params#1=...,
params#1@entry=...) at /home/vg/25-llvm/release/include/llvm/ADT/STLFunctionalExtras.h:46
#4 0x00007dd8c7818259 in llvm::function_ref<bool (llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId)>::operator()(llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId) const (params#1=...,
params#0=, this=)
at /home/vg/25-llvm/release/include/llvm/ADT/STLFunctionalExtras.h:68
#5 lldb_private::npdb::SymbolFileNativePDB::ParseSymbolArrayInScope(lldb_private::npdb::PdbCompilandSymId, llvm::function_ref<bool (llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId)>) (
this=, parent_id=..., fn=...)
at /home/vg/25-llvm/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp:1687
#6 0x00007dd8c7820580 in lldb_private::npdb::SymbolFileNativePDB::ParseLineTable (this=0x6357450af750,
comp_unit=...) at /home/vg/25-llvm/release/include/llvm/ADT/STLFunctionalExtras.h:55
#7 0x00007dd8c700c494 in lldb_private::CompileUnit::GetLineTable (this=0x635744e8daf0)
at /home/vg/25-llvm/lldb/source/Symbol/CompileUnit.cpp:171
#8 0x00007dd8c781a05a in lldb_private::npdb::SymbolFileNativePDB::CreateBlock (this=0x6357450af750,
block_id=...)
at /home/vg/25-llvm/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp:444
#9 0x00007dd8c781a2c3 in lldb_private::npdb::SymbolFileNativePDB::GetOrCreateBlock (this=0x6357450af750,
block_id=...)
at /home/vg/25-llvm/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp:1024
#10 0x00007dd8c781a323 in operator() (id=..., kind=llvm::codeview::S_INLINESITE, __closure=0x7ffe98c30228)
at /home/vg/25-llvm/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp:1661
#11 llvm::function_ref<bool(llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId)>::callback_fn<lldb_private::npdb::SymbolFileNativePDB::ParseBlocksRecursive(lldb_private::Function&)::<lambda(llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId)> >(intptr_t, llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId) (callable=callable@entry=140731461337640, params#0=, params#1=...,
params#1@entry=...) at /home/vg/25-llvm/release/include/llvm/ADT/STLFunctionalExtras.h:46
#12 0x00007dd8c7818259 in llvm::function_ref<bool (llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilan--Type for more, q to quit, c to continue without paging--
dSymId)>::operator()(llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId) const (params#1=...,
params#0=, this=)
at /home/vg/25-llvm/release/include/llvm/ADT/STLFunctionalExtras.h:68
#13 lldb_private::npdb::SymbolFileNativePDB::ParseSymbolArrayInScope(lldb_private::npdb::PdbCompilandSymId, llvm::function_ref<bool (llvm::codeview::SymbolKind, lldb_private::npdb::PdbCompilandSymId)>) (
this=, parent_id=..., fn=...)
at /home/vg/25-llvm/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp:1687
#14 0x00007dd8c7818349 in lldb_private::npdb::SymbolFileNativePDB::ParseBlocksRecursive (
this=0x6357450af750, func=...)
at /home/vg/25-llvm/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp:1668
#15 0x00007dd8c7015c71 in lldb_private::Function::GetBlock (this=0x635744f3f440,
can_create=can_create@entry=true) at /home/vg/25-llvm/lldb/source/Symbol/Function.cpp:386
#16 0x00007dd8c781a534 in lldb_private::npdb::SymbolFileNativePDB::ResolveSymbolContext (
this=0x6357450af750, addr=..., resolve_scope=72, sc=...)
at /home/vg/25-llvm/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp:1174
#17 0x00007dd8c6f62f66 in lldb_private::Module::ResolveSymbolContextForAddress (this=0x635745141860,
so_addr=..., resolve_scope=resolve_scope@entry=72, sc=...,
resolve_tail_call_address=resolve_tail_call_address@entry=false)
at /home/vg/25-llvm/lldb/source/Core/Module.cpp:487
#18 0x00007dd8c6f39470 in lldb_private::Address::CalculateSymbolContext (this=0x6357461011a0,
sc=0x635745fcb840, resolve_scope=72) at /home/vg/25-llvm/lldb/source/Core/Address.cpp:832
#19 0x00007dd8c6f394c7 in lldb_private::Address::ResolveFunctionScope (this=this@entry=0x6357461011a0,
sym_ctx=...) at /home/vg/25-llvm/lldb/source/Core/Address.cpp:270
#20 0x00007dd8c70dd805 in lldb_private::RegisterContextUnwind::InitializeZerothFrame (this=0x635746101110)
at /home/vg/25-llvm/lldb/source/Target/RegisterContextUnwind.cpp:163
#21 0x00007dd8c70ddfc9 in lldb_private::RegisterContextUnwind::RegisterContextUnwind (
this=this@entry=0x635746101110, thread=...,
next_frame=std::shared_ptr<lldb_private::RegisterContextUnwind> (empty) = {...}, sym_ctx=...,
frame_number=frame_number@entry=0, unwind_lldb=...)
at /home/vg/25-llvm/lldb/source/Target/RegisterContextUnwind.cpp:73
#22 0x00007dd8c70cd220 in lldb_private::UnwindLLDB::AddFirstFrame (this=0x7dd7a0050470)
at /home/vg/25-llvm/lldb/source/Target/UnwindLLDB.cpp:81
#23 lldb_private::UnwindLLDB::AddFirstFrame (this=0x7dd7a0050470)
at /home/vg/25-llvm/lldb/source/Target/UnwindLLDB.cpp:71
#24 0x00007dd8c70cd519 in lldb_private::UnwindLLDB::DoGetFrameInfoAtIndex (this=0x7dd7a0050470, idx=0,
cfa=@0x7ffe98c309f0: 18446744073709551615, pc=@0x7ffe98c309e8: 18446744073709551615,
behaves_like_zeroth_frame=@0x7ffe98c309e7: true)
at /home/vg/25-llvm/lldb/source/Target/UnwindLLDB.cpp:400

@Nerixyz
Copy link
Contributor

Nerixyz commented Nov 18, 2025

The resulting file can be loaded into the debugger all right (which wasn't the case before) but it can't resolve any address, because it doesn't have any section contributions (that can be confirmed with llvm-pdbutil dump --section-contribs).

I'm surprised the debugger can even load the PDB. In my testing, section contributions weren't necessary.

I forgot to mention that this needs another patch - #168211.
The reason LLDB is crashing right now is that the offsets included in symbols from the modules (mainly S_G/LPROC32) are incorrect. And that's because the reconstructed symbols from YAML don't include all information. Specifically, S_INLINESITE has variable length annotation data, which gets lost and causes it to change size.
S_G/LPROC32 contains an offset to where the function will end (other records include similar offsets iirc). Now that S_INLINESITE has changed size, this is incorrect.

Arguably, we should also handle this gracefully.

@Nerixyz
Copy link
Contributor

Nerixyz commented Nov 19, 2025

Should work now. You can do something similar to this: https://github.com/llvm/llvm-project/pull/166090/files#diff-d4185e28c08f72d5ae39d59054bcd6cea460ff7a95706bbc0809c37cc5409a2e If you change the FunctionType back to 0x1001 but change the Inlinee in S_INLINESITE.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants