[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