summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Wu <[email protected]>2024-04-22 14:33:34 -0400
committerGitHub <[email protected]>2024-04-22 18:33:34 +0000
commitaeb08bc8a71006b63a6588ef5129b7723c8219fc (patch)
tree2563b237cc301900a55ad63c0d251923c33198af
parent1bb7638e7a864db9b635c5d41f18653e8451dbc7 (diff)
YJIT: Fix String#setbyte crashing for converted arguments
Previously, passing objects that respond to #to_int to `String#setbyte` resulted in a crash when compiled by YJIT. This was due to the lazily pushed frame from rb_yjit_lazy_push_frame() lingering and not being popped by an exception as expected. The fix is to ensure that `ec->cfp` is restored to before the lazy frame push in case the method call for conversion succeeds. Right now, this is only for conversion to integers. Found running `ruby/spec`. * clarify comment We just need to make sure `ec->cfp` is always preserved and this can convert without rising when `raise` is true.
-rw-r--r--bootstraptest/test_yjit.rb17
-rw-r--r--object.c9
2 files changed, 25 insertions, 1 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index c7a5883986..2e190a5b9c 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -4799,3 +4799,20 @@ assert_equal [0x80000000000, 'a+', :ok].inspect, %q{
tests
}
+
+# test String#stebyte with arguments that need conversion
+assert_equal "abc", %q{
+ str = +"a00"
+ def change_bytes(str, one, two)
+ str.setbyte(one, "b".ord)
+ str.setbyte(2, two)
+ end
+
+ to_int_1 = Object.new
+ to_int_99 = Object.new
+ def to_int_1.to_int = 1
+ def to_int_99.to_int = 99
+
+ change_bytes(str, to_int_1, to_int_99)
+ str
+}
diff --git a/object.c b/object.c
index 54240f0774..0a8ce4dc0f 100644
--- a/object.c
+++ b/object.c
@@ -3236,15 +3236,22 @@ ALWAYS_INLINE(static VALUE rb_to_integer_with_id_exception(VALUE val, const char
static inline VALUE
rb_to_integer_with_id_exception(VALUE val, const char *method, ID mid, int raise)
{
+ // We need to pop the lazily pushed frame when not raising an exception.
+ rb_control_frame_t *current_cfp;
VALUE v;
if (RB_INTEGER_TYPE_P(val)) return val;
+ current_cfp = GET_EC()->cfp;
rb_yjit_lazy_push_frame(GET_EC()->cfp->pc);
v = try_to_int(val, mid, raise);
- if (!raise && NIL_P(v)) return Qnil;
+ if (!raise && NIL_P(v)) {
+ GET_EC()->cfp = current_cfp;
+ return Qnil;
+ }
if (!RB_INTEGER_TYPE_P(v)) {
conversion_mismatch(val, "Integer", method, v);
}
+ GET_EC()->cfp = current_cfp;
return v;
}
#define rb_to_integer(val, method, mid) \