diff options
| author | nagachika <nagachika@ruby-lang.org> | 2025-03-29 15:21:40 +0900 |
|---|---|---|
| committer | nagachika <nagachika@ruby-lang.org> | 2025-03-29 15:21:40 +0900 |
| commit | d2eda78e4091a99c1a387d43967af5794d8eac70 (patch) | |
| tree | 982247e4bf7486a5daac8c0db7b1f5526fc54839 | |
| parent | c1319d7e406ddf3f0da487296f1c4c50d90496ad (diff) | |
merge revision(s) 9459bedd84d479bb1d7d3d40bada1cecb4701c37: [Backport #19841]
[Bug #19841] Refine error on marshaling recursive USERDEF
| -rw-r--r-- | marshal.c | 17 | ||||
| -rw-r--r-- | test/ruby/test_marshal.rb | 9 | ||||
| -rw-r--r-- | version.h | 2 |
3 files changed, 27 insertions, 1 deletions
@@ -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 { @@ -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" |
