summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornagachika <nagachika@ruby-lang.org>2025-03-29 15:21:40 +0900
committernagachika <nagachika@ruby-lang.org>2025-03-29 15:21:40 +0900
commitd2eda78e4091a99c1a387d43967af5794d8eac70 (patch)
tree982247e4bf7486a5daac8c0db7b1f5526fc54839
parentc1319d7e406ddf3f0da487296f1c4c50d90496ad (diff)
merge revision(s) 9459bedd84d479bb1d7d3d40bada1cecb4701c37: [Backport #19841]
[Bug #19841] Refine error on marshaling recursive USERDEF
-rw-r--r--marshal.c17
-rw-r--r--test/ruby/test_marshal.rb9
-rw-r--r--version.h2
3 files changed, 27 insertions, 1 deletions
diff --git a/marshal.c b/marshal.c
index 1cd71efd54..6cb636f3ff 100644
--- a/marshal.c
+++ b/marshal.c
@@ -173,6 +173,7 @@ struct dump_arg {
st_table *data;
st_table *compat_tbl;
st_table *encodings;
+ st_table *userdefs;
st_index_t num_entries;
};
@@ -221,6 +222,7 @@ mark_dump_arg(void *ptr)
rb_mark_set(p->symbols);
rb_mark_set(p->data);
rb_mark_hash(p->compat_tbl);
+ rb_mark_set(p->userdefs);
rb_gc_mark(p->str);
}
@@ -238,6 +240,7 @@ memsize_dump_arg(const void *ptr)
if (p->symbols) memsize += rb_st_memsize(p->symbols);
if (p->data) memsize += rb_st_memsize(p->data);
if (p->compat_tbl) memsize += rb_st_memsize(p->compat_tbl);
+ if (p->userdefs) memsize += rb_st_memsize(p->userdefs);
if (p->encodings) memsize += rb_st_memsize(p->encodings);
return memsize;
}
@@ -904,6 +907,9 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
st_index_t hasiv2;
VALUE encname2;
+ if (arg->userdefs && st_is_member(arg->userdefs, (st_data_t)obj)) {
+ rb_raise(rb_eRuntimeError, "can't dump recursive object using _dump()");
+ }
v = INT2NUM(limit);
v = dump_funcall(arg, obj, s_dump, 1, &v);
if (!RB_TYPE_P(v, T_STRING)) {
@@ -920,7 +926,13 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
w_class(TYPE_USERDEF, obj, arg, FALSE);
w_bytes(RSTRING_PTR(v), RSTRING_LEN(v), arg);
if (hasiv) {
+ st_data_t userdefs = (st_data_t)obj;
+ if (!arg->userdefs) {
+ arg->userdefs = rb_init_identtable();
+ }
+ st_add_direct(arg->userdefs, userdefs, 0);
w_ivar(hasiv, ivobj, encname, &c_arg);
+ st_delete(arg->userdefs, &userdefs, NULL);
}
w_remember(obj, arg);
return;
@@ -1127,6 +1139,10 @@ clear_dump_arg(struct dump_arg *arg)
st_free_table(arg->encodings);
arg->encodings = 0;
}
+ if (arg->userdefs) {
+ st_free_table(arg->userdefs);
+ arg->userdefs = 0;
+ }
}
NORETURN(static inline void io_needed(void));
@@ -1204,6 +1220,7 @@ rb_marshal_dump_limited(VALUE obj, VALUE port, int limit)
arg->num_entries = 0;
arg->compat_tbl = 0;
arg->encodings = 0;
+ arg->userdefs = 0;
arg->str = rb_str_buf_new(0);
if (!NIL_P(port)) {
if (!rb_respond_to(port, s_write)) {
diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb
index 13645e3aa8..c27de3524e 100644
--- a/test/ruby/test_marshal.rb
+++ b/test/ruby/test_marshal.rb
@@ -668,6 +668,15 @@ class TestMarshal < Test::Unit::TestCase
end
end
+ def test_recursive_userdef
+ t = Time.utc(0)
+ str = "b".b
+ t.instance_eval {@v = t}
+ assert_raise_with_message(RuntimeError, /recursive\b.*\b_dump/) do
+ Marshal.dump(t)
+ end
+ end
+
def test_unloadable_usrmarshal
c = eval("class UsrMarshal\u{23F0 23F3}<Time;self;end")
c.class_eval {
diff --git a/version.h b/version.h
index 63f382a26d..4a81276a08 100644
--- a/version.h
+++ b/version.h
@@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 7
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
-#define RUBY_PATCHLEVEL 135
+#define RUBY_PATCHLEVEL 136
#include "ruby/version.h"
#include "ruby/internal/abi.h"