DWARF1 AT_sibling sanity check
Alan Modra <[email protected]> Mon, 27 Jul 2026 14:23:41 +0930
| Newsgroups | gmane.comp.gnu.binutils |
|---|---|
| Message-ID | <[email protected]> |
I am not absolutely certain that DWARF version 1 AT_sibling always
points forward, but that seems to be the case for gcc-3.3 from a quick
look at gcc/dwarfout.c and examining some i686-linux output. I
believe gcc stopped supporting DWARF version 1 after gcc-3.3.
If backward links are allowed then it is considerably more tedious to
protect against fuzzed object files that loop forever reading DWARF1.
So this patch may break addr2line and objdump -dS for some old files.
If it does, well, removing DWARF1 support entirely would break that
support too. (readelf doesn't support DWARF version 1).
* dwarf1.c (parse_die): Replace abfd and aDiePtrEnd parameters
with stash pointer. Adjust to suit. Sanity check AT_sibling
value.
(parse_functions_in_unit, _bfd_dwarf1_find_nearest_line): Adjust
parse_die calls.
diff --git a/bfd/dwarf1.c b/bfd/dwarf1.c
index b899cc7876f..2a34b88765b 100644
--- a/bfd/dwarf1.c
+++ b/bfd/dwarf1.c
@@ -178,23 +178,24 @@ alloc_dwarf1_func (struct dwarf1_debug* stash, struct dwarf1_unit* aUnit)
Return FALSE if the die is invalidly formatted; TRUE otherwise. */
static bool
-parse_die (bfd * abfd,
- struct die_info * aDieInfo,
- bfd_byte * aDiePtr,
- bfd_byte * aDiePtrEnd)
+parse_die (const struct dwarf1_debug *stash,
+ struct die_info *aDieInfo,
+ bfd_byte *aDiePtr)
{
+ bfd *abfd = stash->abfd;
+ bfd_byte *aDiePtrEnd;
bfd_byte *this_die = aDiePtr;
bfd_byte *xptr = this_die;
memset (aDieInfo, 0, sizeof (* aDieInfo));
/* First comes the length. */
- if (xptr + 4 > aDiePtrEnd)
+ if (xptr + 4 > stash->debug_section_end)
return false;
aDieInfo->length = bfd_get_32 (abfd, xptr);
xptr += 4;
if (aDieInfo->length <= 4
- || (size_t) (aDiePtrEnd - this_die) < aDieInfo->length)
+ || (size_t) (stash->debug_section_end - this_die) < aDieInfo->length)
return false;
aDiePtrEnd = this_die + aDieInfo->length;
if (aDieInfo->length < 6)
@@ -232,7 +233,21 @@ parse_die (bfd * abfd,
if (xptr + 4 <= aDiePtrEnd)
{
if (attr == AT_sibling)
- aDieInfo->sibling = bfd_get_32 (abfd, xptr);
+ {
+ aDieInfo->sibling = bfd_get_32 (abfd, xptr);
+ if (aDieInfo->sibling != 0)
+ {
+ /* Reject a sibling earlier than the current die,
+ or past the end of the .debug section. This
+ is to stop fuzzers generating endless loops. */
+ size_t next_off = aDiePtrEnd - stash->debug_section;
+ size_t sec_size = (stash->debug_section_end
+ - stash->debug_section);
+ if (aDieInfo->sibling < next_off
+ || aDieInfo->sibling > sec_size)
+ return false;
+ }
+ }
else if (attr == AT_stmt_list)
{
aDieInfo->stmt_list_offset = bfd_get_32 (abfd, xptr);
@@ -384,8 +399,7 @@ parse_functions_in_unit (struct dwarf1_debug* stash, struct dwarf1_unit* aUnit)
{
struct die_info eachDieInfo;
- if (! parse_die (stash->abfd, &eachDieInfo, eachDie,
- stash->debug_section_end))
+ if (!parse_die (stash, &eachDieInfo, eachDie))
return false;
if (eachDieInfo.tag == TAG_global_subroutine
@@ -550,8 +564,7 @@ _bfd_dwarf1_find_nearest_line (bfd *abfd,
{
struct die_info aDieInfo;
- if (! parse_die (stash->abfd, &aDieInfo, stash->currentDie,
- stash->debug_section_end))
+ if (!parse_die (stash, &aDieInfo, stash->currentDie))
return false;
if (aDieInfo.tag == TAG_compile_unit)
--
Alan Modra