From 2a4cab70ef0133fe3b06b8844eb4541fbab5f826 Mon Sep 17 00:00:00 2001 From: SmorkalovG Date: Fri, 29 Sep 2023 12:54:35 +0200 Subject: [PATCH] unix: reset signal counters after fork (#3485) If a signal was received but was not dispatched before fork then caught_signals counter should be reset. Closing of signal pipe makes impossible to receive the signal that was counted. There is no need in this signal because it was sent to parent process Fixes: https://github.com/libuv/libuv/issues/3483 --- src/unix/signal.c | 15 +++++++++++++++ test/test-fork.c | 41 +++++++++++++++++++++++++++++++++++++++++ test/test-list.h | 2 ++ 3 files changed, 58 insertions(+) diff --git a/src/unix/signal.c b/src/unix/signal.c index 63aba5a6..bc4206e6 100644 --- a/src/unix/signal.c +++ b/src/unix/signal.c @@ -279,6 +279,8 @@ static int uv__signal_loop_once_init(uv_loop_t* loop) { int uv__signal_loop_fork(uv_loop_t* loop) { + struct uv__queue* q; + if (loop->signal_pipefd[0] == -1) return 0; uv__io_stop(loop, &loop->signal_io_watcher, POLLIN); @@ -286,6 +288,19 @@ int uv__signal_loop_fork(uv_loop_t* loop) { uv__close(loop->signal_pipefd[1]); loop->signal_pipefd[0] = -1; loop->signal_pipefd[1] = -1; + + uv__queue_foreach(q, &loop->handle_queue) { + uv_handle_t* handle = uv__queue_data(q, uv_handle_t, handle_queue); + uv_signal_t* sh; + + if (handle->type != UV_SIGNAL) + continue; + + sh = (uv_signal_t*) handle; + sh->caught_signals = 0; + sh->dispatched_signals = 0; + } + return uv__signal_loop_once_init(loop); } diff --git a/test/test-fork.c b/test/test-fork.c index 29ed132a..d1a273f4 100644 --- a/test/test-fork.c +++ b/test/test-fork.c @@ -371,6 +371,47 @@ TEST_IMPL(fork_signal_to_child_closed) { return 0; } +static void fork_signal_cb(uv_signal_t* h, int s) { + fork_signal_cb_called = s; +} +static void empty_close_cb(uv_handle_t* h){} + +TEST_IMPL(fork_close_signal_in_child) { + uv_loop_t loop; + uv_signal_t signal_handle; + pid_t child_pid; + + ASSERT_EQ(0, uv_loop_init(&loop)); + ASSERT_EQ(0, uv_signal_init(&loop, &signal_handle)); + ASSERT_EQ(0, uv_signal_start(&signal_handle, &fork_signal_cb, SIGHUP)); + + ASSERT_EQ(0, kill(getpid(), SIGHUP)); + child_pid = fork(); + ASSERT_NE(child_pid, -1); + ASSERT_EQ(0, fork_signal_cb_called); + + if (!child_pid) { + uv_loop_fork(&loop); + uv_close((uv_handle_t*)&signal_handle, &empty_close_cb); + uv_run(&loop, UV_RUN_DEFAULT); + /* Child doesn't receive the signal */ + ASSERT_EQ(0, fork_signal_cb_called); + } else { + /* Parent. Runing once to receive the signal */ + uv_run(&loop, UV_RUN_ONCE); + ASSERT_EQ(SIGHUP, fork_signal_cb_called); + + /* loop should stop after closing the only handle */ + uv_close((uv_handle_t*)&signal_handle, &empty_close_cb); + ASSERT_EQ(0, uv_run(&loop, UV_RUN_DEFAULT)); + + assert_wait_child(child_pid); + } + + MAKE_VALGRIND_HAPPY(&loop); + return 0; +} + static void create_file(const char* name) { int r; diff --git a/test/test-list.h b/test/test-list.h index 36de006f..d112d07a 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -546,6 +546,7 @@ TEST_DECLARE (fork_socketpair) TEST_DECLARE (fork_socketpair_started) TEST_DECLARE (fork_signal_to_child) TEST_DECLARE (fork_signal_to_child_closed) +TEST_DECLARE (fork_close_signal_in_child) #ifndef __APPLE__ /* This is forbidden in a fork child: The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec(). */ @@ -1184,6 +1185,7 @@ TASK_LIST_START TEST_ENTRY (fork_socketpair_started) TEST_ENTRY (fork_signal_to_child) TEST_ENTRY (fork_signal_to_child_closed) + TEST_ENTRY (fork_close_signal_in_child) #ifndef __APPLE__ TEST_ENTRY (fork_fs_events_child) TEST_ENTRY (fork_fs_events_child_dir)