From 0ba48a2a5b261485bcfc19b67d1532cf7c80a7cf Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 17 Jan 2026 13:00:45 +0900 Subject: [PATCH] mruby-set: fix memory leak in khash rebuild using mrb_protect_error() wrap hash and eql callbacks with mrb_protect_error() to catch exceptions during khash table rebuild. when an exception occurs (e.g., SystemStackError from infinite recursion), return a safe default value and store the exception in mrb->exc for later processing. this prevents memory leaks from orphaned allocations when exceptions propagate through khash rebuild. Co-authored-by: Claude --- mrbgems/mruby-set/src/set.c | 40 +++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-set/src/set.c b/mrbgems/mruby-set/src/set.c index a8a02d0df..716dcc364 100644 --- a/mrbgems/mruby-set/src/set.c +++ b/mrbgems/mruby-set/src/set.c @@ -14,22 +14,58 @@ #include #include #include +#include #include /* Use khash.h for set implementation - set mode (no values, only keys) */ KHASH_DECLARE(set_val, mrb_value, char, FALSE) /* FALSE = set mode */ +/* Helper for protected hash computation */ +static mrb_value +kset_hash_body(mrb_state *mrb, void *data) +{ + mrb_value *key = (mrb_value*)data; + return mrb_int_value(mrb, mrb_obj_hash_code(mrb, *key)); +} + +/* Helper for protected equality check */ +struct kset_eql_data { + mrb_value a; + mrb_value b; +}; + +static mrb_value +kset_eql_body(mrb_state *mrb, void *data) +{ + struct kset_eql_data *d = (struct kset_eql_data*)data; + return mrb_bool_value(mrb_eql(mrb, d->a, d->b)); +} + /* Hash and equality functions for mrb_value keys */ +/* These use mrb_protect_error to catch exceptions and prevent leaks in khash rebuild */ static inline khint_t kset_hash_value(mrb_state *mrb, mrb_value key) { - return (khint_t)mrb_obj_hash_code(mrb, key); + mrb_bool error; + mrb_value result = mrb_protect_error(mrb, kset_hash_body, &key, &error); + if (error) { + mrb->exc = mrb_obj_ptr(result); /* Store exception to raise later */ + return 0; /* Return default hash value */ + } + return (khint_t)mrb_integer(result); } static inline mrb_bool kset_equal_value(mrb_state *mrb, mrb_value a, mrb_value b) { - return mrb_eql(mrb, a, b); + struct kset_eql_data data = { a, b }; + mrb_bool error; + mrb_value result = mrb_protect_error(mrb, kset_eql_body, &data, &error); + if (error) { + mrb->exc = mrb_obj_ptr(result); /* Store exception to raise later */ + return FALSE; /* Return not-equal */ + } + return mrb_test(result); } KHASH_DEFINE(set_val, mrb_value, char, FALSE, kset_hash_value, kset_equal_value)