diff options
| author | Mark Wielaard <[email protected]> | 2015-01-07 23:39:28 +0100 |
|---|---|---|
| committer | Mark Wielaard <[email protected]> | 2015-01-15 14:25:35 +0100 |
| commit | b4b2de9f1f1fb694b77371a61f808a1641bbafea (patch) | |
| tree | 7da33fc1555daac5853a41fe6bea883e4f86b012 /libdw/encoded-value.h | |
| parent | 975a37d11d1a14229ada9293fac88a493bf83cfa (diff) | |
libdw: Robustify eh_frame_hdr and encoded-values reading.
Sanity check and keep track of binary_search_table data buffer length.
Add bounds check to encoded value reading. Also fix a bug when reading
the eh_frame header data from an other endian ELF image. Add a testcase
that would fail the new sanity checks because of the endian bug.
Signed-off-by: Mark Wielaard <[email protected]>
Diffstat (limited to 'libdw/encoded-value.h')
| -rw-r--r-- | libdw/encoded-value.h | 69 |
1 files changed, 51 insertions, 18 deletions
diff --git a/libdw/encoded-value.h b/libdw/encoded-value.h index ae9a38f9..f953f5ef 100644 --- a/libdw/encoded-value.h +++ b/libdw/encoded-value.h @@ -1,5 +1,5 @@ /* DW_EH_PE_* support for libdw unwinder. - Copyright (C) 2009-2010, 2014 Red Hat, Inc. + Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -32,8 +32,11 @@ #include <dwarf.h> #include <stdlib.h> #include "libdwP.h" +#include "../libelf/common.h" +/* Returns zero if the value is omitted, the encoding is unknown or + the (leb128) size cannot be determined. */ static size_t __attribute__ ((unused)) encoded_value_size (const Elf_Data *data, const unsigned char e_ident[], uint8_t encoding, const uint8_t *p) @@ -63,11 +66,11 @@ encoded_value_size (const Elf_Data *data, const unsigned char e_ident[], } default: - abort (); return 0; } } +/* Returns zero when value was read successfully, minus one otherwise. */ static inline int __attribute__ ((unused)) __libdw_cfi_read_address_inc (const Dwarf_CFI *cache, const unsigned char **addrp, @@ -82,16 +85,32 @@ __libdw_cfi_read_address_inc (const Dwarf_CFI *cache, /* Only .debug_frame might have relocation to consider. Read plain values from .eh_frame data. */ + const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size; + Dwarf eh_dbg = { .other_byte_order = MY_ELFDATA != cache->e_ident[EI_DATA] }; + if (width == 4) - *ret = read_4ubyte_unaligned_inc (cache, *addrp); + { + if (unlikely (*addrp + 4 > endp)) + { + invalid_data: + __libdw_seterrno (DWARF_E_INVALID_CFI); + return -1; + } + *ret = read_4ubyte_unaligned_inc (&eh_dbg, *addrp); + } else - *ret = read_8ubyte_unaligned_inc (cache, *addrp); + { + if (unlikely (*addrp + 8 > endp)) + goto invalid_data; + *ret = read_8ubyte_unaligned_inc (&eh_dbg, *addrp); + } return 0; } +/* Returns true on error, false otherwise. */ static bool __attribute__ ((unused)) -read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding, const uint8_t **p, - Dwarf_Addr *result) +read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding, + const uint8_t **p, Dwarf_Addr *result) { *result = 0; switch (encoding & 0x70) @@ -115,8 +134,11 @@ read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding, const uint8_t **p, break; case DW_EH_PE_aligned: { - const size_t size = encoded_value_size (&cache->data->d, cache->e_ident, + const size_t size = encoded_value_size (&cache->data->d, + cache->e_ident, encoding, *p); + if (unlikely (size == 0)) + return true; size_t align = ((cache->frame_vaddr + (*p - (const uint8_t *) cache->data->d.d_buf)) & (size - 1)); @@ -126,54 +148,63 @@ read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding, const uint8_t **p, } default: - abort (); + __libdw_seterrno (DWARF_E_INVALID_CFI); + return true; } Dwarf_Addr value; + const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size; switch (encoding & 0x0f) { case DW_EH_PE_udata2: + if (unlikely (*p + 2 > endp)) + { + invalid_data: + __libdw_seterrno (DWARF_E_INVALID_CFI); + return true; + } value = read_2ubyte_unaligned_inc (cache, *p); break; case DW_EH_PE_sdata2: + if (unlikely (*p + 2 > endp)) + goto invalid_data; value = read_2sbyte_unaligned_inc (cache, *p); break; case DW_EH_PE_udata4: - if (__libdw_cfi_read_address_inc (cache, p, 4, &value)) + if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0)) return true; break; case DW_EH_PE_sdata4: - if (__libdw_cfi_read_address_inc (cache, p, 4, &value)) + if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0)) return true; value = (Dwarf_Sword) (Elf32_Sword) value; /* Sign-extend. */ break; case DW_EH_PE_udata8: case DW_EH_PE_sdata8: - if (__libdw_cfi_read_address_inc (cache, p, 8, &value)) + if (unlikely (__libdw_cfi_read_address_inc (cache, p, 8, &value) != 0)) return true; break; case DW_EH_PE_absptr: - if (__libdw_cfi_read_address_inc (cache, p, 0, &value)) + if (unlikely (__libdw_cfi_read_address_inc (cache, p, 0, &value) != 0)) return true; break; case DW_EH_PE_uleb128: - // XXX we trust there is enough data. - get_uleb128 (value, *p, *p + len_leb128 (Dwarf_Addr)); + get_uleb128 (value, *p, endp); break; case DW_EH_PE_sleb128: - // XXX we trust there is enough data. - get_sleb128 (value, *p, *p + len_leb128 (Dwarf_Addr)); + get_sleb128 (value, *p, endp); break; default: - abort (); + __libdw_seterrno (DWARF_E_INVALID_CFI); + return true; } *result += value; @@ -188,7 +219,9 @@ read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding, const uint8_t **p, DW_EH_PE_absptr, NULL)))) return true; const uint8_t *ptr = cache->data->d.d_buf + *result; - return __libdw_cfi_read_address_inc (cache, &ptr, 0, result); + if (unlikely (__libdw_cfi_read_address_inc (cache, &ptr, 0, result) + != 0)) + return true; } return false; |
