From 744d17ff6c33b09334508e8110007ea2a82252f5 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 8 Jul 2021 18:23:39 -0400 Subject: [PATCH] Fix infinite loop when b_return TracePoint throws Previously, we didn't pop the frame that runs the TracePoint hook for b_return events for blocks running as methods (bmethods). In case the hook raises, that formed an infinite loop during stack unwinding in hook_before_rewind(). [Bug #18060] --- test/ruby/test_settracefunc.rb | 27 +++++++++++++++++++++++++++ vm.c | 4 ++-- vm_trace.c | 1 + 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index bbd7579eeb..56d457c7d7 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -2656,4 +2656,31 @@ CODE TracePoint.allow_reentry{} end end + + def test_raising_from_b_return_tp_tracing_bmethod + assert_normal_exit(<<~RUBY, '[Bug #18060]', timeout: 3) + class Foo + define_singleton_method(:foo) { return } # a bmethod + end + + TracePoint.trace(:b_return) do |tp| + raise + end + + Foo.foo + RUBY + + # Same thing but with a target + assert_normal_exit(<<~RUBY, '[Bug #18060]', timeout: 3) + class Foo + define_singleton_method(:foo) { return } # a bmethod + end + + TracePoint.new(:b_return) do |tp| + raise + end.enable(target: Foo.method(:foo)) + + Foo.foo + RUBY + end end diff --git a/vm.c b/vm.c index 0d74521b37..c2dda3fdc4 100644 --- a/vm.c +++ b/vm.c @@ -2068,10 +2068,10 @@ hook_before_rewind(rb_execution_context_t *ec, const rb_control_frame_t *cfp, } - EXEC_EVENT_HOOK(ec, RUBY_EVENT_B_RETURN, ec->cfp->self, 0, 0, 0, bmethod_return_value); + EXEC_EVENT_HOOK_AND_POP_FRAME(ec, RUBY_EVENT_B_RETURN, ec->cfp->self, 0, 0, 0, bmethod_return_value); if (UNLIKELY(local_hooks && local_hooks->events & RUBY_EVENT_B_RETURN)) { rb_exec_event_hook_orig(ec, local_hooks, RUBY_EVENT_B_RETURN, - ec->cfp->self, 0, 0, 0, bmethod_return_value, FALSE); + ec->cfp->self, 0, 0, 0, bmethod_return_value, TRUE); } const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(ec->cfp); diff --git a/vm_trace.c b/vm_trace.c index 7f65f98695..51b815c54a 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -390,6 +390,7 @@ exec_hooks_protected(rb_execution_context_t *ec, rb_hook_list_t *list, const rb_ return state; } +// pop_p: Whether to pop the frame for the TracePoint when it throws. MJIT_FUNC_EXPORTED void rb_exec_event_hooks(rb_trace_arg_t *trace_arg, rb_hook_list_t *hooks, int pop_p) {