diff options
author | Peter Zhu <peter@peterzhu.ca> | 2023-11-30 10:51:51 -0500 |
---|---|---|
committer | Peter Zhu <peter@peterzhu.ca> | 2023-12-01 17:13:56 -0500 |
commit | 0aed37b973c66e9ff071a1f46944cb0c6151d2dc (patch) | |
tree | e6f785c47230ebeb7ad341e61495bfaa73b1c32e /vm_insnhelper.c | |
parent | 492c82cb417a92d1941f10b52e77ec0c4b2cc8a6 (diff) | |
download | ruby-0aed37b973c66e9ff071a1f46944cb0c6151d2dc.tar.gz |
Make expandarray compaction safe
The expandarray instruction can allocate an array, which can trigger
a GC compaction. However, since it does not increment the sp until the
end of the instruction, the objects it places on the stack are not
marked or reference updated by the GC, which can cause the objects to
move which leaves broken or incorrect objects on the stack.
This commit changes the instruction to be handles_sp so the sp is
incremented inside of the instruction right after the object is written
on the stack.
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r-- | vm_insnhelper.c | 51 |
1 files changed, 28 insertions, 23 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 9352820b82..8e1ac27c51 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1866,11 +1866,9 @@ rb_vm_throw(const rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_nu } static inline void -vm_expandarray(VALUE *sp, VALUE ary, rb_num_t num, int flag) +vm_expandarray(struct rb_control_frame_struct *cfp, VALUE ary, rb_num_t num, int flag) { int is_splat = flag & 0x01; - rb_num_t space_size = num + is_splat; - VALUE *base = sp - 1; const VALUE *ptr; rb_num_t len; const VALUE obj = ary; @@ -1885,7 +1883,7 @@ vm_expandarray(VALUE *sp, VALUE ary, rb_num_t num, int flag) len = (rb_num_t)RARRAY_LEN(ary); } - if (space_size == 0) { + if (num + is_splat == 0) { /* no space left on stack */ } else if (flag & 0x02) { @@ -1893,41 +1891,48 @@ vm_expandarray(VALUE *sp, VALUE ary, rb_num_t num, int flag) rb_num_t i = 0, j; if (len < num) { - for (i=0; i<num-len; i++) { - *base++ = Qnil; + for (i = 0; i < num - len; i++) { + *cfp->sp++ = Qnil; } } - for (j=0; i<num; i++, j++) { + + for (j = 0; i < num; i++, j++) { VALUE v = ptr[len - j - 1]; - *base++ = v; + *cfp->sp++ = v; } + if (is_splat) { - *base = rb_ary_new4(len - j, ptr); + *cfp->sp++ = rb_ary_new4(len - j, ptr); } } else { /* normal: ary[num..-1], ary[num-2], ary[num-3], ..., ary[0] # top */ - rb_num_t i; - VALUE *bptr = &base[space_size - 1]; - - for (i=0; i<num; i++) { - if (len <= i) { - for (; i<num; i++) { - *bptr-- = Qnil; - } - break; - } - *bptr-- = ptr[i]; - } if (is_splat) { if (num > len) { - *bptr = rb_ary_new(); + *cfp->sp++ = rb_ary_new(); } else { - *bptr = rb_ary_new4(len - num, ptr + num); + *cfp->sp++ = rb_ary_new4(len - num, ptr + num); + } + } + + if (num > len) { + rb_num_t i = 0; + for (; i < num - len; i++) { + *cfp->sp++ = Qnil; + } + + for (rb_num_t j = 0; i < num; i++, j++) { + *cfp->sp++ = ptr[len - j - 1]; + } + } + else { + for (rb_num_t j = 0; j < num; j++) { + *cfp->sp++ = ptr[num - j - 1]; } } } + RB_GC_GUARD(ary); } |