server: Atomically set CLOEXEC on accept fds
commiteb5c56a76795041a30eaacbff2e74c78772a0ccb
authorEric Blake <eblake@redhat.com>
Fri, 2 Aug 2019 21:13:51 +0000 (2 16:13 -0500)
committerEric Blake <eblake@redhat.com>
Fri, 2 Aug 2019 22:36:28 +0000 (2 17:36 -0500)
tree16d9545b4874634fcc6a66fc84a2b8bed4802074
parent0a5d00e0f2a58b1b1317f651b84d3b278f4dda31
server: Atomically set CLOEXEC on accept fds

The sh plugin makes it easy to test whether we leak fds into a plugin
that fork()s as part of handling a client request:

./nbdkit -U - --filter=log --filter=stats sh - \
  logfile=/dev/null statsfile=/dev/null \
  --run 'qemu-io -r -f raw -c "r 0 1" $nbd' <<\EOF
case $1 in
get_size) echo 1m;;
pread) ls -l /proc/$$/fd >/dev/tty
  dd iflag=count_bytes count=$3 if=/dev/zero || exit 1 ;;
*) exit 2 ;;
esac
EOF

Pre-patch, this demonstrates that we are leaking internal socket and
filter's log fds, as well as internal pipe fds fixed a couple patches
ago (the listing should only include fds 0, 1, 2, and whatever sh has
open on the temporary script file; bash picks 255 for that).  This
patch deals with the server leaks, the next one with the filter leaks.

In the case of accept, we either require atomicity or a mutex to
ensure that it is not possible for one thread to be accepting a new
client while another thread is processing .pread or similar with a
fork(), so that we avoid this race:

client1              client2
fd1 = accept
.pread
                     fd2 = accept
  fork
                     fcntl(F_SETFD, FD_CLOEXEC)

where the fd2 socket for the second client leaks into the fork for
handling the first client's pread callback.  If the server is running
with a thread model of SERIALIZE_ALL_REQUESTS or stricter, then we
already have such a lock (although we have to tweak lock_request to
work with a NULL connection), but the race could bite a server in the
looser SERIALIZE_REQUESTS or PARALLEL thread models; hence our
restriction added in the previous patch, and our preference for the
use of accept4().

Signed-off-by: Eric Blake <eblake@redhat.com>
server/internal.h
server/locks.c
server/sockets.c