From 7dfd560df8d0ade6016a856b3c2d336c0f9ccd4f Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 2 May 2026 11:27:01 +0900 Subject: [PATCH] mruby-io: cap puts recursion depth to prevent C stack overflow io_puts_ary recursed unconditionally on nested arrays. For cyclic arrays (a = []; a << a; puts a) or pathologically deep arrays, this caused a C stack overflow. Add a depth cap (IO_PUTS_MAX_DEPTH = 16); on overflow, write "[...]\n" and return, matching CRuby's behavior on cycles. The pattern mirrors mruby-set's MAX_NESTED_DEPTH for the same problem shape (pure C recursion not dispatched as a Ruby method). Reported by OSS-Fuzz (clusterfuzz testcase 6233530857488384). Co-authored-by: Claude --- mrbgems/mruby-io/src/io.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/mrbgems/mruby-io/src/io.c b/mrbgems/mruby-io/src/io.c index 87e3d6f31..451e360d7 100644 --- a/mrbgems/mruby-io/src/io.c +++ b/mrbgems/mruby-io/src/io.c @@ -992,10 +992,20 @@ io_puts_str(mrb_state *mrb, int fd, mrb_value str) } } +/* Maximum nesting depth for puts with arrays; guards against cyclic and + pathologically deep arrays causing C stack overflow. */ +#define IO_PUTS_MAX_DEPTH 16 + /* Recursive helper for puts with arrays */ static void -io_puts_ary(mrb_state *mrb, int fd, mrb_value ary) +io_puts_ary(mrb_state *mrb, int fd, mrb_value ary, int depth) { + if (depth >= IO_PUTS_MAX_DEPTH) { + mrb_value mark = mrb_str_new_lit(mrb, "[...]\n"); + fd_write(mrb, fd, mark); + return; + } + mrb_int len = RARRAY_LEN(ary); if (len == 0) { @@ -1008,7 +1018,7 @@ io_puts_ary(mrb_state *mrb, int fd, mrb_value ary) for (mrb_int i = 0; i < len; i++) { mrb_value elem = RARRAY_PTR(ary)[i]; if (mrb_array_p(elem)) { - io_puts_ary(mrb, fd, elem); /* Recursive call for nested arrays */ + io_puts_ary(mrb, fd, elem, depth + 1); } else { io_puts_str(mrb, fd, elem); @@ -1040,7 +1050,7 @@ io_puts(mrb_state *mrb, mrb_value io) for (mrb_int i = 0; i < argc; i++) { mrb_value arg = argv[i]; if (mrb_array_p(arg)) { - io_puts_ary(mrb, fd, arg); + io_puts_ary(mrb, fd, arg, 0); } else { io_puts_str(mrb, fd, arg);