Skip to content

Commit b2edc35

Browse files
jhawthorncomposerinteralia
authored andcommitted
Fix signal crash during keyword argument call
64f508a changed rb_threadptr_raise to call rb_exception_setup, which uses rb_scan_args with RB_SCAN_ARGS_PASS_CALLED_KEYWORDS. This checked rb_keyword_given_p(), which read the interrupted frame's keyword state rather than the signal raise arguments, causing a crash when a signal arrived during a keyword call. Revert rb_threadptr_raise to use rb_make_exception directly, and have thread_raise_m call rb_exception_setup where rb_keyword_given_p() reflects the correct frame. [Bug #21865]
1 parent e04267a commit b2edc35

File tree

2 files changed

+32
-3
lines changed

2 files changed

+32
-3
lines changed

test/ruby/test_signal.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,4 +350,18 @@ def test_sigwait_fd_unused
350350
loop { sleep }
351351
End
352352
end if Process.respond_to?(:kill) && Process.respond_to?(:daemon)
353+
354+
def test_signal_during_kwarg_call
355+
status = assert_in_out_err([], <<~'RUBY', [], [], success: false)
356+
Thread.new do
357+
sleep 0.1
358+
Process.kill("TERM", $$)
359+
end
360+
361+
loop do
362+
File.open(IO::NULL, kwarg: true) {}
363+
end
364+
RUBY
365+
assert_predicate(status, :signaled?) if Signal.list.include?("QUIT")
366+
end if Process.respond_to?(:kill)
353367
end

thread.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2737,19 +2737,27 @@ rb_threadptr_ready(rb_thread_t *th)
27372737
static VALUE
27382738
rb_threadptr_raise(rb_thread_t *target_th, int argc, VALUE *argv)
27392739
{
2740+
VALUE exc;
2741+
27402742
if (rb_threadptr_dead(target_th)) {
27412743
return Qnil;
27422744
}
27432745

2744-
VALUE exception = rb_exception_setup(argc, argv);
2746+
if (argc == 0) {
2747+
exc = rb_exc_new(rb_eRuntimeError, 0, 0);
2748+
}
2749+
else {
2750+
exc = rb_make_exception(argc, argv);
2751+
}
27452752

27462753
/* making an exception object can switch thread,
27472754
so we need to check thread deadness again */
27482755
if (rb_threadptr_dead(target_th)) {
27492756
return Qnil;
27502757
}
27512758

2752-
rb_threadptr_pending_interrupt_enque(target_th, exception);
2759+
rb_ec_setup_exception(GET_EC(), exc, Qundef);
2760+
rb_threadptr_pending_interrupt_enque(target_th, exc);
27532761
rb_threadptr_interrupt(target_th);
27542762

27552763
return Qnil;
@@ -2933,7 +2941,14 @@ thread_raise_m(int argc, VALUE *argv, VALUE self)
29332941
const rb_thread_t *current_th = GET_THREAD();
29342942

29352943
threadptr_check_pending_interrupt_queue(target_th);
2936-
rb_threadptr_raise(target_th, argc, argv);
2944+
2945+
if (rb_threadptr_dead(target_th)) {
2946+
return Qnil;
2947+
}
2948+
2949+
VALUE exception = rb_exception_setup(argc, argv);
2950+
rb_threadptr_pending_interrupt_enque(target_th, exception);
2951+
rb_threadptr_interrupt(target_th);
29372952

29382953
/* To perform Thread.current.raise as Kernel.raise */
29392954
if (current_th == target_th) {

0 commit comments

Comments
 (0)