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 <kuba@kernel.org>
Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2022-09-21 12:29:45 -07:00
parent 0825dda116
commit 330c71b5b5

View File

@ -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,