authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-20 14:44:10+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:56+01:00
logb0f222777c38088d90041ba1f28bfb1341cc76c6
tree57a538f00e2bd676fe7bf9bb37658f0e59cc941d
parentc41bf996848a32c60e6f1dac89769d33a1b83178
signaturelock-open Commit is signed but in an unrecognized format.

std.debug: cap total stack trace frames

...just in case there is broken debug info and/or bad values on the stack, either of which could cause stack unwinding to potentially loop forever.

1 files changed, 18 insertions(+), 0 deletions(-)

lib/std/debug.zig+18
...@@ -571,12 +571,19 @@ pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize)...@@ -571,12 +571,19 @@ pub fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize)
571 var it = StackIterator.init(options.context) catch return empty_trace;571 var it = StackIterator.init(options.context) catch return empty_trace;
572 defer it.deinit();572 defer it.deinit();
573 if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace;573 if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace;
574 var total_frames: usize = 0;
574 var frame_idx: usize = 0;575 var frame_idx: usize = 0;
575 var wait_for = options.first_address;576 var wait_for = options.first_address;
576 while (true) switch (it.next()) {577 while (true) switch (it.next()) {
577 .switch_to_fp => if (!it.stratOk(options.allow_unsafe_unwind)) break,578 .switch_to_fp => if (!it.stratOk(options.allow_unsafe_unwind)) break,
578 .end => break,579 .end => break,
579 .frame => |ret_addr| {580 .frame => |ret_addr| {
581 if (total_frames > 10_000) {
582 // Limit the number of frames in case of (e.g.) broken debug information which is
583 // getting unwinding stuck in a loop.
584 break;
585 }
586 total_frames += 1;
580 if (wait_for) |target| {587 if (wait_for) |target| {
581 if (ret_addr != target) continue;588 if (ret_addr != target) continue;
582 wait_for = null;589 wait_for = null;
...@@ -624,6 +631,7 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_...@@ -624,6 +631,7 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_
624 tty_config.setColor(writer, .reset) catch {};631 tty_config.setColor(writer, .reset) catch {};
625 return;632 return;
626 }633 }
634 var total_frames: usize = 0;
627 var wait_for = options.first_address;635 var wait_for = options.first_address;
628 var printed_any_frame = false;636 var printed_any_frame = false;
629 while (true) switch (it.next()) {637 while (true) switch (it.next()) {
...@@ -657,6 +665,16 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_...@@ -657,6 +665,16 @@ pub fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Writer, tty_
657 },665 },
658 .end => break,666 .end => break,
659 .frame => |ret_addr| {667 .frame => |ret_addr| {
668 if (total_frames > 10_000) {
669 tty_config.setColor(writer, .dim) catch {};
670 try writer.print(
671 "Stopping trace after {d} frames (large frame count may indicate broken debug info)\n",
672 .{total_frames},
673 );
674 tty_config.setColor(writer, .reset) catch {};
675 return;
676 }
677 total_frames += 1;
660 if (wait_for) |target| {678 if (wait_for) |target| {
661 if (ret_addr != target) continue;679 if (ret_addr != target) continue;
662 wait_for = null;680 wait_for = null;