[llvm] [BOLT] Fix null pointer dereference in DWP processing with split DWARF (PR #191474)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 10 10:46:41 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-bolt
Author: Farid Zakaria (fzakaria)
<details>
<summary>Changes</summary>
Fix two null pointer dereferences in BOLT's DWP processing path that cause SIGSEGV in worker threads when -update-debug-sections is used with a co-located .dwp file.
1. getSliceData() in updateDebugData() dereferences the result of getContribution() without checking for null. getContribution() returns nullptr when the requested section kind (e.g. DW_SECT_LINE) is not present as a column in the DWP CU index. When BOLT processes a DWP where certain section kinds are absent from the index, every worker thread that hits this path crashes simultaneously.
2. processSplitCU() dereferences getUnitDIEbyUnit() without checking for null. If buildDWOUnit() fails for a CU, the returned DIE* is null and the dereference crashes.
Crash signature from dmesg:
```
llvm-worker-*: segfault at 8 ip <offset> error 4 in llvm-bolt
(multiple worker threads crash at the same instruction)
```
The faulting address 0x8 corresponds to accessing the Length field (offset 8) of a null `DWARFUnitIndex::Entry::SectionContribution*`.
At Meta, I reproduced this building hhvm with a co-located .dwp file and the flags `update-debug-sections -debug-thread-count=80 -lite=0` with profile data.
I confirmed that the unfixed BOLT crashes deterministically whereas the fixed BOLT completes successfully.
---
Patch is 20.25 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/191474.diff
2 Files Affected:
- (modified) bolt/lib/Rewrite/DWARFRewriter.cpp (+9-2)
- (added) bolt/test/X86/dwarf4-dwp-missing-line-contribution.s (+427)
``````````diff
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index 816acb229fec5..c0d1b0442bad3 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -665,8 +665,13 @@ void DWARFRewriter::updateDebugInfo() {
updateUnitDebugInfo(SplitCU, DWODIEBuilder, DebugLocDWoWriter,
TempRangesSectionWriter, AddressWriter);
- DebugLocDWoWriter.finalize(DWODIEBuilder,
- *DWODIEBuilder.getUnitDIEbyUnit(SplitCU));
+ DIE *UnitDIE = DWODIEBuilder.getUnitDIEbyUnit(SplitCU);
+ if (!UnitDIE) {
+ errs() << "BOLT-WARNING: failed to construct DIE for split CU "
+ << Twine::utohexstr(*Unit.getDWOId()) << "\n";
+ return;
+ }
+ DebugLocDWoWriter.finalize(DWODIEBuilder, *UnitDIE);
if (Unit.getVersion() >= 5)
TempRangesSectionWriter.finalizeSection();
@@ -1811,6 +1816,8 @@ std::optional<StringRef> updateDebugData(
uint64_t &DWPOffset) -> StringRef {
if (DWOEntry) {
DWOSectionContribution *DWOContrubution = DWOEntry->getContribution(Sec);
+ if (!DWOContrubution)
+ return OutData;
DWPOffset = DWOContrubution->getOffset();
OutData = OutData.substr(DWPOffset, DWOContrubution->getLength());
}
diff --git a/bolt/test/X86/dwarf4-dwp-missing-line-contribution.s b/bolt/test/X86/dwarf4-dwp-missing-line-contribution.s
new file mode 100644
index 0000000000000..0c99c9d7fbb31
--- /dev/null
+++ b/bolt/test/X86/dwarf4-dwp-missing-line-contribution.s
@@ -0,0 +1,427 @@
+## Test that BOLT does not crash when a DWP file contains a section (e.g.
+## .debug_line.dwo) whose kind is not tracked as a column in the CU index.
+## This triggers a null pointer dereference in getSliceData() where
+## getContribution() returns nullptr because the section kind has no column.
+#
+## We reproduce it by taking a normal DWARF4 split-dwarf DWP (whose CU index
+## has no LINE column) and injecting a .debug_line.dwo section via objcopy.
+## BOLT iterates the DWP's sections, finds .debug_line.dwo, and calls
+## getContribution(DW_SECT_LINE) which returns nullptr.
+
+# REQUIRES: system-linux
+
+# RUN: rm -rf %t && mkdir -p %t && cd %t
+# RUN: split-file %s %t
+# RUN: %clangxx %cxxflags -g -gdwarf-4 -gsplit-dwarf %t/main.s %t/callee.s -o %t/main.exe
+# RUN: llvm-dwp -e %t/main.exe -o %t/main.exe.dwp
+
+## Inject a .debug_line.dwo section into the DWP after index creation.
+## The CU index will NOT have a LINE column, but the file will have the section.
+# RUN: printf '\x04\x00\x00\x00\x02\x00\x00\x00\x00\x00\x01\x01' > %t/line.bin
+# RUN: llvm-objcopy --add-section=.debug_line.dwo=%t/line.bin %t/main.exe.dwp
+
+## Verify the mismatch: DWP has .debug_line.dwo but CU index has no LINE column.
+# RUN: llvm-dwarfdump --debug-cu-index %t/main.exe.dwp | FileCheck %s --check-prefix=INDEX
+# INDEX-NOT: LINE
+
+# RUN: llvm-bolt %t/main.exe -o %t/main.exe.bolt -update-debug-sections 2>&1 | FileCheck %s
+
+# CHECK-NOT: Assertion
+# CHECK-NOT: PLEASE submit a bug report
+
+#--- main.s
+ .file "main.cpp"
+ .globl main # -- Begin function main
+ .type main, at function
+main:
+.Lfunc_begin0:
+ .file 1 "." "main.cpp"
+ .loc 1 2 0 # main.cpp:2:0
+ .loc 1 2 21 prologue_end # main.cpp:2:21
+ .loc 1 2 14 epilogue_begin is_stmt 0 # main.cpp:2:14
+ retq
+.Lfunc_end0:
+ .size main, .Lfunc_end0-main
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 0 # DW_CHILDREN_no
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 14 # DW_FORM_strp
+ .ascii "\264B" # DW_AT_GNU_pubnames
+ .byte 25 # DW_FORM_flag_present
+ .ascii "\260B" # DW_AT_GNU_dwo_name
+ .byte 14 # DW_FORM_strp
+ .ascii "\261B" # DW_AT_GNU_dwo_id
+ .byte 7 # DW_FORM_data8
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .ascii "\263B" # DW_AT_GNU_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x25 DW_TAG_compile_unit
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .long .Lskel_string0 # DW_AT_comp_dir
+ # DW_AT_GNU_pubnames
+ .long .Lskel_string1 # DW_AT_GNU_dwo_name
+ .quad 1465063543908291764 # DW_AT_GNU_dwo_id
+ .quad .Lfunc_begin0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_GNU_addr_base
+.Ldebug_info_end0:
+ .section .debug_str,"MS", at progbits,1
+.Lskel_string0:
+ .asciz "." # string offset=0
+.Lskel_string1:
+ .asciz "main.exe-main.dwo" # string offset=2
+ .section .debug_str.dwo,"eMS", at progbits,1
+.Linfo_string0:
+ .asciz "main" # string offset=0
+.Linfo_string1:
+ .asciz "int" # string offset=5
+.Linfo_string2:
+ .byte 0 # string offset=9
+.Linfo_string3:
+ .asciz "main.cpp" # string offset=10
+.Linfo_string4:
+ .asciz "main.exe-main.dwo" # string offset=19
+ .section .debug_str_offsets.dwo,"e", at progbits
+ .long 0
+ .long 5
+ .long 9
+ .long 10
+ .long 19
+ .section .debug_info.dwo,"e", at progbits
+ .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0 # Length of Unit
+.Ldebug_info_dwo_start0:
+ .short 4 # DWARF version number
+ .long 0 # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x22 DW_TAG_compile_unit
+ .byte 2 # DW_AT_producer
+ .short 33 # DW_AT_language
+ .byte 3 # DW_AT_name
+ .byte 4 # DW_AT_GNU_dwo_name
+ .quad 1465063543908291764 # DW_AT_GNU_dwo_id
+ .byte 2 # Abbrev [2] 0x19:0xf DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .byte 0 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 40 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x28:0x4 DW_TAG_base_type
+ .byte 1 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_dwo_end0:
+ .section .debug_abbrev.dwo,"e", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .ascii "\260B" # DW_AT_GNU_dwo_name
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .ascii "\261B" # DW_AT_GNU_dwo_id
+ .byte 7 # DW_FORM_data8
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 0 # DW_CHILDREN_no
+ .byte 17 # DW_AT_low_pc
+ .ascii "\201>" # DW_FORM_GNU_addr_index
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_addr,"", at progbits
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .section .debug_gnu_pubnames,"", at progbits
+ .long .LpubNames_end0-.LpubNames_start0
+.LpubNames_start0:
+ .short 2
+ .long .Lcu_begin0
+ .long 48
+ .long 25
+ .byte 48
+ .asciz "main"
+ .long 0
+.LpubNames_end0:
+ .section .debug_gnu_pubtypes,"", at progbits
+ .long .LpubTypes_end0-.LpubTypes_start0
+.LpubTypes_start0:
+ .short 2
+ .long .Lcu_begin0
+ .long 48
+ .long 40
+ .byte 144
+ .asciz "int"
+ .long 0
+.LpubTypes_end0:
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .addrsig_sym _Z6calleei
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
+#--- callee.s
+ .file "callee.cpp"
+ .globl _Z6calleei # -- Begin function _Z6calleei
+ .type _Z6calleei, at function
+_Z6calleei: # @_Z6calleei
+.Lfunc_begin0:
+ .file 1 "." "callee.cpp"
+ .loc 1 1 0 # callee.cpp:1:0
+ .loc 1 1 28 prologue_end # callee.cpp:1:28
+ .loc 1 1 21 epilogue_begin is_stmt 0 # callee.cpp:1:21
+ retq
+.Lfunc_end0:
+ .size _Z6calleei, .Lfunc_end0-_Z6calleei
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 0 # DW_CHILDREN_no
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 14 # DW_FORM_strp
+ .ascii "\264B" # DW_AT_GNU_pubnames
+ .byte 25 # DW_FORM_flag_present
+ .ascii "\260B" # DW_AT_GNU_dwo_name
+ .byte 14 # DW_FORM_strp
+ .ascii "\261B" # DW_AT_GNU_dwo_id
+ .byte 7 # DW_FORM_data8
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .ascii "\263B" # DW_AT_GNU_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0
+.Ldebug_info_start0:
+ .short 4
+ .long .debug_abbrev
+ .byte 8
+ .byte 1 # Abbrev [1] 0xb:0x25 DW_TAG_compile_unit
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .long .Lskel_string0 # DW_AT_comp_dir
+ # DW_AT_GNU_pubnames
+ .long .Lskel_string1 # DW_AT_GNU_dwo_name
+ .quad -8413212350243343807 # DW_AT_GNU_dwo_id
+ .quad .Lfunc_begin0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_GNU_addr_base
+.Ldebug_info_end0:
+ .section .debug_str,"MS", at progbits,1
+.Lskel_string0:
+ .asciz "."
+.Lskel_string1:
+ .asciz "main.exe-callee.dwo"
+ .section .debug_str.dwo,"eMS", at progbits,1
+.Linfo_string0:
+ .asciz "_Z6calleei"
+.Linfo_string1:
+ .asciz "callee"
+.Linfo_string2:
+ .asciz "int"
+.Linfo_string3:
+ .asciz "x"
+.Linfo_string4:
+ .byte 0
+.Linfo_string5:
+ .asciz "callee.cpp"
+.Linfo_string6:
+ .asciz "main.exe-callee.dwo"
+ .section .debug_str_offsets.dwo,"e", at progbits
+ .long 0
+ .long 11
+ .long 18
+ .long 22
+ .long 24
+ .long 25
+ .long 36
+ .section .debug_info.dwo,"e", at progbits
+ .long .Ldebug_info_dwo_end0-.Ldebug_info_dwo_start0
+.Ldebug_info_dwo_start0:
+ .short 4
+ .long 0
+ .byte 8
+ .byte 1 # Abbrev [1] 0xb:0x2f DW_TAG_compile_unit
+ .byte 4 # DW_AT_producer
+ .short 33 # DW_AT_language
+ .byte 5 # DW_AT_name
+ .byte 6 # DW_AT_GNU_dwo_name
+ .quad -8413212350243343807 # DW_AT_GNU_dwo_id
+ .byte 2 # Abbrev [2] 0x19:0x1c DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ .byte 0 # DW_AT_linkage_name
+ .byte 1 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .long 53 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x29:0xb DW_TAG_formal_parameter
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .byte 3 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .long 53 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 4 # Abbrev [4] 0x35:0x4 DW_TAG_base_type
+ .byte 2 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_dwo_end0:
+ .section .debug_abbrev.dwo,"e", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .ascii "\260B" # DW_AT_GNU_dwo_name
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .ascii "\261B" # DW_AT_GNU_dwo_id
+ .byte 7 # DW_FORM_data8
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .ascii "\201>" # DW_FORM_GNU_addr_index
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 110 # DW_AT_linkage_name
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .byte 3 # DW_AT_name
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .ascii "\202>" # DW_FORM_GNU_str_index
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_addr,"", at progbits
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .section .debug_gnu_pubnames,"", at progbits
+ .long .LpubNames_end0-.LpubNames_start0
+.LpubNames_start0:
+ .short 2
+ .long .Lcu_begin0
+ .long 48
+ .long 25
+ .byte 48
+ .asciz "_Z6calleei"
+ .long 0
+.LpubNames_end0:
+ .section .debug_gnu_pubtypes,"", at progbits
+ .long .LpubTypes_end...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/191474
More information about the llvm-commits
mailing list