From: Ronald Hecht Date: Mon, 15 Jun 2026 07:04:01 +0000 (+0200) Subject: gdb: z80: Fix endless loop in frame unwinder and validate saved register types X-Git-Tag: binutils-2_47~47 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=32a7e94f84a3df0bcd08a2835e2c316f5ed07f77;p=thirdparty%2Fbinutils-gdb.git gdb: z80: Fix endless loop in frame unwinder and validate saved register types In z80_frame_unwind_cache, the loop scanning for the frame base pointer (for (;; ++sp)) could run into an endless loop or scan too far if the termination condition (sp < this_base) was not met due to corrupted or unexpected stack layouts. This patch introduces a loop_count to limit the scan to a maximum of 2 * addr_len iterations. Additionally, when iterating through saved registers to adjust their offsets into concrete addresses, the code now explicitly checks if the register actually holds an address using is_addr() before calling addr(). This prevents potential assertions or undefined behavior for registers that do not contain valid address data. gdb/ChangeLog: * z80-tdep.c (z80_frame_unwind_cache): Limit stack scanning to 2 * addr_len iterations to prevent endless loops. Check is_addr() before adjusting saved register addresses. Approved-By: Kevin Buettner --- diff --git a/gdb/z80-tdep.c b/gdb/z80-tdep.c index 7359f9bcfb6..6b995e74d9f 100644 --- a/gdb/z80-tdep.c +++ b/gdb/z80-tdep.c @@ -595,17 +595,27 @@ z80_frame_unwind_cache (const frame_info_ptr &this_frame, { CORE_ADDR addr; CORE_ADDR sp; - CORE_ADDR sp_mask = (1 << gdbarch_ptr_bit(gdbarch)) - 1; + CORE_ADDR addr_space_max = (1 << gdbarch_ptr_bit(gdbarch)) - 1; enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + int loop_count = 0; /* Assume that the FP is this frame's SP but with that pushed stack space added back. */ this_base = get_frame_register_unsigned (this_frame, Z80_SP_REGNUM); sp = this_base + info->size; for (;; ++sp) { - sp &= sp_mask; - if (sp < this_base) - { /* overflow, looks like end of stack */ + /* Limit the scan to 2 * addr_len iterations. If the unwinder's + frame size calculation is slightly off (e.g. due to unpopped + arguments or temporary pushes), the return address might be + hidden a few bytes deeper. Scanning up to 2 * addr_len bytes + comfortably covers two misplaced pointer-sized values. + Scanning further drastically increases the risk of false + positives: we might wander into the caller's local variables, + hit a random CALL or conditional CALL opcode, and generate a + corrupted backtrace. It also prevents massive slow-downs on + remote serial targets if the stack is severely corrupted. */ + if (++loop_count > 2 * addr_len || sp > addr_space_max) + { /* Limit reached or end of address space, assume end of stack. */ sp = this_base + info->size; break; } @@ -644,9 +654,14 @@ z80_frame_unwind_cache (const frame_info_ptr &this_frame, /* Adjust all the saved registers so that they contain addresses and not offsets. */ for (i = 0; i < gdbarch_num_regs (gdbarch) - 1; i++) - if (info->saved_regs[i].addr () > 0) - info->saved_regs[i].set_addr - (info->prev_sp - info->saved_regs[i].addr () * addr_len); + { + if (info->saved_regs[i].is_addr ()) + { + if (info->saved_regs[i].addr () > 0) + info->saved_regs[i].set_addr + (info->prev_sp - info->saved_regs[i].addr () * addr_len); + } + } /* Except for the startup code, the return PC is always saved on the stack and is at the base of the frame. */