[llvm] [llvm-dwp] Fix infinite loop on DWARFv5 DW_FORM_implicit_const (PR #205567)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 24 07:01:04 PDT 2026
https://github.com/youngd007 created https://github.com/llvm/llvm-project/pull/205567
llvm-dwp could spin forever (100% CPU, never terminating) when packaging a
DWARFv5 .dwo whose abbreviation table uses DW_FORM_implicit_const.
A DWARF abbreviation declaration is a list of (attribute, form) ULEB128
pairs terminated by a (0, 0) pair. DW_FORM_implicit_const is the only form
that stores an additional value -- an SLEB128 constant -- in the
abbreviation declaration itself (the value is not present in .debug_info).
getCUAbbrev() and getCUIdentifiers() in llvm/lib/DWP/DWP.cpp walked these
pairs but never consumed that extra SLEB128, so every implicit_const entry
shifted the cursor by one ULEB and desynced the rest of the table walk.
With enough implicit_const attributes preceding the compile unit's
abbreviation code, the scan in getCUAbbrev() runs off the end of
.debug_abbrev without ever matching the code. Past end-of-buffer
DataExtractor::getULEB128 returns 0 without advancing the offset, so the
loop `while (getULEB128(&Offset) != AbbrCode)` spins on `0 != AbbrCode`
forever. The input is valid DWARFv5; the bug is entirely in llvm-dwp.
Consume the implicit_const SLEB128 in both abbreviation-walk loops so the
(attribute, form) walk stays aligned, matching how
DWARFAbbreviationDeclaration::extract parses the same data.
Adds a regression test that hangs before this change and passes after it.
AI assisted with: claude
>From 803f47385f3bcc8380d269992ee84819b9787c4c Mon Sep 17 00:00:00 2001
From: David Young <davidayoung at meta.com>
Date: Wed, 24 Jun 2026 06:58:29 -0700
Subject: [PATCH] [llvm-dwp] Fix infinite loop on DWARFv5
DW_FORM_implicit_const
---
llvm/lib/DWP/DWP.cpp | 28 +++++++--
llvm/test/tools/llvm-dwp/X86/implicit_const.s | 58 +++++++++++++++++++
2 files changed, 80 insertions(+), 6 deletions(-)
create mode 100644 llvm/test/tools/llvm-dwp/X86/implicit_const.s
diff --git a/llvm/lib/DWP/DWP.cpp b/llvm/lib/DWP/DWP.cpp
index db0d33cd5a52d..779c9ea7f371c 100644
--- a/llvm/lib/DWP/DWP.cpp
+++ b/llvm/lib/DWP/DWP.cpp
@@ -48,8 +48,18 @@ static uint64_t getCUAbbrev(StringRef Abbrev, uint64_t AbbrCode) {
// DW_CHILDREN
AbbrevData.getU8(&Offset);
// Attributes
- while (AbbrevData.getULEB128(&Offset) | AbbrevData.getULEB128(&Offset))
- ;
+ while (true) {
+ uint64_t Name = AbbrevData.getULEB128(&Offset);
+ dwarf::Form Form =
+ static_cast<dwarf::Form>(AbbrevData.getULEB128(&Offset));
+ // DW_FORM_implicit_const carries its value as an SLEB128 in the
+ // abbreviation declaration itself; failing to consume it desyncs the
+ // rest of the abbreviation table walk.
+ if (Form == dwarf::DW_FORM_implicit_const)
+ AbbrevData.getSLEB128(&Offset);
+ if (Name == 0 && Form == 0)
+ break;
+ }
}
return Offset;
}
@@ -115,10 +125,16 @@ getCUIdentifiers(InfoSectionUnitHeader &Header, StringRef Abbrev,
AbbrevData.getU8(&AbbrevOffset);
uint32_t Name;
dwarf::Form Form;
- while ((Name = AbbrevData.getULEB128(&AbbrevOffset)) |
- (Form = static_cast<dwarf::Form>(
- AbbrevData.getULEB128(&AbbrevOffset))) &&
- (Name != 0 || Form != 0)) {
+ for (;;) {
+ Name = AbbrevData.getULEB128(&AbbrevOffset);
+ Form = static_cast<dwarf::Form>(AbbrevData.getULEB128(&AbbrevOffset));
+ // DW_FORM_implicit_const stores its value as an SLEB128 in the abbreviation
+ // declaration, not in .debug_info; consume it so the (attribute, form) walk
+ // stays aligned.
+ if (Form == dwarf::DW_FORM_implicit_const)
+ AbbrevData.getSLEB128(&AbbrevOffset);
+ if (Name == 0 && Form == 0)
+ break;
switch (Name) {
case dwarf::DW_AT_name: {
Expected<const char *> EName = getIndexedString(
diff --git a/llvm/test/tools/llvm-dwp/X86/implicit_const.s b/llvm/test/tools/llvm-dwp/X86/implicit_const.s
new file mode 100644
index 0000000000000..15d9569c7c671
--- /dev/null
+++ b/llvm/test/tools/llvm-dwp/X86/implicit_const.s
@@ -0,0 +1,58 @@
+# Check that llvm-dwp correctly handles a DWARFv5 .dwo whose abbreviation table
+# uses DW_FORM_implicit_const. DW_FORM_implicit_const stores its value as an
+# SLEB128 in the abbreviation declaration itself (not in .debug_info). If
+# llvm-dwp fails to consume that value while scanning the abbreviation table, the
+# (attribute, form) walk desyncs; here the desync makes the scan for the
+# compile-unit's abbreviation code run off the end of .debug_abbrev and spin
+# forever. This is a regression test: before the fix llvm-dwp hangs, after the
+# fix it terminates and emits the expected CU identifiers.
+
+# RUN: llvm-mc --triple=x86_64-unknown-linux --filetype=obj --split-dwarf-file=%t.dwo -dwarf-version=5 %s -o %t.o
+# RUN: llvm-dwp %t.dwo -o %t.dwp
+# RUN: llvm-dwarfdump -v %t.dwp | FileCheck %s
+
+# CHECK: .debug_info.dwo contents:
+# CHECK: 0x00000000: Compile Unit: {{.*}} version = 0x0005, unit_type = DW_UT_split_compile, {{.*}} DWO_id = [[DWOID:0x[0-9a-f]+]]
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_name{{.*}}"test.c"
+# CHECK: DW_AT_dwo_name{{.*}}"test.dwo"
+
+# CHECK: .debug_cu_index contents:
+# CHECK: version = 5, units = 1, slots = 2
+# CHECK: [[DWOID]] [0x0000000000000000
+
+ .section .debug_info.dwo,"e", at progbits
+ .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit
+.Ldebug_info_dwo_start0:
+ .short 5 # DWARF version number
+ .byte 5 # DWARF Unit Type (DW_UT_split_compile)
+ .byte 8 # Address Size (in bytes)
+ .long 0 # Offset Into Abbrev. Section
+ .quad 0x1100001122334455 # DWO_id
+ .byte 2 # Abbrev [2] DW_TAG_compile_unit
+ .asciz "test.c" # DW_AT_name
+ .asciz "test.dwo" # DW_AT_dwo_name
+.Ldebug_info_dwo_end0:
+
+ .section .debug_abbrev.dwo,"e", at progbits
+ # Abbrev [1] DW_TAG_variable, declared *before* the compile unit and using
+ # DW_FORM_implicit_const. This is the declaration that desyncs the walk.
+ .byte 1 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 28 # DW_AT_const_value
+ .byte 33 # DW_FORM_implicit_const
+ .byte 42 # implicit const value (SLEB128)
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ # Abbrev [2] DW_TAG_compile_unit
+ .byte 2 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 8 # DW_FORM_string
+ .byte 118 # DW_AT_dwo_name
+ .byte 8 # DW_FORM_string
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
More information about the llvm-commits
mailing list