summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstraptest/test_yjit.rb31
-rw-r--r--yjit_codegen.c4
2 files changed, 35 insertions, 0 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb
index 30298a820d..47744efb73 100644
--- a/bootstraptest/test_yjit.rb
+++ b/bootstraptest/test_yjit.rb
@@ -1,3 +1,34 @@
+assert_equal '2022', %q{
+ def contrivance(hash, key)
+ # Expect this to compile to an `opt_aref`.
+ hash[key]
+
+ # The [] call above tracks that the `hash` local has a VALUE that
+ # is a heap pointer and the guard for the Kernel#itself call below
+ # doesn't check that it's a heap pointer VALUE.
+ #
+ # As you can see from the crash, the call to rb_hash_aref() can set the
+ # `hash` local, making eliding the heap object guard unsound.
+ hash.itself
+ end
+
+ # This is similar to ->(recv, mid) { send(recv, mid).local_variable_set(...) }.
+ # By composing we avoid creating new Ruby frames and so sending :binding
+ # captures the environment of the frame that does the missing key lookup.
+ # We use it to capture the environment inside of `contrivance`.
+ cap_then_set =
+ Kernel.instance_method(:send).method(:bind_call).to_proc >>
+ ->(binding) { binding.local_variable_set(:hash, 2022) }
+ special_missing = Hash.new(&cap_then_set)
+
+ # Make YJIT speculate that it's a hash and generate code
+ # that calls rb_hash_aref().
+ contrivance({}, :warmup)
+ contrivance({}, :warmup)
+
+ contrivance(special_missing, :binding)
+}
+
assert_equal '18374962167983112447', %q{
# regression test for incorrectly discarding 32 bits of a pointer when it
# comes to default values.
diff --git a/yjit_codegen.c b/yjit_codegen.c
index aa352dac70..25fcfca083 100644
--- a/yjit_codegen.c
+++ b/yjit_codegen.c
@@ -184,6 +184,10 @@ jit_prepare_routine_call(jitstate_t *jit, ctx_t *ctx, x86opnd_t scratch_reg)
jit->record_boundary_patch_point = true;
jit_save_pc(jit, scratch_reg);
jit_save_sp(jit, ctx);
+
+ // In case the routine calls Ruby methods, it can set local variables
+ // through Kernel#binding and other means.
+ ctx_clear_local_types(ctx);
}
// Record the current codeblock write position for rewriting into a jump into