afl-fuzz pointed out that __libdw_intern_expression didn't handle CFI containing DW_OP_call_ref, DW_OP_implicit_pointer or DW_OP_addr. Because in that case the Dwarf dbg is NULL. Both DW_OP_call_ref and DW_OP_implicit_pointer cannot be used in CFI. That is just an error. But DW_OP_addr can be. Without a Dwarf dbg we'll need to read the address argument directly. Don't use __libdw_read_address_inc which might do a relocation of the value read. But in practice the relocation hook isn't implemented anyway. Signed-off-by: Mark Wielaard --- libdw/ChangeLog | 6 ++++++ libdw/dwarf_getlocation.c | 42 ++++++++++++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 0e7e7a3..dd11755 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,9 @@ +2015-01-02 Mark Wielaard + + * dwarf_getlocation.c (__libdw_intern_expression): Check dbg is not + NULL for DW_OP_call_ref and DW_OP_GNU_implicit_pointer. For + DW_OP_addr if dbg is NULL then read argument directly. + 2014-12-24 Mark Wielaard * dwarf_getsrc_die.c (dwarf_getsrc_die): Return the last line record diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index 068f385..a3a5bd4 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -1,5 +1,5 @@ /* Return location expression list. - Copyright (C) 2000-2010, 2013, 2014 Red Hat, Inc. + Copyright (C) 2000-2010, 2013-2015 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper , 2000. @@ -270,16 +270,36 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, { case DW_OP_addr: /* Address, depends on address size of CU. */ - if (__libdw_read_address_inc (dbg, sec_index, &data, - address_size, &newloc->number)) - return -1; + if (dbg == NULL) + { + // XXX relocation? + if (address_size == 4) + { + if (unlikely (data + 4 > end_data)) + goto invalid; + else + newloc->number = read_4ubyte_unaligned_inc (&bo, data); + } + else + { + if (unlikely (data + 8 > end_data)) + goto invalid; + else + newloc->number = read_8ubyte_unaligned_inc (&bo, data); + } + } + else if (__libdw_read_address_inc (dbg, sec_index, &data, + address_size, &newloc->number)) + goto invalid; break; case DW_OP_call_ref: /* DW_FORM_ref_addr, depends on offset size of CU. */ - if (__libdw_read_offset_inc (dbg, sec_index, &data, ref_size, - &newloc->number, IDX_debug_info, 0)) - return -1; + if (dbg == NULL || __libdw_read_offset_inc (dbg, sec_index, &data, + ref_size, + &newloc->number, + IDX_debug_info, 0)) + goto invalid; break; case DW_OP_deref: @@ -435,9 +455,11 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order, case DW_OP_GNU_implicit_pointer: /* DW_FORM_ref_addr, depends on offset size of CU. */ - if (__libdw_read_offset_inc (dbg, sec_index, &data, ref_size, - &newloc->number, IDX_debug_info, 0)) - return -1; + if (dbg == NULL || __libdw_read_offset_inc (dbg, sec_index, &data, + ref_size, + &newloc->number, + IDX_debug_info, 0)) + goto invalid; if (unlikely (data >= end_data)) goto invalid; get_uleb128 (newloc->number2, data, end_data); /* Byte offset. */ -- 2.1.0