[Lldb-commits] [lldb] [lldb] Recognize flag forms in DWARFFormValue's constant accessors (PR #229738)
Yao Qi via lldb-commits
lldb-commits at lists.llvm.org
Wed Oct 7 05:21:17 PDT 2026
https://github.com/qiyao created https://github.com/llvm/llvm-project/pull/229738
LLDB crashes on parsing a `DW_TAG_LLVM_ptrauth_type` with
`DW_AT_LLVM_ptrauth_address_discriminated`, here is the crash
inside lldb,
```
thread #1, stop reason = libc++: .../usr/include/c++/v1/optional:1150:libc++ Hardening assertion this->has_value() failed: optional operator*
called on a disengaged value
frame #2: llvm::DWARFTypePrinter<...>::appendUnqualifiedNameAfter(...)::'lambda'(...)::operator()(Attr=DW_AT_LLVM_ptrauth_address_discriminated) const at DWARFTypePrinter.h:375
374 if (auto Form = D.find(Attr))
-> 375 return *Form->getAsUnsignedConstant();
(lldb) frame variable Form
(std::optional<DWARFFormValue>) Form = Has Value=true {
Value = { m_form = DW_FORM_flag_present, m_value = (uval = 1, ...) }
}
```
The dereference that actually traps is one level deeper:
`Form->getAsUnsignedConstant()` itself returns `std::nullopt`, because
`DWARFFormValue::getAsUnsignedConstant()` and `getAsSignedConstant()`
only accept forms where `IsDataForm()` is true, and `IsDataForm()`
lists `sdata`, `udata`, and `data1` through `data8`, but not
`DW_FORM_flag` or `DW_FORM_flag_present`.
DWARF form values are grouped into classes, and `constant` and `flag`
are two separate classes. LLVM's own `DWARFFormValue` (in
`llvm/lib/DebugInfo/DWARF`) keeps that distinction: its
`getAsUnsignedConstant()` and `getAsSignedConstant()` check
`isFormClass(FC_Constant) || isFormClass(FC_Flag)`. LLDB's
`DWARFFormValue`'s two accessors check only `IsDataForm()`,that is
the divergence this crash exposes.
Add a new `IsFlagForm()`, parallel to the existing `IsBlockForm()`, and
have `getAsUnsignedConstant()` and `getAsSignedConstant()` accept
either `IsDataForm()` or `IsFlagForm()`, matching LLVM's own
`FC_Constant`/`FC_Flag` check. `DWARFFormValue::ExtractValue()` already
stores a flag's 0/1 value in `m_value.uval` the same way it stores any
other data form's value, so there is nothing to lose by accepting it
here.
Add a regression test building a class template whose argument type is
an address-discriminated `DW_TAG_LLVM_ptrauth_type`, then calling
`GetDIEClassTemplateParams`.
>From c47c907415c385c0a5f56aa3fc0b1f68e2e60b13 Mon Sep 17 00:00:00 2001
From: Yao Qi <yao_qi at apple.com>
Date: Fri, 2 Oct 2026 16:52:26 +0100
Subject: [PATCH] [lldb] Recognize flag forms in DWARFFormValue's constant
accessors
LLDB crashes on parsing a `DW_TAG_LLVM_ptrauth_type` with
`DW_AT_LLVM_ptrauth_address_discriminated`, here is the crash
inside lldb,
```
thread #1, stop reason = libc++: .../usr/include/c++/v1/optional:1150:libc++ Hardening assertion this->has_value() failed: optional operator*
called on a disengaged value
frame #2: llvm::DWARFTypePrinter<...>::appendUnqualifiedNameAfter(...)::'lambda'(...)::operator()(Attr=DW_AT_LLVM_ptrauth_address_discriminated) const at DWARFTypePrinter.h:375
374 if (auto Form = D.find(Attr))
-> 375 return *Form->getAsUnsignedConstant();
(lldb) frame variable Form
(std::optional<DWARFFormValue>) Form = Has Value=true {
Value = { m_form = DW_FORM_flag_present, m_value = (uval = 1, ...) }
}
```
The dereference that actually traps is one level deeper:
`Form->getAsUnsignedConstant()` itself returns `std::nullopt`, because
`DWARFFormValue::getAsUnsignedConstant()` and `getAsSignedConstant()`
only accept forms where `IsDataForm()` is true, and `IsDataForm()`
lists `sdata`, `udata`, and `data1` through `data8`, but not
`DW_FORM_flag` or `DW_FORM_flag_present`.
DWARF form values are grouped into classes, and `constant` and `flag`
are two separate classes. LLVM's own `DWARFFormValue` (in
`llvm/lib/DebugInfo/DWARF`) keeps that distinction: its
`getAsUnsignedConstant()` and `getAsSignedConstant()` check
`isFormClass(FC_Constant) || isFormClass(FC_Flag)`. LLDB's
`DWARFFormValue`'s two accessors check only `IsDataForm()`,that is
the divergence this crash exposes.
Add a new `IsFlagForm()`, parallel to the existing `IsBlockForm()`, and
have `getAsUnsignedConstant()` and `getAsSignedConstant()` accept
either `IsDataForm()` or `IsFlagForm()`, matching LLVM's own
`FC_Constant`/`FC_Flag` check. `DWARFFormValue::ExtractValue()` already
stores a flag's 0/1 value in `m_value.uval` the same way it stores any
other data form's value, so there is nothing to lose by accepting it
here.
Add a regression test building a class template whose argument type is
an address-discriminated `DW_TAG_LLVM_ptrauth_type`, then calling
`GetDIEClassTemplateParams`.
---
.../SymbolFile/DWARF/DWARFFormValue.cpp | 16 +++-
.../Plugins/SymbolFile/DWARF/DWARFFormValue.h | 1 +
.../DWARF/DWARFASTParserClangTests.cpp | 95 +++++++++++++++++++
3 files changed, 110 insertions(+), 2 deletions(-)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp
index c599da27f4701..e0d51a525e7d8 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp
@@ -584,13 +584,14 @@ uint64_t DWARFFormValue::Reference(dw_offset_t base_offset) const {
}
std::optional<uint64_t> DWARFFormValue::getAsUnsignedConstant() const {
- if ((!IsDataForm(m_form)) || m_form == llvm::dwarf::DW_FORM_sdata)
+ if ((!IsDataForm(m_form) && !IsFlagForm(m_form)) ||
+ m_form == llvm::dwarf::DW_FORM_sdata)
return std::nullopt;
return m_value.uval;
}
std::optional<int64_t> DWARFFormValue::getAsSignedConstant() const {
- if ((!IsDataForm(m_form)) ||
+ if ((!IsDataForm(m_form) && !IsFlagForm(m_form)) ||
(m_form == llvm::dwarf::DW_FORM_udata &&
uint64_t(std::numeric_limits<int64_t>::max()) < m_value.uval))
return std::nullopt;
@@ -640,6 +641,17 @@ bool DWARFFormValue::IsDataForm(const dw_form_t form) {
llvm_unreachable("All cases handled above!");
}
+bool DWARFFormValue::IsFlagForm(const dw_form_t form) {
+ switch (form) {
+ case DW_FORM_flag:
+ case DW_FORM_flag_present:
+ return true;
+ default:
+ return false;
+ }
+ llvm_unreachable("All cases handled above!");
+}
+
bool DWARFFormValue::FormIsSupported(dw_form_t form) {
switch (form) {
case DW_FORM_addr:
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h
index 613948f2f3c9b..def06d59c86fe 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h
@@ -72,6 +72,7 @@ class DWARFFormValue {
lldb::offset_t *offset_ptr, const DWARFUnit *unit);
static bool IsBlockForm(const dw_form_t form);
static bool IsDataForm(const dw_form_t form);
+ static bool IsFlagForm(const dw_form_t form);
static int Compare(const DWARFFormValue &a, const DWARFFormValue &b);
void Clear();
static bool FormIsSupported(dw_form_t form);
diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
index 4d4dc57816694..729b47c7d08d0 100644
--- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserClangTests.cpp
@@ -451,6 +451,101 @@ TEST_F(DWARFASTParserClangTests, TestPtrAuthParsing) {
ASSERT_EQ(compiler_type.GetPtrAuthDiscriminator(), 42U);
}
+TEST_F(DWARFASTParserClangTests, TestPtrAuthAddressDiscriminatedTemplateParam) {
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AARCH64
+DWARF:
+ debug_str:
+ - Foo
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x01
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x02
+ Tag: DW_TAG_structure_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Code: 0x03
+ Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Code: 0x04
+ Tag: DW_TAG_LLVM_ptrauth_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_LLVM_ptrauth_key
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_LLVM_ptrauth_address_discriminated
+ Form: DW_FORM_flag_present
+ - Code: 0x05
+ Tag: DW_TAG_pointer_type
+ Children: DW_CHILDREN_no
+
+ debug_info:
+ - Version: 5
+ UnitType: DW_UT_compile
+ AddrSize: 8
+ Entries:
+# 0x0c: DW_TAG_compile_unit
+ - AbbrCode: 0x01
+ Values:
+ - Value: 0x04 # DW_LANG_C_plus_plus
+
+# 0x0f: DW_TAG_structure_type
+# DW_AT_name [DW_FORM_strp] (\"Foo\")
+ - AbbrCode: 0x02
+ Values:
+ - Value: 0x00
+
+# 0x14: DW_TAG_template_type_parameter
+# DW_AT_type [DW_FORM_ref4] (0x0000001a)
+ - AbbrCode: 0x03
+ Values:
+ - Value: 0x1a
+
+ - AbbrCode: 0x00 # end of children of structure_type
+
+# 0x1a: DW_TAG_LLVM_ptrauth_type
+# DW_AT_type [DW_FORM_ref4] (0x00000020)
+# DW_AT_LLVM_ptrauth_key [DW_FORM_data1] (0x04)
+# DW_AT_LLVM_ptrauth_address_discriminated [DW_FORM_flag_present] (true)
+ - AbbrCode: 0x04
+ Values:
+ - Value: 0x20
+ - Value: 0x04
+
+# 0x20: DW_TAG_pointer_type
+ - AbbrCode: 0x05
+
+ - AbbrCode: 0x00 # end of children of compile_unit
+...
+)";
+ DWARFASTParserClangYAMLTester tester(yamldata);
+
+ DWARFDIE cu_die = tester.GetCUDIE();
+ DWARFDIE struct_die = cu_die.GetFirstChild();
+ ASSERT_EQ(struct_die.Tag(), DW_TAG_structure_type);
+
+ EXPECT_EQ(tester.GetParser().GetDIEClassTemplateParams(struct_die),
+ "<void *__ptrauth(4, 1, 0x00)>");
+}
+
struct ExtractIntFromFormValueTest : public testing::Test {
SubsystemRAII<FileSystem, HostInfo> subsystems;
clang_utils::TypeSystemClangHolder holder;
More information about the lldb-commits
mailing list