mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2024-11-25 08:37:38 +01:00
Check that the CIE is within the segment
Due to a bug in the gold linker[1], the .eh_frame and .eh_frame_hdr sections contains garbage. When dwarf_extract_proc_info_from_fde tried to look up the begin of the CIE subsection, it would underflow the .eh_frame segment, resulting in a crash[2]. This patch avoids that crash by checking whether the CIE pointer is located after the begin of the .eh_frame section. The variable "base" was misused in various places as a boolean (decode as .debug_frame or decode as .eh_frame). These instances have been renamed to is_debug_frame where applicable. Tested on Linux x86_64. [1]: https://sourceware.org/bugzilla/show_bug.cgi?id=17639 [2]: http://lists.nongnu.org/archive/html/libunwind-devel/2014-11/msg00009.html Signed-off-by: Peter Wu <peter@lekensteyn.nl>
This commit is contained in:
parent
6e3254ea6e
commit
68a2910bae
3 changed files with 29 additions and 15 deletions
|
@ -423,8 +423,9 @@ extern int dwarf_extract_proc_info_from_fde (unw_addr_space_t as,
|
||||||
unw_accessors_t *a,
|
unw_accessors_t *a,
|
||||||
unw_word_t *fde_addr,
|
unw_word_t *fde_addr,
|
||||||
unw_proc_info_t *pi,
|
unw_proc_info_t *pi,
|
||||||
int need_unwind_info,
|
|
||||||
unw_word_t base,
|
unw_word_t base,
|
||||||
|
int need_unwind_info,
|
||||||
|
int is_debug_frame,
|
||||||
void *arg);
|
void *arg);
|
||||||
extern int dwarf_find_save_locs (struct dwarf_cursor *c);
|
extern int dwarf_find_save_locs (struct dwarf_cursor *c);
|
||||||
extern int dwarf_create_state_record (struct dwarf_cursor *c,
|
extern int dwarf_create_state_record (struct dwarf_cursor *c,
|
||||||
|
|
|
@ -45,7 +45,7 @@ is_cie_id (unw_word_t val, int is_debug_frame)
|
||||||
static inline int
|
static inline int
|
||||||
parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
|
parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
|
||||||
const unw_proc_info_t *pi, struct dwarf_cie_info *dci,
|
const unw_proc_info_t *pi, struct dwarf_cie_info *dci,
|
||||||
unw_word_t base, void *arg)
|
int is_debug_frame, void *arg)
|
||||||
{
|
{
|
||||||
uint8_t version, ch, augstr[5], fde_encoding, handler_encoding;
|
uint8_t version, ch, augstr[5], fde_encoding, handler_encoding;
|
||||||
unw_word_t len, cie_end_addr, aug_size;
|
unw_word_t len, cie_end_addr, aug_size;
|
||||||
|
@ -79,7 +79,7 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
|
||||||
/* the CIE is in the 32-bit DWARF format */
|
/* the CIE is in the 32-bit DWARF format */
|
||||||
uint32_t cie_id;
|
uint32_t cie_id;
|
||||||
/* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */
|
/* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */
|
||||||
const uint32_t expected_id = (base) ? 0xffffffff : 0;
|
const uint32_t expected_id = (is_debug_frame) ? 0xffffffff : 0;
|
||||||
|
|
||||||
len = u32val;
|
len = u32val;
|
||||||
cie_end_addr = addr + len;
|
cie_end_addr = addr + len;
|
||||||
|
@ -97,7 +97,7 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
|
||||||
uint64_t cie_id;
|
uint64_t cie_id;
|
||||||
/* DWARF says CIE id should be 0xffffffffffffffff, but in
|
/* DWARF says CIE id should be 0xffffffffffffffff, but in
|
||||||
.eh_frame, it's 0 */
|
.eh_frame, it's 0 */
|
||||||
const uint64_t expected_id = (base) ? 0xffffffffffffffffull : 0;
|
const uint64_t expected_id = (is_debug_frame) ? 0xffffffffffffffffull : 0;
|
||||||
|
|
||||||
if ((ret = dwarf_readu64 (as, a, &addr, &u64val, arg)) < 0)
|
if ((ret = dwarf_readu64 (as, a, &addr, &u64val, arg)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -220,7 +220,8 @@ parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
|
||||||
HIDDEN int
|
HIDDEN int
|
||||||
dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
|
dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
|
||||||
unw_word_t *addrp, unw_proc_info_t *pi,
|
unw_word_t *addrp, unw_proc_info_t *pi,
|
||||||
int need_unwind_info, unw_word_t base,
|
unw_word_t base,
|
||||||
|
int need_unwind_info, int is_debug_frame,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
unw_word_t fde_end_addr, cie_addr, cie_offset_addr, aug_end_addr = 0;
|
unw_word_t fde_end_addr, cie_addr, cie_offset_addr, aug_end_addr = 0;
|
||||||
|
@ -251,14 +252,18 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
|
||||||
*addrp = fde_end_addr = addr + u32val;
|
*addrp = fde_end_addr = addr + u32val;
|
||||||
cie_offset_addr = addr;
|
cie_offset_addr = addr;
|
||||||
|
|
||||||
|
/* CIE must be within the segment. */
|
||||||
|
if (cie_offset_addr < base)
|
||||||
|
return -UNW_ENOINFO;
|
||||||
|
|
||||||
if ((ret = dwarf_reads32 (as, a, &addr, &cie_offset, arg)) < 0)
|
if ((ret = dwarf_reads32 (as, a, &addr, &cie_offset, arg)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (is_cie_id (cie_offset, base != 0))
|
if (is_cie_id (cie_offset, is_debug_frame))
|
||||||
/* ignore CIEs (happens during linear searches) */
|
/* ignore CIEs (happens during linear searches) */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (base != 0)
|
if (is_debug_frame)
|
||||||
cie_addr = base + cie_offset;
|
cie_addr = base + cie_offset;
|
||||||
else
|
else
|
||||||
/* DWARF says that the CIE_pointer in the FDE is a
|
/* DWARF says that the CIE_pointer in the FDE is a
|
||||||
|
@ -279,14 +284,18 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
|
||||||
*addrp = fde_end_addr = addr + u64val;
|
*addrp = fde_end_addr = addr + u64val;
|
||||||
cie_offset_addr = addr;
|
cie_offset_addr = addr;
|
||||||
|
|
||||||
|
/* CIE must be within the segment. */
|
||||||
|
if (cie_offset_addr < base)
|
||||||
|
return -UNW_ENOINFO;
|
||||||
|
|
||||||
if ((ret = dwarf_reads64 (as, a, &addr, &cie_offset, arg)) < 0)
|
if ((ret = dwarf_reads64 (as, a, &addr, &cie_offset, arg)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (is_cie_id (cie_offset, base != 0))
|
if (is_cie_id (cie_offset, is_debug_frame))
|
||||||
/* ignore CIEs (happens during linear searches) */
|
/* ignore CIEs (happens during linear searches) */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (base != 0)
|
if (is_debug_frame)
|
||||||
cie_addr = base + cie_offset;
|
cie_addr = base + cie_offset;
|
||||||
else
|
else
|
||||||
/* DWARF says that the CIE_pointer in the FDE is a
|
/* DWARF says that the CIE_pointer in the FDE is a
|
||||||
|
@ -298,7 +307,7 @@ dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
|
||||||
|
|
||||||
Debug (15, "looking for CIE at address %lx\n", (long) cie_addr);
|
Debug (15, "looking for CIE at address %lx\n", (long) cie_addr);
|
||||||
|
|
||||||
if ((ret = parse_cie (as, a, cie_addr, pi, &dci, base, arg)) < 0)
|
if ((ret = parse_cie (as, a, cie_addr, pi, &dci, is_debug_frame, arg)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* IP-range has same encoding as FDE pointers, except that it's
|
/* IP-range has same encoding as FDE pointers, except that it's
|
||||||
|
|
|
@ -59,8 +59,9 @@ linear_search (unw_addr_space_t as, unw_word_t ip,
|
||||||
while (i++ < fde_count && addr < eh_frame_end)
|
while (i++ < fde_count && addr < eh_frame_end)
|
||||||
{
|
{
|
||||||
fde_addr = addr;
|
fde_addr = addr;
|
||||||
if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, 0, 0, arg))
|
if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi,
|
||||||
< 0)
|
eh_frame_start,
|
||||||
|
0, 0, arg)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (ip >= pi->start_ip && ip < pi->end_ip)
|
if (ip >= pi->start_ip && ip < pi->end_ip)
|
||||||
|
@ -69,6 +70,7 @@ linear_search (unw_addr_space_t as, unw_word_t ip,
|
||||||
return 1;
|
return 1;
|
||||||
addr = fde_addr;
|
addr = fde_addr;
|
||||||
if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi,
|
if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi,
|
||||||
|
eh_frame_start,
|
||||||
need_unwind_info, 0,
|
need_unwind_info, 0,
|
||||||
arg))
|
arg))
|
||||||
< 0)
|
< 0)
|
||||||
|
@ -468,8 +470,8 @@ dwarf_find_debug_frame (int found, unw_dyn_info_t *di_debug, unw_word_t ip,
|
||||||
|
|
||||||
err = dwarf_extract_proc_info_from_fde (unw_local_addr_space,
|
err = dwarf_extract_proc_info_from_fde (unw_local_addr_space,
|
||||||
a, &fde_addr,
|
a, &fde_addr,
|
||||||
&this_pi, 0,
|
&this_pi,
|
||||||
(uintptr_t) buf,
|
(uintptr_t) buf, 0, 1,
|
||||||
NULL);
|
NULL);
|
||||||
if (err == 0)
|
if (err == 0)
|
||||||
{
|
{
|
||||||
|
@ -902,8 +904,10 @@ dwarf_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
|
||||||
"fde_addr = %lx\n", (long) e->fde_offset, (long) segbase,
|
"fde_addr = %lx\n", (long) e->fde_offset, (long) segbase,
|
||||||
(long) debug_frame_base, (long) fde_addr);
|
(long) debug_frame_base, (long) fde_addr);
|
||||||
if ((ret = dwarf_extract_proc_info_from_fde (as, a, &fde_addr, pi,
|
if ((ret = dwarf_extract_proc_info_from_fde (as, a, &fde_addr, pi,
|
||||||
|
debug_frame_base ?
|
||||||
|
debug_frame_base : segbase,
|
||||||
need_unwind_info,
|
need_unwind_info,
|
||||||
debug_frame_base, arg)) < 0)
|
debug_frame_base != 0, arg)) < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* .debug_frame uses an absolute encoding that does not know about any
|
/* .debug_frame uses an absolute encoding that does not know about any
|
||||||
|
|
Loading…
Reference in a new issue