From 330c71b5b59906c1336df9f2de19257c20f749ab Mon Sep 17 00:00:00 2001 From: Omar Sandoval Date: Wed, 21 Sep 2022 12:29:45 -0700 Subject: [PATCH] libdrgn: dwarf_info: fix segfault on anonymous DIEs during scope search Jakub Kicinski reported that prog.crashed_thread().stack_trace()[1]['does not exist'] segfaulted on a vmcore he encountered. The segfault was a NULL pointer dereference of dwarf_diename() of a DW_TAG_subprogram DIE in drgn_find_in_dwarf_scopes(). The fix is to ignore DIEs without a name. I was curious what this anonymous DW_TAG_subprogram was. It turned out to be some dubious DWARF generated by Clang when a local variable is defined via a macro. One such example comes from the following code in arch/x86/events/intel/uncore.h: static inline bool uncore_mmio_is_valid_offset(struct intel_uncore_box *box, unsigned long offset) { if (offset < box->pmu->type->mmio_map_size) return true; pr_warn_once("perf uncore: Invalid offset 0x%lx exceeds mapped area of %s.\n", offset, box->pmu->type->name); return false; } pr_warn_once() expands to: #define pr_warn_once(fmt, ...) \ printk_once(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) #define printk_once(fmt, ...) \ ({ \ static bool __section(".data.once") __print_once; \ bool __ret_print_once = !__print_once; \ \ if (!__print_once) { \ __print_once = true; \ printk(fmt, ##__VA_ARGS__); \ } \ unlikely(__ret_print_once); \ }) For some reason, Clang generates an anonymous, top-level DW_TAG_subprogram DIE to contain the __print_once variable: <1><1cf86e>: Abbrev Number: 62 (DW_TAG_subprogram) <2><1cf86f>: Abbrev Number: 61 (DW_TAG_variable) <1cf870> DW_AT_name : (indirect string, offset: 0x34fb2e): __print_once <1cf874> DW_AT_type : <0x1c574c> <1cf878> DW_AT_decl_file : 1 <1cf879> DW_AT_decl_line : 229 <1cf87a> DW_AT_location : 16 byte block: 3 2c 84 66 83 ff ff ff ff 94 1 31 1e 30 22 9f (DW_OP_addr: ffffffff8366842c; DW_OP_deref_size: 1; DW_OP_lit1; DW_OP_mul; DW_OP_lit0; DW_OP_plus; DW_OP_stack_value) Whereas GCC puts it in a DW_TAG_lexical block DIE inside of the DW_TAG_subprogram DIE for uncore_mmio_is_valid_offset(): <1><3110b2>: Abbrev Number: 45 (DW_TAG_subprogram) <3110b3> DW_AT_name : (indirect string, offset: 0x2e13e): uncore_mmio_is_valid_offset <3110b7> DW_AT_decl_file : 4 <3110b8> DW_AT_decl_line : 223 <3110b9> DW_AT_decl_column : 20 <3110ba> DW_AT_prototyped : 1 <3110ba> DW_AT_type : <0x2f416b> <3110be> DW_AT_inline : 3 (declared as inline and inlined) <3110bf> DW_AT_sibling : <0x311142> <2><3110ef>: Abbrev Number: 66 (DW_TAG_lexical_block) <3><3110f0>: Abbrev Number: 120 (DW_TAG_variable) <3110f1> DW_AT_name : (indirect string, offset: 0x2da3f): __print_once <3110f5> DW_AT_decl_file : 4 <3110f6> DW_AT_decl_line : 229 <3110f7> DW_AT_decl_column : 2 <3110f8> DW_AT_type : <0x2f416b> <3110fc> DW_AT_location : 9 byte block: 3 2c 28 48 83 ff ff ff ff (DW_OP_addr: ffffffff8348282c) Regardless, we shouldn't crash on this input. Reported-by: Jakub Kicinski Signed-off-by: Omar Sandoval --- libdrgn/dwarf_info.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libdrgn/dwarf_info.c b/libdrgn/dwarf_info.c index 67c52c039..976a43d6b 100644 --- a/libdrgn/dwarf_info.c +++ b/libdrgn/dwarf_info.c @@ -5373,8 +5373,9 @@ struct drgn_error *drgn_find_in_dwarf_scopes(Dwarf_Die *scopes, switch (dwarf_tag(&die)) { case DW_TAG_variable: case DW_TAG_formal_parameter: - case DW_TAG_subprogram: - if (strcmp(dwarf_diename(&die), name) == 0) { + case DW_TAG_subprogram: { + const char *die_name = dwarf_diename(&die); + if (die_name && strcmp(die_name, name) == 0) { *die_ret = die; bool declaration; if (dwarf_flag(&die, DW_AT_declaration, @@ -5386,6 +5387,7 @@ struct drgn_error *drgn_find_in_dwarf_scopes(Dwarf_Die *scopes, return NULL; } break; + } case DW_TAG_enumeration_type: { bool enum_class; if (dwarf_flag_integrate(&die, DW_AT_enum_class,