8000 Always lookup IV buffers when iterating · tenderlove/ruby@366e6fc · GitHub
[go: up one dir, main page]

Skip to content

Commit 366e6fc

Browse files
committed
Always lookup IV buffers when iterating
Always look up instance variable buffers when iterating. It is possible for the instance variable buffer to change out from under the object during iteration, so we cannot cache the buffer on the stack. In the case of Bug #19095, the transient heap moved the buffer during iteration: ``` Watchpoint 1 hit: old value: 0x0000000107c00df8 new value: 0x00000001032743c0 Process 31720 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1 frame #0: 0x00000001006e5178 miniruby`rb_obj_transient_heap_evacuate(obj=0x000000010d6b94b0, promote=1) at variable.c:1361:5 1358 } 1359 MEMCPY(new_ptr, old_ptr, VALUE, len); 1360 ROBJECT(obj)->as.heap.ivptr = new_ptr; -> 1361 } 1362 } 1363 #endif 1364 miniruby`rb_obj_transient_heap_evacuate: -> 0x1006e5178 <+328>: b 0x1006e517c ; <+332> at variable.c:1362:1 0x1006e517c <+332>: ldp x29, x30, [sp, #0x50] 0x1006e5180 <+336>: add sp, sp, #0x60 0x1006e5184 <+340>: ret Target 0: (miniruby) stopped. (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1 * frame #0: 0x00000001006e5178 miniruby`rb_obj_transient_heap_evacuate(obj=0x000000010d6b94b0, promote=1) at variable.c:1361:5 frame #1: 0x00000001006cb150 miniruby`transient_heap_block_evacuate(theap=0x0000000100b196c0, block=0x0000000107c00000) at transient_heap.c:734:17 frame #2: 0x00000001006c854c miniruby`transient_heap_evacuate(dmy=0x0000000000000000) at transient_heap.c:808:17 frame #3: 0x00000001007fe6c0 miniruby`rb_postponed_job_flush(vm=0x0000000104402900) at vm_trace.c:1773:21 frame #4: 0x0000000100637a84 miniruby`rb_threadptr_execute_interrupts(th=0x0000000103803bc0, blocking_timing=0) at thread.c:2316:13 frame #5: 0x000000010078b730 miniruby`rb_vm_check_ints(ec=0x00000001048038d0) at vm_core.h:2025:9 frame #6: 0x00000001006fbd10 miniruby`vm_pop_frame(ec=0x00000001048038d0, cfp=0x0000000104a04440, ep=0x0000000104904a28) at vm_insnhelper.c:422:5 frame ruby#7: 0x00000001006fbca0 miniruby`rb_vm_pop_frame(ec=0x00000001048038d0) at vm_insnhelper.c:431:5 frame ruby#8: 0x00000001007d6420 miniruby`vm_call0_cfunc_with_frame(ec=0x00000001048038d0, calling=0x000000016fdcc6a0, argv=0x0000000000000000) at vm_eval.c:153:9 frame ruby#9: 0x00000001007d44cc miniruby`vm_call0_cfunc(ec=0x00000001048038d0, calling=0x000000016fdcc6a0, argv=0x0000000000000000) at vm_eval.c:164:12 frame ruby#10: 0x0000000100766e80 miniruby`vm_call0_body(ec=0x00000001048038d0, calling=0x000000016fdcc6a0, argv=0x0000000000000000) at vm_eval.c:210:15 frame ruby#11: 0x00000001007d76f0 miniruby`vm_call0_cc(ec=0x00000001048038d0, recv=0x000000010d6b49d8, id=2769, argc=0, argv=0x0000000000000000, cc=0x000000010d6b2e58, kw_splat=0) at vm_eval.c:87:12 frame ruby#12: 0x0000000100769e48 miniruby`rb_funcallv_scope(recv=0x000000010d6b49d8, mid=2769, argc=0, argv=0x0000000000000000, scope=CALL_FCALL) at vm_eval.c:1051:16 frame ruby#13: 0x0000000100760a54 miniruby`rb_funcallv(recv=0x000000010d6b49d8, mid=2769, argc=0, argv=0x0000000000000000) at vm_eval.c:1066:12 frame ruby#14: 0x000000010037513c miniruby`rb_inspect(obj=0x000000010d6b49d8) at object.c:633:34 frame ruby#15: 0x000000010002c950 miniruby`inspect_ary(ary=0x000000010d6b4938, dummy=0x0000000000000000, recur=0) at array.c:3091:13 frame ruby#16: 0x0000000100642020 miniruby`exec_recursive(func=(miniruby`inspect_ary at array.c:3084), obj=0x000000010d6b4938, pairid=0x0000000000000000, arg=0x0000000000000000, outer=0, mid=2769) at thread.c:5177:23 frame ruby#17: 0x00000001006412fc miniruby`rb_exec_recursive(func=(miniruby`inspect_ary at array.c:3084), obj=0x000000010d6b4938, arg=0x0000000000000000) at thread.c:5205:12 frame ruby#18: 0x00000001000127f0 miniruby`rb_ary_inspect(ary=0x000000010d6b4938) at array.c:3117:12 ``` In general though, any calls back out to the interpreter could change the IV buffer, so it's not safe to cache. [Bug #19095]
1 parent 02f1554 commit 366e6fc

