Skip to content

Commit 5f6b31c

Browse files
authored
Correctly handle Process.fork with an active Fiber.scheduler. (ruby#15385)
In the child process, nullify the current fiber scheduler and set the current fiber to blocking.
1 parent e822209 commit 5f6b31c

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

cont.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3371,6 +3371,8 @@ rb_fiber_atfork(rb_thread_t *th)
33713371
th->root_fiber = th->ec->fiber_ptr;
33723372
}
33733373
th->root_fiber->prev = 0;
3374+
th->root_fiber->blocking = 1;
3375+
th->blocking = 1;
33743376
}
33753377
}
33763378
#endif

test/fiber/test_scheduler.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,4 +226,61 @@ def test_condition_variable
226226
thread.join
227227
assert_kind_of RuntimeError, error
228228
end
229+
230+
def test_post_fork_scheduler_reset
231+
omit 'fork not supported' unless Process.respond_to?(:fork)
232+
233+
forked_scheduler_state = nil
234+
thread = Thread.new do
235+
r, w = IO.pipe
236+
scheduler = Scheduler.new
237+
Fiber.set_scheduler scheduler
238+
239+
forked_pid = fork do
240+
r.close
241+
w << (Fiber.scheduler ? 'set' : 'reset')
242+
w.close
243+
end
244+
w.close
245+
forked_scheduler_state = r.read
246+
Process.wait(forked_pid)
247+
ensure
248+
r.close rescue nil
249+
w.close rescue nil
250+
end
251+
thread.join
252+
assert_equal 'reset', forked_scheduler_state
253+
ensure
254+
thread.kill rescue nil
255+
end
256+
257+
def test_post_fork_fiber_blocking
258+
omit 'fork not supported' unless Process.respond_to?(:fork)
259+
260+
fiber_blocking_state = nil
261+
thread = Thread.new do
262+
r, w = IO.pipe
263+
scheduler = Scheduler.new
264+
Fiber.set_scheduler scheduler
265+
266+
forked_pid = nil
267+
Fiber.schedule do
268+
forked_pid = fork do
269+
r.close
270+
w << (Fiber.current.blocking? ? 'blocking' : 'nonblocking')
271+
w.close
272+
end
273+
end
274+
w.close
275+
fiber_blocking_state = r.read
276+
Process.wait(forked_pid)
277+
ensure
278+
r.close rescue nil
279+
w.close rescue nil
280+
end
281+
thread.join
282+
assert_equal 'blocking', fiber_blocking_state
283+
ensure
284+
thread.kill rescue nil
285+
end
229286
end

thread.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4997,6 +4997,7 @@ rb_thread_atfork(void)
49974997
rb_threadptr_pending_interrupt_clear(th);
49984998
rb_thread_atfork_internal(th, terminate_atfork_i);
49994999
th->join_list = NULL;
5000+
th->scheduler = Qnil;
50005001
rb_fiber_atfork(th);
50015002

50025003
/* We don't want reproduce CVE-2003-0900. */

0 commit comments

Comments
 (0)