From 2f50736782894519a81136691363c3f1227efba5 Mon Sep 17 00:00:00 2001 From: Yorhel Date: Mon, 24 Mar 2025 11:07:36 +0100 Subject: [PATCH] fdpass_recv: Set O_CLOEXEC on received fds Turns out this is necessary even if the fd is going to be passed through exec() soon, because the supervisor might receive multiple fds before spawning another process, in which case all of them are going to be passed to the new process instead of just one. --- FU.pm | 13 +++++-------- FU/Util.pm | 7 +++---- c/fdpass.c | 2 +- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/FU.pm b/FU.pm index 81bcf94..1451c74 100644 --- a/FU.pm +++ b/FU.pm @@ -475,26 +475,23 @@ sub _supervisor($c) { } # Don't bother spawning more than 1 at a time while in error state - my $spawn = !$err ? $c->{proc} - keys %childs : (grep $_ == 1, values %childs) ? 0 : 1; + my $spawn = !$err ? $c->{proc} - keys %childs : !@client_fd && (grep $_ == 1, values %childs) ? 0 : 1; for (1..$spawn) { - my $client = shift @client_fd; + my $client = @client_fd ? IO::Socket->new_from_fd(shift(@client_fd), 'r') : undef; my $pid = fork; die $! if !defined $pid; if (!$pid) { # child $SIG{CHLD} = $SIG{HUP} = $SIG{INT} = $SIG{TERM} = undef; + # In error state, wait with loading the script until we've received a request. + # Otherwise we'll end up in an infinite spawning loop if the script doesn't start properly. + $client = $c->{listen_sock}->accept() or die $! if !$client && $err; if ($client) { - $ENV{FU_CLIENT_FD} = $client; - } elsif ($err) { - # In error state, wait with loading the script until we've received a request. - # Otherwise we'll end up in an infinite spawning loop if the script doesn't start properly. - $client = $c->{listen_sock}->accept() or die $!; fcntl $client, Fcntl::F_SETFD, 0; $ENV{FU_CLIENT_FD} = fileno $client; } exec $^X, (map "-I$_", @INC), $0; exit 1; } - $client && IO::Socket->new_from_fd($client, 'r'); # close() the fd if we have one $childs{$pid} = 1; } diff --git a/FU/Util.pm b/FU/Util.pm index 7b8c6ad..e823d79 100644 --- a/FU/Util.pm +++ b/FU/Util.pm @@ -458,10 +458,9 @@ Like regular socket I/O, a single C message may be split across multiple C calls; in that case the C<$fd> is only received on the first call. -Don't use this function if the sender may include multiple file descriptors in -a single message, weird things can happen. File descriptors received this way -do not have the C flag and will thus survive a call to C. -Refer to L flag is set on received file descriptors. Don't use this +function if the sender may include multiple file descriptors in a single +message, weird things can happen. Refer to L for more weirdness and edge cases. diff --git a/c/fdpass.c b/c/fdpass.c index ae4b141..b54df7d 100644 --- a/c/fdpass.c +++ b/c/fdpass.c @@ -53,7 +53,7 @@ static int fufdpass_recv(pTHX_ I32 ax, int socket, size_t len) { msg.msg_controllen = sizeof(cmsgbuf.buf); msg.msg_flags = 0; - ssize_t r = recvmsg(socket, &msg, 0); + ssize_t r = recvmsg(socket, &msg, MSG_CMSG_CLOEXEC); if (r < 0) { ST(0) = &PL_sv_undef; ST(1) = &PL_sv_undef;