File tree

1 file changed

+44
-7
lines changed

1 file changed

+44
-7
lines changed

variable.c

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,22 +1566,47 @@ rb_ivar_defined(VALUE obj, ID id)
15661566
typedef int rb_ivar_foreach_callback_func(ID key, VALUE val, st_data_t arg);
15671567
st_data_t rb_st_nth_key(st_table *tab, st_index_t index);
15681568

1569+
enum iv_iter_type {
1570+
IV_ITER_OBJECT,
1571+
IV_ITER_CLASS,
1572+
IV_ITER_GENERIC,
1573+
};
1574+
1575+
struct iv_itr_data {
1576+
enum iv_iter_type type;
1577+
VALUE obj;
1578+
struct gen_ivtbl * ivtbl;
1579+
st_data_t arg;
1580+
};
1581+
15691582
static void
1570-
iterate_over_shapes_with_callback(rb_shape_t *shape, VALUE* iv_list, rb_ivar_foreach_callback_func *callback, st_data_t arg)
1583+
iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_func *callback, struct iv_itr_data * itr_data)
15711584
{
15721585
switch ((enum shape_type)shape->type) {
15731586
case SHAPE_ROOT:
15741587
return;
15751588
case SHAPE_IVAR:
1576-
iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), iv_list, callback, arg);
1589+
iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), callback, itr_data);
1590+
VALUE * iv_list;
1591+
switch(itr_data->type) {
1592+
case IV_ITER_OBJECT:
1593+
iv_list = ROBJECT_IVPTR(itr_data->obj);
1594+
break;
1595+
case IV_ITER_CLASS:
1596+
iv_list = RCLASS_IVPTR(itr_data->obj);
1597+
break;
1598+
case IV_ITER_GENERIC:
1599+
iv_list = itr_data->ivtbl->ivptr;
1600+
break;
1601+
}
15771602
VALUE val = iv_list[shape->next_iv_index - 1];
15781603
if (val != Qundef) {
1579-
callback(shape->edge_name, val, arg);
1604+
callback(shape->edge_name, val, itr_data->arg);
15801605
}
15811606
return;
15821607
case SHAPE_IVAR_UNDEF:
15831608
case SHAPE_FROZEN:
1584-
iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), iv_list, callback, arg);
1609+
iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), callback, itr_data);
15851610
return;
15861611
}
15871612
}
@@ -1590,7 +1615,11 @@ static void
15901615
obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
15911616
{
15921617
rb_shape_t* shape = rb_shape_get_shape(obj);
1593-
iterate_over_shapes_with_callback(shape, ROBJECT_IVPTR(obj), func, arg);
1618+
struct iv_itr_data itr_data;
1619+
itr_data.type = IV_ITER_OBJECT;
1620+
itr_data.obj = obj;
1621+
itr_data.arg = arg;
1622+
iterate_over_shapes_with_callback(shape, func, &itr_data);
15941623
}
15951624

15961625
static void
@@ -1600,7 +1629,11 @@ gen_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
16001629
struct gen_ivtbl *ivtbl;
16011630
if (!rb_gen_ivtbl_get(obj, 0, &ivtbl)) return;
16021631

1603-
iterate_over_shapes_with_callback(shape, ivtbl->ivptr, func, arg);
1632+
struct iv_itr_data itr_data;
1633+
itr_data.type = IV_ITER_GENERIC;
1634+
itr_data.ivtbl = ivtbl;
1635+
itr_data.arg = arg;
1636+
iterate_over_shapes_with_callback(shape, func, &itr_data);
16041637
}
16051638

16061639
static void
@@ -1609,7 +1642,11 @@ class_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
16091642
RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
16101643

16111644
rb_shape_t* shape = rb_shape_get_shape(obj);
1612-
iterate_over_shapes_with_callback(shape, RCLASS_IVPTR(obj), func, arg);
1645+
struct iv_itr_data itr_data;
1646+
itr_data.type = IV_ITER_CLASS;
1647+
itr_data.obj = obj;
1648+
itr_data.arg = arg;
1649+
iterate_over_shapes_with_callback(shape, func, &itr_data);
16131650
}
16141651

16151652
void

0 commit comments

Comments
 (0)
0