>From 41d33ebccb7573b6ce277c05db72eb6c4e0f0a41 Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Fri, 12 Dec 2014 16:43:04 +0100 Subject: [PATCH] libdw: Make sure all attributes come with a (fake) CU for bound checks. All attributes now have a reference to a (fake) CU that has startp and endp set to the data section where the form data comes from. Use that for bounds checking in __libdw_form_val_len and dwarf_formblock to make sure data read doesn't overflow any data section. Remove libdwP.h cu_data and use cu startp and endp directly where appropriate. Signed-off-by: Mark Wielaard --- libdw/ChangeLog | 28 +++++++++++++++++++++++++++ libdw/dwarf_begin_elf.c | 22 +++++++++++++++++++++ libdw/dwarf_child.c | 4 +--- libdw/dwarf_end.c | 3 +++ libdw/dwarf_formblock.c | 21 +++++++++++++------- libdw/dwarf_formref_die.c | 13 ++++++++----- libdw/dwarf_getattrs.c | 4 +--- libdw/dwarf_getlocation.c | 8 ++++++++ libdw/dwarf_getlocation_attr.c | 29 +++++++++++++++++++++++----- libdw/dwarf_getlocation_implicit_pointer.c | 8 +++++--- libdw/dwarf_getmacros.c | 5 +++-- libdw/libdwP.h | 31 ++++++++++++++---------------- libdw/libdw_findcu.c | 5 ++--- libdw/libdw_form.c | 12 ++++++------ 14 files changed, 139 insertions(+), 54 deletions(-) diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 922cc6b..6bb326d 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,31 @@ +2014-12-12 Mark Wielaard + + * libdwP.h (struct Dwarf): Add fake_loc_cu. + (cu_data): Removed. + (DIE_OFFSET_FROM_CU_OFFSET): Don't use cu_data, use cu_sec_idx. + (__libdw_form_val_compute_len): Drop dbg and endp arguments. + (__libdw_form_val_len): Likewise. + * libdw_form.c (__libdw_form_val_compute_len): Likewise. + * libdw_findcu.c (__libdw_intern_next_unit): Don't use cu_data, use + the already found data buffer directly. + * dwarf_begin_elf.c (valid_p): Setup fake_loc_cu. + * dwarf_end.c (dwarf_end): Free fake_loc_cu. + * dwarf_child.c (__libdw_find_attr): Call __libdw_form_val_len with + just cu. + * dwarf_getattrs.c (dwarf_getattrs): Likewise. + * dwarf_formblock.c (dwarf_formblock): Add bounds checking. + * dwarf_getlocation_attr.c (attr_form_cu): New function. + (dwarf_getlocation_attr): Use attr_form_cu to set result->cu. + (getlocation): Handle empty blocks immediately. + * dwarf_getlocation_implicit_pointer.c (empty_cu): New static var. + (__libdw_empty_loc_attr): Drop cu argument, use empty_cu. + (dwarf_getlocation_implicit_pointer): Call __libdw_empty_loc_attr with + one argument. + * dwarf_getmacros.c (read_macros): Also setup startp and endp for + fake_cu. Call __libdw_form_val_len with just fake_cu. + * dwarf_formref_die.c (dwarf_formref_die): Don't use cu_data, get + datap and size directly from cu startp and endp. + 2014-12-13 Mark Wielaard * dwarf_getaranges.c (compare_aranges): Make sure Dwarf_Addr diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index 4c6346a..4c49ce2 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -235,6 +235,28 @@ valid_p (Dwarf *result) result = NULL; } + if (result != NULL && result->sectiondata[IDX_debug_loc] != NULL) + { + result->fake_loc_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU)); + if (unlikely (result->fake_loc_cu == NULL)) + { + __libdw_free_zdata (result); + Dwarf_Sig8_Hash_free (&result->sig8_hash); + __libdw_seterrno (DWARF_E_NOMEM); + free (result); + result = NULL; + } + else + { + result->fake_loc_cu->dbg = result; + result->fake_loc_cu->startp + = result->sectiondata[IDX_debug_loc]->d_buf; + result->fake_loc_cu->endp + = (result->sectiondata[IDX_debug_loc]->d_buf + + result->sectiondata[IDX_debug_loc]->d_size); + } + } + return result; } diff --git a/libdw/dwarf_child.c b/libdw/dwarf_child.c index 2a5d379..96d8e23 100644 --- a/libdw/dwarf_child.c +++ b/libdw/dwarf_child.c @@ -94,9 +94,7 @@ __libdw_find_attr (Dwarf_Die *die, unsigned int search_name, /* Skip over the rest of this attribute (if there is any). */ if (attr_form != 0) { - size_t len = __libdw_form_val_len (dbg, die->cu, attr_form, readp, - endp); - + size_t len = __libdw_form_val_len (die->cu, attr_form, readp); if (unlikely (len == (size_t) -1l)) { readp = NULL; diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index 647a1b8..922dc8f 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -117,6 +117,9 @@ dwarf_end (dwarf) if (dwarf->free_elf) elf_end (dwarf->elf); + /* Free the fake location list CU. */ + free (dwarf->fake_loc_cu); + /* Free the context descriptor. */ free (dwarf); } diff --git a/libdw/dwarf_formblock.c b/libdw/dwarf_formblock.c index 799d877..980bc10 100644 --- a/libdw/dwarf_formblock.c +++ b/libdw/dwarf_formblock.c @@ -1,5 +1,5 @@ /* Return block represented by attribute. - Copyright (C) 2004-2010 Red Hat, Inc. + Copyright (C) 2004-2010, 2014 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper , 2004. @@ -43,28 +43,37 @@ dwarf_formblock (attr, return_block) if (attr == NULL) return -1; - const unsigned char *datap; + const unsigned char *datap = attr->valp; + const unsigned char *endp = attr->cu->endp; switch (attr->form) { case DW_FORM_block1: + if (unlikely (endp - datap < 1)) + goto invalid; return_block->length = *(uint8_t *) attr->valp; return_block->data = attr->valp + 1; break; case DW_FORM_block2: + if (unlikely (endp - datap < 2)) + goto invalid; return_block->length = read_2ubyte_unaligned (attr->cu->dbg, attr->valp); return_block->data = attr->valp + 2; break; case DW_FORM_block4: + if (unlikely (endp - datap < 4)) + goto invalid; return_block->length = read_4ubyte_unaligned (attr->cu->dbg, attr->valp); return_block->data = attr->valp + 4; break; case DW_FORM_block: case DW_FORM_exprloc: - datap = attr->valp; + if (unlikely (endp - datap < 1)) + goto invalid; + // XXX bounds check get_uleb128 (return_block->length, datap); return_block->data = (unsigned char *) datap; break; @@ -74,12 +83,10 @@ dwarf_formblock (attr, return_block) return -1; } - if (unlikely (cu_data (attr->cu)->d_size - - (return_block->data - - (unsigned char *) cu_data (attr->cu)->d_buf) - < return_block->length)) + if (unlikely (return_block->length > (size_t) (endp - return_block->data))) { /* Block does not fit. */ + invalid: __libdw_seterrno (DWARF_E_INVALID_DWARF); return -1; } diff --git a/libdw/dwarf_formref_die.c b/libdw/dwarf_formref_die.c index b54e216..63f6697 100644 --- a/libdw/dwarf_formref_die.c +++ b/libdw/dwarf_formref_die.c @@ -70,7 +70,8 @@ dwarf_formref_die (attr, result) return INTUSE(dwarf_offdie) (dbg_ret, offset, result); } - Elf_Data *data; + const unsigned char *datap; + size_t size; if (attr->form == DW_FORM_ref_sig8) { /* This doesn't have an offset, but instead a value we @@ -92,7 +93,8 @@ dwarf_formref_die (attr, result) } while (cu->type_sig8 != sig); - data = cu->dbg->sectiondata[IDX_debug_types]; + datap = cu->dbg->sectiondata[IDX_debug_types]->d_buf; + size = cu->dbg->sectiondata[IDX_debug_types]->d_size; offset = cu->type_offset; } else @@ -101,17 +103,18 @@ dwarf_formref_die (attr, result) if (unlikely (__libdw_formref (attr, &offset) != 0)) return NULL; - data = cu_data (cu); + datap = cu->startp; + size = cu->endp - cu->startp; } - if (unlikely (data->d_size - cu->start <= offset)) + if (unlikely (offset >= size)) { __libdw_seterrno (DWARF_E_INVALID_DWARF); return NULL; } memset (result, '\0', sizeof (Dwarf_Die)); - result->addr = (char *) data->d_buf + cu->start + offset; + result->addr = (char *) datap + offset; result->cu = cu; return result; } diff --git a/libdw/dwarf_getattrs.c b/libdw/dwarf_getattrs.c index 9ea70fc..0c54e5d 100644 --- a/libdw/dwarf_getattrs.c +++ b/libdw/dwarf_getattrs.c @@ -106,9 +106,7 @@ dwarf_getattrs (Dwarf_Die *die, int (*callback) (Dwarf_Attribute *, void *), /* Skip over the rest of this attribute (if there is any). */ if (attr.form != 0) { - size_t len = __libdw_form_val_len (dbg, die->cu, attr.form, - die_addr, endp); - + size_t len = __libdw_form_val_len (die->cu, attr.form, die_addr); if (unlikely (len == (size_t) -1l)) /* Something wrong with the file. */ return -1l; diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c index 2a4c890..38e93e6 100644 --- a/libdw/dwarf_getlocation.c +++ b/libdw/dwarf_getlocation.c @@ -555,6 +555,14 @@ static int getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block, Dwarf_Op **llbuf, size_t *listlen, int sec_index) { + /* Empty location expressions don't have any ops to intern. + Note that synthetic empty_cu doesn't have an associated DWARF dbg. */ + if (block->length == 0) + { + *listlen = 0; + return 0; + } + return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order, cu->address_size, (cu->version == 2 ? cu->address_size diff --git a/libdw/dwarf_getlocation_attr.c b/libdw/dwarf_getlocation_attr.c index cb29045..3229baf 100644 --- a/libdw/dwarf_getlocation_attr.c +++ b/libdw/dwarf_getlocation_attr.c @@ -1,5 +1,5 @@ /* Return DWARF attribute associated with a location expression op. - Copyright (C) 2013 Red Hat, Inc. + Copyright (C) 2013, 2014 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -33,6 +33,24 @@ #include #include +static Dwarf_CU * +attr_form_cu (Dwarf_Attribute *attr) +{ + /* If the attribute has block/expr form the data comes from the + .debug_info from the same cu as the attr. Otherwise it comes from + the .debug_loc data section. */ + switch (attr->form) + { + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + case DW_FORM_block: + case DW_FORM_exprloc: + return attr->cu; + default: + return attr->cu->dbg->fake_loc_cu; + } +} int dwarf_getlocation_attr (attr, op, result) @@ -43,26 +61,27 @@ dwarf_getlocation_attr (attr, op, result) if (attr == NULL) return -1; - result->cu = attr->cu; - switch (op->atom) { case DW_OP_implicit_value: result->code = DW_AT_const_value; result->form = DW_FORM_block; result->valp = (unsigned char *) (uintptr_t) op->number2; + result->cu = attr_form_cu (attr); break; case DW_OP_GNU_entry_value: result->code = DW_AT_location; result->form = DW_FORM_exprloc; result->valp = (unsigned char *) (uintptr_t) op->number2; + result->cu = attr_form_cu (attr); break; case DW_OP_GNU_const_type: result->code = DW_AT_const_value; result->form = DW_FORM_block1; result->valp = (unsigned char *) (uintptr_t) op->number2; + result->cu = attr_form_cu (attr); break; case DW_OP_call2: @@ -74,7 +93,7 @@ dwarf_getlocation_attr (attr, op, result) return -1; if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL) { - __libdw_empty_loc_attr (result, attr->cu); + __libdw_empty_loc_attr (result); return 0; } } @@ -88,7 +107,7 @@ dwarf_getlocation_attr (attr, op, result) if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL && INTUSE(dwarf_attr) (&die, DW_AT_const_value, result) == NULL) { - __libdw_empty_loc_attr (result, attr->cu); + __libdw_empty_loc_attr (result); return 0; } } diff --git a/libdw/dwarf_getlocation_implicit_pointer.c b/libdw/dwarf_getlocation_implicit_pointer.c index f93d17e..f1c16be 100644 --- a/libdw/dwarf_getlocation_implicit_pointer.c +++ b/libdw/dwarf_getlocation_implicit_pointer.c @@ -35,15 +35,17 @@ static unsigned char empty_exprloc = 0; +static Dwarf_CU empty_cu = { .startp = &empty_exprloc, + .endp = &empty_exprloc + 1 }; void internal_function -__libdw_empty_loc_attr (Dwarf_Attribute *attr, struct Dwarf_CU *cu ) +__libdw_empty_loc_attr (Dwarf_Attribute *attr) { attr->code = DW_AT_location; attr->form = DW_FORM_exprloc; attr->valp = &empty_exprloc; - attr->cu = cu; + attr->cu = &empty_cu; } int @@ -69,7 +71,7 @@ dwarf_getlocation_implicit_pointer (attr, op, result) if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL && INTUSE(dwarf_attr) (&die, DW_AT_const_value, result) == NULL) { - __libdw_empty_loc_attr (result, attr->cu); + __libdw_empty_loc_attr (result); return 0; } diff --git a/libdw/dwarf_getmacros.c b/libdw/dwarf_getmacros.c index 737dc5d..848128e 100644 --- a/libdw/dwarf_getmacros.c +++ b/libdw/dwarf_getmacros.c @@ -354,6 +354,8 @@ read_macros (Dwarf *dbg, int sec_index, .dbg = dbg, .version = 4, .offset_size = table->is_64bit ? 8 : 4, + .startp = (void *) startp + offset, + .endp = (void *) endp, }; Dwarf_Attribute attributes[proto->nforms]; @@ -367,8 +369,7 @@ read_macros (Dwarf *dbg, int sec_index, attributes[i].valp = (void *) readp; attributes[i].cu = &fake_cu; - size_t len = __libdw_form_val_len (dbg, &fake_cu, - proto->forms[i], readp, endp); + size_t len = __libdw_form_val_len (&fake_cu, proto->forms[i], readp); if (len == (size_t) -1) return -1; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index edceb59..eda61a9 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -188,6 +188,10 @@ struct Dwarf /* Cached info from the CFI section. */ struct Dwarf_CFI_s *cfi; + /* Fake loc CU. Used when synthesizing attributes for Dwarf_Ops that + came from a location list entry in dwarf_getlocation_attr. */ + struct Dwarf_CU *fake_loc_cu; + /* Internal memory handling. This is basically a simplified reimplementation of obstacks. Unfortunately the standard obstack implementation is not usable in libraries. */ @@ -337,7 +341,7 @@ struct Dwarf_CU ((Dwarf_Die) \ { \ .cu = (fromcu), \ - .addr = ((char *) cu_data (fromcu)->d_buf \ + .addr = ((char *) fromcu->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf \ + DIE_OFFSET_FROM_CU_OFFSET ((fromcu)->start, \ (fromcu)->offset_size, \ (fromcu)->type_offset != 0)) \ @@ -483,18 +487,16 @@ __libdw_dieabbrev (Dwarf_Die *die, const unsigned char **readp) } /* Helper functions for form handling. */ -extern size_t __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu, +extern size_t __libdw_form_val_compute_len (struct Dwarf_CU *cu, unsigned int form, - const unsigned char *valp, - const unsigned char *endp) - __nonnull_attribute__ (1, 2, 4, 5) internal_function; + const unsigned char *valp) + __nonnull_attribute__ (1, 3) internal_function; /* Find the length of a form attribute. */ static inline size_t -__nonnull_attribute__ (1, 2, 4, 5) -__libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu, - unsigned int form, const unsigned char *valp, - const unsigned char *endp) +__nonnull_attribute__ (1, 3) +__libdw_form_val_len (struct Dwarf_CU *cu, unsigned int form, + const unsigned char *valp) { /* Small lookup table of forms with fixed lengths. Absent indexes are initialized 0, so any truly desired 0 is set to 0x80 and masked. */ @@ -513,6 +515,7 @@ __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu, uint8_t len = form_lengths[form]; if (len != 0) { + const unsigned char *endp = cu->endp; len &= 0x7f; /* Mask to allow 0x80 -> 0. */ if (unlikely (len > (size_t) (endp - valp))) { @@ -524,7 +527,7 @@ __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu, } /* Other forms require some computation. */ - return __libdw_form_val_compute_len (dbg, cu, form, valp, endp); + return __libdw_form_val_compute_len (cu, form, valp); } /* Helper function for DW_FORM_ref* handling. */ @@ -721,12 +724,6 @@ cu_sec_idx (struct Dwarf_CU *cu) return cu->type_offset == 0 ? IDX_debug_info : IDX_debug_types; } -static inline Elf_Data * -cu_data (struct Dwarf_CU *cu) -{ - return cu->dbg->sectiondata[cu_sec_idx (cu)]; -} - /* Read up begin/end pair and increment read pointer. - If it's normal range record, set up *BEGINP and *ENDP and return 0. - If it's base address selection record, set up *BASEP and return 1. @@ -744,7 +741,7 @@ unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index, internal_function; /* Fills in the given attribute to point at an empty location expression. */ -void __libdw_empty_loc_attr (Dwarf_Attribute *attr, struct Dwarf_CU *cu) +void __libdw_empty_loc_attr (Dwarf_Attribute *attr) internal_function; /* Load .debug_line unit at DEBUG_LINE_OFFSET. COMP_DIR is a value of diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c index c783ff8..d8da2e3 100644 --- a/libdw/libdw_findcu.c +++ b/libdw/libdw_findcu.c @@ -119,9 +119,8 @@ __libdw_intern_next_unit (dbg, debug_types) if (debug_types) Dwarf_Sig8_Hash_insert (&dbg->sig8_hash, type_sig8, newp); - void *buf = cu_data (newp)->d_buf; - newp->startp = buf + newp->start; - newp->endp = buf + newp->end; + newp->startp = data->d_buf + newp->start; + newp->endp = data->d_buf + newp->end; /* Add the new entry to the search tree. */ if (tsearch (newp, tree, findcu_cb) == NULL) diff --git a/libdw/libdw_form.c b/libdw/libdw_form.c index 65ac7de..379ede5 100644 --- a/libdw/libdw_form.c +++ b/libdw/libdw_form.c @@ -39,11 +39,11 @@ size_t internal_function -__libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu, - unsigned int form, const unsigned char *valp, - const unsigned char *endp) +__libdw_form_val_compute_len (struct Dwarf_CU *cu, unsigned int form, + const unsigned char *valp) { const unsigned char *startp = valp; + const unsigned char *endp = cu->endp; Dwarf_Word u128; size_t result; @@ -75,13 +75,13 @@ __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu, case DW_FORM_block2: if (unlikely ((size_t) (endp - startp) < 2)) goto invalid; - result = read_2ubyte_unaligned (dbg, valp) + 2; + result = read_2ubyte_unaligned (cu->dbg, valp) + 2; break; case DW_FORM_block4: if (unlikely ((size_t) (endp - startp) < 4)) goto invalid; - result = read_4ubyte_unaligned (dbg, valp) + 4; + result = read_4ubyte_unaligned (cu->dbg, valp) + 4; break; case DW_FORM_block: @@ -112,7 +112,7 @@ __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu, case DW_FORM_indirect: get_uleb128 (u128, valp); // XXX Is this really correct? - result = __libdw_form_val_len (dbg, cu, u128, valp, endp); + result = __libdw_form_val_len (cu, u128, valp); if (result != (size_t) -1) result += valp - startp; else -- 2.1.0