fix(signal): use sigaction and async-signal-safe handlers
Client: replace the non-async-signal-safe signal(3) call inside client_signal_handler() with a precomputed SIG_DFL sigaction(2), which is on the POSIX async-signal-safe list. The handler stays installed while a transfer is armed so a repeated Ctrl-C still leads to a graceful abort rather than a hard kill mid-cleanup. Server: cleanup() now only calls _exit(2) (async-signal-safe). The former server_delete()/daemon_conf_free()/credentials_free() teardown called free()/close()/SSL_CTX_free() from signal context, which can deadlock or corrupt the heap if the signal lands inside malloc/free. Handlers are installed with sigaction(2) instead of signal(3). The normal shutdown path in main() still performs the full teardown; the signal path relies on process exit to reclaim the parent daemon's socket, anonymous shared mapping and heap (no named/persistent parent resource is left behind).
This commit is contained in:
+16
-3
@@ -54,13 +54,26 @@ bool client_abort_pending(void) {
|
||||
}
|
||||
|
||||
#ifndef FASTSYNC_TEST_BUILD
|
||||
/* SIG_DFL disposition used by the handler's "not armed" fallback. It is built
|
||||
* once at load time so the handler can restore the default action with
|
||||
* sigaction(2) -- which is async-signal-safe -- instead of signal(3), which is
|
||||
* not. The zero-initialized sa_mask is the empty set. */
|
||||
static const struct sigaction client_default_action = {
|
||||
.sa_handler = SIG_DFL,
|
||||
.sa_flags = 0,
|
||||
};
|
||||
|
||||
/* Signal handler: perform NO work beyond storing the flag. Logging, protocol
|
||||
* I/O and the STATUS_ABORT frame are all done later on the normal send path,
|
||||
* which is not async-signal-safe. When no transfer is armed, fall back to the
|
||||
* default action so local-only modes remain interruptible. */
|
||||
* which is not async-signal-safe. When no transfer is armed, restore the
|
||||
* default disposition (async-signal-safe sigaction) and re-raise so local-only
|
||||
* modes remain interruptible. The handler deliberately stays installed while a
|
||||
* transfer is armed -- rather than using SA_RESETHAND -- so a second Ctrl-C
|
||||
* during the graceful abort keeps setting the flag instead of hard-killing the
|
||||
* process mid-cleanup. */
|
||||
static void client_signal_handler(int signo) {
|
||||
if (!client_abort_armed) {
|
||||
signal(signo, SIG_DFL);
|
||||
sigaction(signo, &client_default_action, NULL);
|
||||
raise(signo);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user