summaryrefslogtreecommitdiffstats
path: root/libdw/encoded-value.h
diff options
context:
space:
mode:
authorMark Wielaard <[email protected]>2015-01-07 23:39:28 +0100
committerMark Wielaard <[email protected]>2015-01-15 14:25:35 +0100
commitb4b2de9f1f1fb694b77371a61f808a1641bbafea (patch)
tree7da33fc1555daac5853a41fe6bea883e4f86b012 /libdw/encoded-value.h
parent975a37d11d1a14229ada9293fac88a493bf83cfa (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.h69
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;