Skip to content

Commit ae2fcdc

Browse files
nobuk0kubun
authored andcommitted
[Bug #19841] Refine error on marshaling recursive USERDEF
1 parent 0e416fa commit ae2fcdc

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

marshal.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ struct dump_arg {
156156
st_table *data;
157157
st_table *compat_tbl;
158158
st_table *encodings;
159+
st_table *userdefs;
159160
st_index_t num_entries;
160161
};
161162

@@ -204,6 +205,7 @@ mark_dump_arg(void *ptr)
204205
rb_mark_set(p->symbols);
205206
rb_mark_set(p->data);
206207
rb_mark_hash(p->compat_tbl);
208+
rb_mark_set(p->userdefs);
207209
rb_gc_mark(p->str);
208210
}
209211

@@ -221,6 +223,7 @@ memsize_dump_arg(const void *ptr)
221223
if (p->symbols) memsize += rb_st_memsize(p->symbols);
222224
if (p->data) memsize += rb_st_memsize(p->data);
223225
if (p->compat_tbl) memsize += rb_st_memsize(p->compat_tbl);
226+
if (p->userdefs) memsize += rb_st_memsize(p->userdefs);
224227
if (p->encodings) memsize += rb_st_memsize(p->encodings);
225228
return memsize;
226229
}
@@ -887,6 +890,9 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
887890
st_index_t hasiv2;
888891
VALUE encname2;
889892

893+
if (arg->userdefs && st_is_member(arg->userdefs, (st_data_t)obj)) {
894+
rb_raise(rb_eRuntimeError, "can't dump recursive object using _dump()");
895+
}
890896
v = INT2NUM(limit);
891897
v = dump_funcall(arg, obj, s_dump, 1, &v);
892898
if (!RB_TYPE_P(v, T_STRING)) {
@@ -903,7 +909,13 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
903909
w_class(TYPE_USERDEF, obj, arg, FALSE);
904910
w_bytes(RSTRING_PTR(v), RSTRING_LEN(v), arg);
905911
if (hasiv) {
912+
st_data_t userdefs = (st_data_t)obj;
913+
if (!arg->userdefs) {
914+
arg->userdefs = rb_init_identtable();
915+
}
916+
st_add_direct(arg->userdefs, userdefs, 0);
906917
w_ivar(hasiv, ivobj, encname, &c_arg);
918+
st_delete(arg->userdefs, &userdefs, NULL);
907919
}
908920
w_remember(obj, arg);
909921
return;
@@ -1110,6 +1122,10 @@ clear_dump_arg(struct dump_arg *arg)
11101122
st_free_table(arg->encodings);
11111123
arg->encodings = 0;
11121124
}
1125+
if (arg->userdefs) {
1126+
st_free_table(arg->userdefs);
1127+
arg->userdefs = 0;
1128+
}
11131129
}
11141130

11151131
NORETURN(static inline void io_needed(void));
@@ -1187,6 +1203,7 @@ rb_marshal_dump_limited(VALUE obj, VALUE port, int limit)
11871203
arg->num_entries = 0;
11881204
arg->compat_tbl = 0;
11891205
arg->encodings = 0;
1206+
arg->userdefs = 0;
11901207
arg->str = rb_str_buf_new(0);
11911208
if (!NIL_P(port)) {
11921209
if (!rb_respond_to(port, s_write)) {

test/ruby/test_marshal.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,15 @@ class << c
676676
end
677677
end
678678

679+
def test_recursive_userdef
680+
t = Time.utc(0)
681+
str = "b".b
682+
t.instance_eval {@v = t}
683+
assert_raise_with_message(RuntimeError, /recursive\b.*\b_dump/) do
684+
Marshal.dump(t)
685+
end
686+
end
687+
679688
def test_unloadable_usrmarshal
680689
c = eval("class UsrMarshal\u{23F0 23F3}<Time;self;end")
681690
c.class_eval {

0 commit comments

Comments
 (0)