Skip to content

Commit 2d86e79

Browse files
committedNov 1, 2022
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 #7: 0x00000001006fbca0 miniruby`rb_vm_pop_frame(ec=0x00000001048038d0) at vm_insnhelper.c:431:5 frame #8: 0x00000001007d6420 miniruby`vm_call0_cfunc_with_frame(ec=0x00000001048038d0, calling=0x000000016fdcc6a0, argv=0x0000000000000000) at vm_eval.c:153:9 frame #9: 0x00000001007d44cc miniruby`vm_call0_cfunc(ec=0x00000001048038d0, calling=0x000000016fdcc6a0, argv=0x0000000000000000) at vm_eval.c:164:12 frame #10: 0x0000000100766e80 miniruby`vm_call0_body(ec=0x00000001048038d0, calling=0x000000016fdcc6a0, argv=0x0000000000000000) at vm_eval.c:210:15 frame #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 #12: 0x0000000100769e48 miniruby`rb_funcallv_scope(recv=0x000000010d6b49d8, mid=2769, argc=0, argv=0x0000000000000000, scope=CALL_FCALL) at vm_eval.c:1051:16 frame #13: 0x0000000100760a54 miniruby`rb_funcallv(recv=0x000000010d6b49d8, mid=2769, argc=0, argv=0x0000000000000000) at vm_eval.c:1066:12 frame #14: 0x000000010037513c miniruby`rb_inspect(obj=0x000000010d6b49d8) at object.c:633:34 frame #15: 0x000000010002c950 miniruby`inspect_ary(ary=0x000000010d6b4938, dummy=0x0000000000000000, recur=0) at array.c:3091:13 frame #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 #17: 0x00000001006412fc miniruby`rb_exec_recursive(func=(miniruby`inspect_ary at array.c:3084), obj=0x000000010d6b4938, arg=0x0000000000000000) at thread.c:5205:12 frame #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 2d86e79

File tree

1 file changed

+36
-7
lines changed

1 file changed

+36
-7
lines changed
 

‎variable.c

+36-7
Original file line numberDiff line numberDiff line change
@@ -1566,22 +1566,41 @@ 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+
struct iv_itr_data {
1570+
VALUE obj;
1571+
struct gen_ivtbl * ivtbl;
1572+
st_data_t arg;
1573+
};
1574+
15691575
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)
1576+
iterate_over_shapes_with_callback(rb_shape_t *shape, rb_ivar_foreach_callback_func *callback, struct iv_itr_data * itr_data)
15711577
{
15721578
switch ((enum shape_type)shape->type) {
15731579
case SHAPE_ROOT:
15741580
return;
15751581
case SHAPE_IVAR:
1576-
iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), iv_list, callback, arg);
1582+
iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), callback, itr_data);
1583+
VALUE * iv_list;
1584+
switch(BUILTIN_TYPE(itr_data->obj)) {
1585+
case T_OBJECT:
1586+
iv_list = ROBJECT_IVPTR(itr_data->obj);
1587+
break;
1588+
case T_CLASS:
1589+
case T_MODULE:
1590+
iv_list = RCLASS_IVPTR(itr_data->obj);
1591+
break;
1592+
default:
1593+
iv_list = itr_data->ivtbl->ivptr;
1594+
break;
1595+
}
15771596
VALUE val = iv_list[shape->next_iv_index - 1];
15781597
if (val != Qundef) {
1579-
callback(shape->edge_name, val, arg);
1598+
callback(shape->edge_name, val, itr_data->arg);
15801599
}
15811600
return;
15821601
case SHAPE_IVAR_UNDEF:
15831602
case SHAPE_FROZEN:
1584-
iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), iv_list, callback, arg);
1603+
iterate_over_shapes_with_callback(rb_shape_get_shape_by_id(shape->parent_id), callback, itr_data);
15851604
return;
15861605
}
15871606
}
@@ -1590,7 +1609,10 @@ static void
15901609
obj_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
15911610
{
15921611
rb_shape_t* shape = rb_shape_get_shape(obj);
1593-
iterate_over_shapes_with_callback(shape, ROBJECT_IVPTR(obj), func, arg);
1612+
struct iv_itr_data itr_data;
1613+
itr_data.obj = obj;
1614+
itr_data.arg = arg;
1615+
iterate_over_shapes_with_callback(shape, func, &itr_data);
15941616
}
15951617

15961618
static void
@@ -1600,7 +1622,11 @@ gen_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
16001622
struct gen_ivtbl *ivtbl;
16011623
if (!rb_gen_ivtbl_get(obj, 0, &ivtbl)) return;
16021624

1603-
iterate_over_shapes_with_callback(shape, ivtbl->ivptr, func, arg);
1625+
struct iv_itr_data itr_data;
1626+
itr_data.obj = obj;
1627+
itr_data.ivtbl = ivtbl;
1628+
itr_data.arg = arg;
1629+
iterate_over_shapes_with_callback(shape, func, &itr_data);
16041630
}
16051631

16061632
static void
@@ -1609,7 +1635,10 @@ class_ivar_each(VALUE obj, rb_ivar_foreach_callback_func *func, st_data_t arg)
16091635
RUBY_ASSERT(RB_TYPE_P(obj, T_CLASS) || RB_TYPE_P(obj, T_MODULE));
16101636

16111637
rb_shape_t* shape = rb_shape_get_shape(obj);
1612-
iterate_over_shapes_with_callback(shape, RCLASS_IVPTR(obj), func, arg);
1638+
struct iv_itr_data itr_data;
1639+
itr_data.obj = obj;
1640+
itr_data.arg = arg;
1641+
iterate_over_shapes_with_callback(shape, func, &itr_data);
16131642
}
16141643

16151644
void

0 commit comments

Comments
 (0)
Failed to load comments.