From: Mark Wielaard Date: Thu, 4 Dec 2014 20:43:44 +0000 (+0100) Subject: libdw: Add overflow checking to __libdw_form_val_len. X-Git-Tag: elfutils-0.161~55 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cb73b5a015606a02f952f7eddaba15327f6191fa;p=thirdparty%2Felfutils.git libdw: Add overflow checking to __libdw_form_val_len. Pass endp as argument to __libdw_form_val_len and check we don't read beyond the end of expected data and don't return lengths that would overflow. Signed-off-by: Mark Wielaard --- diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 909fdbc1f..b9b868b5c 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,13 @@ +2014-12-04 Mark Wielaard + + * libdwP.h (__libdw_form_val_compute_len): Add endp argument. + (__libdw_form_val_len): Likewise and check len doesn't overflow. + * libdw_form.c (__libdw_form_val_compute_len): Likewise. + * dwarf_child.c (__libdw_find_attr): Call __libdw_form_val_len + with endp. + * dwarf_getattrs.c (dwarf_getattrs): Likewise. + * dwarf_getmacros.c (read_macros): Likewise and check for errors. + 2014-12-02 Petr Machata * dwarf_getmacros.c (token_from_offset, offset_from_token): New diff --git a/libdw/dwarf_child.c b/libdw/dwarf_child.c index 1d3a33717..fa3160066 100644 --- a/libdw/dwarf_child.c +++ b/libdw/dwarf_child.c @@ -1,5 +1,5 @@ /* Return child of current DIE. - Copyright (C) 2003-2011 Red Hat, Inc. + Copyright (C) 2003-2011, 2014 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper , 2003. @@ -104,7 +104,8 @@ __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); + size_t len = __libdw_form_val_len (dbg, die->cu, attr_form, readp, + endp); if (unlikely (len == (size_t) -1l)) { @@ -112,7 +113,7 @@ __libdw_find_attr (Dwarf_Die *die, unsigned int search_name, break; } - // XXX We need better boundary checks. + // __libdw_form_val_len will have done a bounds check. readp += len; } } diff --git a/libdw/dwarf_getattrs.c b/libdw/dwarf_getattrs.c index 82eb3f8fa..627a85118 100644 --- a/libdw/dwarf_getattrs.c +++ b/libdw/dwarf_getattrs.c @@ -1,5 +1,5 @@ /* Get attributes of the DIE. - Copyright (C) 2004, 2005, 2008, 2009 Red Hat, Inc. + Copyright (C) 2004, 2005, 2008, 2009, 2014 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper , 2004. @@ -67,12 +67,13 @@ dwarf_getattrs (Dwarf_Die *die, int (*callback) (Dwarf_Attribute *, void *), /* Go over the list of attributes. */ Dwarf *dbg = die->cu->dbg; + const unsigned char *endp; + endp = ((const unsigned char *) dbg->sectiondata[IDX_debug_abbrev]->d_buf + + dbg->sectiondata[IDX_debug_abbrev]->d_size); while (1) { /* Are we still in bounds? */ - if (unlikely (attrp - >= ((unsigned char *) dbg->sectiondata[IDX_debug_abbrev]->d_buf - + dbg->sectiondata[IDX_debug_abbrev]->d_size))) + if (unlikely (attrp >= endp)) goto invalid_dwarf; /* Get attribute name and form. */ @@ -111,13 +112,13 @@ dwarf_getattrs (Dwarf_Die *die, int (*callback) (Dwarf_Attribute *, void *), if (attr.form != 0) { size_t len = __libdw_form_val_len (dbg, die->cu, attr.form, - die_addr); + die_addr, endp); if (unlikely (len == (size_t) -1l)) /* Something wrong with the file. */ return -1l; - // XXX We need better boundary checks. + // __libdw_form_val_len will have done a bounds check. die_addr += len; } } diff --git a/libdw/dwarf_getmacros.c b/libdw/dwarf_getmacros.c index bd64d60f0..737dc5d11 100644 --- a/libdw/dwarf_getmacros.c +++ b/libdw/dwarf_getmacros.c @@ -367,8 +367,12 @@ read_macros (Dwarf *dbg, int sec_index, attributes[i].valp = (void *) readp; attributes[i].cu = &fake_cu; - readp += __libdw_form_val_len (dbg, &fake_cu, - proto->forms[i], readp); + size_t len = __libdw_form_val_len (dbg, &fake_cu, + proto->forms[i], readp, endp); + if (len == (size_t) -1) + return -1; + + readp += len; } Dwarf_Macro macro = { diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 5ccb13c4b..351c5b47b 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -458,14 +458,16 @@ extern Dwarf_Abbrev *__libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, /* Helper functions for form handling. */ extern size_t __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu, unsigned int form, - const unsigned char *valp) - __nonnull_attribute__ (1, 2, 4) internal_function; + const unsigned char *valp, + const unsigned char *endp) + __nonnull_attribute__ (1, 2, 4, 5) internal_function; /* Find the length of a form attribute. */ static inline size_t -__nonnull_attribute__ (1, 2, 4) +__nonnull_attribute__ (1, 2, 4, 5) __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu, - unsigned int form, const unsigned char *valp) + unsigned int form, const unsigned char *valp, + const unsigned char *endp) { /* Small lookup table of forms with fixed lengths. Absent indexes are initialized 0, so any truly desired 0 is set to 0x80 and masked. */ @@ -483,11 +485,19 @@ __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu, { uint8_t len = form_lengths[form]; if (len != 0) - return len & 0x7f; /* Mask to allow 0x80 -> 0. */ + { + len &= 0x7f; /* Mask to allow 0x80 -> 0. */ + if (unlikely (len > (size_t) (endp - valp))) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + return len; + } } /* Other forms require some computation. */ - return __libdw_form_val_compute_len (dbg, cu, form, valp); + return __libdw_form_val_compute_len (dbg, cu, form, valp, endp); } /* Helper function for DW_FORM_ref* handling. */ diff --git a/libdw/libdw_form.c b/libdw/libdw_form.c index 53505564d..65ac7dede 100644 --- a/libdw/libdw_form.c +++ b/libdw/libdw_form.c @@ -1,5 +1,5 @@ /* Helper functions for form handling. - Copyright (C) 2003-2009 Red Hat, Inc. + Copyright (C) 2003-2009, 2014 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper , 2003. @@ -40,9 +40,10 @@ size_t internal_function __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu, - unsigned int form, const unsigned char *valp) + unsigned int form, const unsigned char *valp, + const unsigned char *endp) { - const unsigned char *saved; + const unsigned char *startp = valp; Dwarf_Word u128; size_t result; @@ -66,49 +67,67 @@ __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu, break; case DW_FORM_block1: + if (unlikely ((size_t) (endp - startp) < 1)) + goto invalid; result = *valp + 1; break; case DW_FORM_block2: + if (unlikely ((size_t) (endp - startp) < 2)) + goto invalid; result = read_2ubyte_unaligned (dbg, valp) + 2; break; case DW_FORM_block4: + if (unlikely ((size_t) (endp - startp) < 4)) + goto invalid; result = read_4ubyte_unaligned (dbg, valp) + 4; break; case DW_FORM_block: case DW_FORM_exprloc: - saved = valp; + // XXX overflow check get_uleb128 (u128, valp); - result = u128 + (valp - saved); + result = u128 + (valp - startp); break; case DW_FORM_string: - result = strlen ((char *) valp) + 1; - break; + { + const unsigned char *endstrp = memchr (valp, '\0', + (size_t) (endp - startp)); + if (unlikely (endstrp == NULL)) + goto invalid; + result = (size_t) (endstrp - startp) + 1; + break; + } case DW_FORM_sdata: case DW_FORM_udata: case DW_FORM_ref_udata: - saved = valp; + // XXX overflow check get_uleb128 (u128, valp); - result = valp - saved; + result = valp - startp; break; case DW_FORM_indirect: - saved = valp; get_uleb128 (u128, valp); // XXX Is this really correct? - result = __libdw_form_val_len (dbg, cu, u128, valp); + result = __libdw_form_val_len (dbg, cu, u128, valp, endp); if (result != (size_t) -1) - result += valp - saved; + result += valp - startp; + else + return (size_t) -1; break; default: + goto invalid; + } + + if (unlikely (result > (size_t) (endp - startp))) + { + invalid: __libdw_seterrno (DWARF_E_INVALID_DWARF); - result = (size_t) -1l; - break; + result = (size_t) -1; } return result;