[PATCH v4] [gdb/symtab] Fix an out of bounds array access in find_epilogue_using_linetable
Tom de Vries
tdevries@suse.de
Mon Apr 8 12:57:16 GMT 2024
From: Bernd Edlinger <bernd.edlinger@hotmail.de>
An out of bounds array access in find_epilogue_using_linetable causes random
test failures like these:
FAIL: gdb.base/unwind-on-each-insn-amd64.exp: foo: instruction 6: $fba_value == $fn_fba
FAIL: gdb.base/unwind-on-each-insn-amd64.exp: foo: instruction 6: check frame-id matches
FAIL: gdb.base/unwind-on-each-insn-amd64.exp: foo: instruction 6: bt 2
FAIL: gdb.base/unwind-on-each-insn-amd64.exp: foo: instruction 6: up
FAIL: gdb.base/unwind-on-each-insn-amd64.exp: foo: instruction 6: $sp_value == $::main_sp
FAIL: gdb.base/unwind-on-each-insn-amd64.exp: foo: instruction 6: $fba_value == $::main_fba
FAIL: gdb.base/unwind-on-each-insn-amd64.exp: foo: instruction 6: [string equal $fid $::main_fid]
Here the read happens below the first element of the line
table, and the test failure depends on the value that is
read from there.
It also happens that std::lower_bound returns a pointer exactly at the upper
bound of the line table, also here the read value is undefined, that happens
in this test:
FAIL: gdb.dwarf2/dw2-epilogue-begin.exp: confirm watchpoint doesn't trigger
Fixes: 528b729be1a2 ("gdb/dwarf2: Add support for DW_LNS_set_epilogue_begin in line-table")
Co-Authored-By: Tom de Vries <tdevries@suse.de>
PR symtab/31268
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31268
---
gdb/symtab.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 77 insertions(+), 10 deletions(-)
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 86603dfebc3..427d7b9f8b2 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4166,10 +4166,14 @@ find_epilogue_using_linetable (CORE_ADDR func_addr)
= unrelocated_addr (end_pc - objfile->text_section_offset ());
const linetable *linetable = sal.symtab->linetable ();
- /* This should find the last linetable entry of the current function.
- It is probably where the epilogue begins, but since the DWARF 5
- spec doesn't guarantee it, we iterate backwards through the function
- until we either find it or are sure that it doesn't exist. */
+ if (linetable == nullptr || linetable->nitems == 0)
+ {
+ /* Empty line table. */
+ return {};
+ }
+
+ /* Find the first linetable entry after the current function. Note that
+ this also may be an end_sequence entry. */
auto it = std::lower_bound
(linetable->item, linetable->item + linetable->nitems, unrel_end,
[] (const linetable_entry <e, unrelocated_addr pc)
@@ -4177,13 +4181,76 @@ find_epilogue_using_linetable (CORE_ADDR func_addr)
return lte.unrelocated_pc () < pc;
});
- while (it->unrelocated_pc () >= unrel_start)
- {
- if (it->epilogue_begin)
- return {it->pc (objfile)};
- it --;
- }
+ if (it == linetable->item + linetable->nitems)
+ {
+ /* We couldn't find either:
+ - a linetable entry starting the function after the current
+ function, or
+ - an end_sequence entry that terminates the current function
+ at unrel_end.
+ This can happen when the linetable doesn't describe the full
+ extent of the function, which shouldn't happen with
+ compiler-generated debug info. Handle the corner case
+ conservatively. */
+ return {};
+ }
+ else
+ {
+ if (unrel_end < it->unrelocated_pc ())
+ {
+ /* We found a line entry that starts past the end of the
+ function. This can happen if the previous entry straddles
+ two functions, which shouldn't happen with compiler-generated
+ debug info. Handle the corner case conservatively. */
+ return {};
+ }
+ gdb_assert (unrel_end == it->unrelocated_pc ());
+ }
+
+ /* Move to the last linetable entry of the current function. */
+ if (it == &linetable->item[0])
+ {
+ /* Doing it-- would introduce undefined behaviour, avoid it by
+ explicitly handling this case. */
+ return {};
+ }
+ it--;
+ if (it->unrelocated_pc () < unrel_start)
+ {
+ /* Not in the current function. */
+ return {};
+ }
+ gdb_assert (it->unrelocated_pc () < unrel_end);
+
+ /* We're at the the last linetable entry of the current function. This
+ is probably where the epilogue begins, but since the DWARF 5 spec
+ doesn't guarantee it, we iterate backwards through the current
+ function until we either find the epilogue beginning, or are sure
+ that it doesn't exist. */
+ for (; it >= &linetable->item[0]; it--)
+ {
+ if (it->unrelocated_pc () < unrel_start)
+ {
+ /* No longer in the current function. */
+ break;
+ }
+
+ if (it->epilogue_begin)
+ {
+ /* Found the beginning of the epilogue. */
+ return {it->pc (objfile)};
+ }
+
+ if (it == &linetable->item[0])
+ {
+ /* No more entries in the current function.
+ Doing it-- would introduce undefined behaviour, avoid it by
+ explicitly handling this case. */
+ break;
+ }
+ }
}
+
return {};
}
base-commit: 94a94c904da29bb733c19b5a0a168557c1819720
--
2.35.3
More information about the Gdb-patches
mailing list