可能出现空指针漏洞的地方:
在读取属性时,info_ptr可能会超过info_ptr_end而变为NULL,在read_attribute函数中需要判断info_ptr是否为空指针。
在读取属性时,如果读到DW_AT_abstract_origin或DW_AT_specification属性,并且在find_abstract_instance函数中没有找到对应的DIE,则func->name、func->is_linkage、func->file和func->line这些指针都不会被赋值,这可能导致后续访问这些指针时出现空指针异常。
修改代码如下:
static bfd_boolean scan_unit_for_symbols (struct comp_unit *unit) { bfd *abfd = unit->abfd; bfd_byte *info_ptr = unit->first_child_die_ptr; bfd_byte *info_ptr_end = unit->stash->info_ptr_end; int nesting_level = 0; struct nest_funcinfo {
struct funcinfo *func;
} *nested_funcs; int nested_funcs_size;
/* Maintain a stack of in-scope functions and inlined functions, which we
can use to set the caller_func field. */
nested_funcs_size = 32; nested_funcs = (struct nest_funcinfo *)
bfd_malloc (nested_funcs_size * sizeof (*nested_funcs));
if (nested_funcs == NULL)
return FALSE;
nested_funcs[nesting_level].func = 0;
while (nesting_level >= 0)
{
unsigned int abbrev_number, bytes_read, i;
struct abbrev_info *abbrev;
struct attribute attr;
struct funcinfo *func = NULL;
struct varinfo *var = NULL;
bfd_vma low_pc = 0;
bfd_vma high_pc = 0;
bfd_boolean high_pc_relative = FALSE;
/* PR 17512: file: 9f405d9d. */
if (info_ptr >= info_ptr_end)
goto fail;
abbrev_number = _bfd_safe_read_leb128 (abfd, info_ptr, &bytes_read,
FALSE, info_ptr_end);
info_ptr += bytes_read;
if (! abbrev_number)
{
nesting_level--;
continue;
}
abbrev = lookup_abbrev (abbrev_number, unit->abbrevs);
if (! abbrev)
{
static unsigned int previous_failed_abbrev = -1U;
/* Avoid multiple reports of the same missing abbrev. */
if (abbrev_number != previous_failed_abbrev)
{
_bfd_error_handler
(_("DWARF error: could not find abbrev number %u"),
abbrev_number);
previous_failed_abbrev = abbrev_number;
}
bfd_set_error (bfd_error_bad_value);
goto fail;
}
if (abbrev->tag == DW_TAG_subprogram
|| abbrev->tag == DW_TAG_entry_point
|| abbrev->tag == DW_TAG_inlined_subroutine)
{
bfd_size_type amt = sizeof (struct funcinfo);
func = (struct funcinfo *) bfd_zalloc (abfd, amt);
if (func == NULL)
goto fail;
func->tag = abbrev->tag;
func->prev_func = unit->function_table;
unit->function_table = func;
unit->number_of_functions++;
BFD_ASSERT (!unit->cached);
if (func->tag == DW_TAG_inlined_subroutine)
for (i = nesting_level; i-- != 0; )
if (nested_funcs[i].func)
{
func->caller_func = nested_funcs[i].func;
break;
}
nested_funcs[nesting_level].func = func;
}
else if (abbrev->tag == DW_TAG_variable)
{
bfd_size_type amt = sizeof (struct varinfo);
var = (struct varinfo *) bfd_zalloc (abfd, amt);
if (var == NULL)
goto fail;
var->tag = abbrev->tag;
var->stack = 1;
var->prev_var = unit->variable_table;
unit->variable_table = var;
}
for (i = 0; i < abbrev->num_attrs; ++i)
{
info_ptr = read_attribute (&attr, &abbrev->attrs[i],
unit, info_ptr, info_ptr_end);
if (info_ptr == NULL)
goto fail;
if (func)
{
switch (attr.name)
{
case DW_AT_call_file:
func->caller_file = concat_filename (unit->line_table,
attr.u.val);
break;
case DW_AT_call_line:
func->caller_line = attr.u.val;
break;
case DW_AT_abstract_origin:
case DW_AT_specification:
if (!find_abstract_instance (unit, info_ptr, &attr,
&func->name,
&func->is_linkage,
&func->file,
&func->line))
{
/* If find_abstract_instance failed, we need to free
the memory allocated for func. */
bfd_free(func);
func = NULL;
goto fail;
}
break;
case DW_AT_name:
/* Prefer DW_AT_MIPS_linkage_name or DW_AT_linkage_name
over DW_AT_name. */
if (func->name == NULL && is_str_attr (attr.form))
{
func->name = attr.u.str;
if (non_mangled (unit->lang))
func->is_linkage = TRUE;
}
break;
case DW_AT_linkage_name:
case DW_AT_MIPS_linkage_name:
/* PR 16949: Corrupt debug info can place
non-string forms into these attributes. */
if (is_str_attr (attr.form))
{
func->name = attr.u.str;
func->is_linkage = TRUE;
}
break;
case DW_AT_low_pc:
low_pc = attr.u.val;
break;
case DW_AT_high_pc:
high_pc = attr.u.val;
high_pc_relative = attr.form != DW_FORM_addr;
break;
case DW_AT_ranges:
if (!read_rangelist (unit, &func->arange, attr.u.val))
{
bfd_free(func);
func = NULL;
goto fail;
}
break;
case DW_AT_decl_file:
func->file = concat_filename (unit->line_table,
attr.u.val);
break;
case DW_AT_decl_line:
func->line = attr.u.val;
break;
default:
break;
}
}
else if (var)
{
switch (attr.name)
{
case DW_AT_name:
if (is_str_attr (attr.form))
var->name = attr.u.str;
break;
case DW_AT_decl_file:
var->file = concat_filename (unit->line_table,
attr.u.val);
break;
case DW_AT_decl_line:
var->line = attr.u.val;
break;
case DW_AT_external:
if (attr.u.val != 0)
var->stack = 0;
break;
case DW_AT_location:
switch (attr.form)
{
case DW_FORM_block:
case DW_FORM_block1:
case DW_FORM_block2:
case DW_FORM_block4:
case DW_FORM_exprloc:
if (attr.u.blk->data != NULL
&& *attr.u.blk->data == DW_OP_addr)
{
var->stack = 0;
/* Verify that DW_OP_addr is the only opcode in the
location, in which case the block size will be 1
plus the address size. */
/* ??? For TLS variables, gcc can emit
DW_OP_addr <addr> DW_OP_GNU_push_tls_address
which we don't handle here yet. */
if (attr.u.blk->size == unit->addr_size + 1U)
var->addr = bfd_get (unit->addr_size * 8,
unit->abfd,
attr.u.blk->data + 1);
}
break;
default:
break;
}
break;
default:
break;
}
}
}
if (high_pc_relative)
high_pc += low_pc;
if (func && high_pc != 0)
{
if (!arange_add (unit, &func->arange, low_pc, high_pc))
{
bfd_free(func);
func = NULL;
goto fail;
}
}
if (abbrev->has_children)
{
nesting_level++;
if (nesting_level >= nested_funcs_size)
{
struct nest_funcinfo *tmp;
nested_funcs_size *= 2;
tmp = (struct nest_funcinfo *)
bfd_realloc (nested_funcs,
nested_funcs_size * sizeof (*nested_funcs));
if (tmp == NULL)
{
bfd_free(func);
func = NULL;
goto fail;
}
nested_funcs = tmp;
}
nested_funcs[nesting_level].func = 0;
}
}
free (nested_funcs); return TRUE;
fail: free (nested_funcs); return FALSE; }
修改的地方:
在读取属性时,如果info_ptr为空指针,则跳转到fail标签。
在读取DW_AT_abstract_origin或DW_AT_specification属性后,如果find_abstract_instance函数返回FALSE,则需要释放已经分配的func内存,并跳转到fail标签。