From 1c75ded5ad35aaf3f4e1da316c50869c73903420 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Fri, 10 Feb 2023 11:22:45 -0500 Subject: [PATCH 3/5] mq_notify: rework to fix use-after-close/double-close bugs in the error path where the mq_notify syscall fails, the initiating thread may have closed the socket before the worker thread calls recv on it. even in the absence of such a race, if the recv call failed, e.g. due to seccomp policy blocking it, the worker thread could proceed to close, producing a double-close condition. this can all be simplified by moving the mq_notify syscall into the new thread, so that the error case does not require pthread_cancel. now, the initiating thread only needs to read back the error status after waiting for the worker thread to consume its arguments. --- src/mq/mq_notify.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/mq/mq_notify.c b/src/mq/mq_notify.c index a42888d2..8eac71ed 100644 --- a/src/mq/mq_notify.c +++ b/src/mq/mq_notify.c @@ -10,6 +10,8 @@ struct args { sem_t sem; int sock; + mqd_t mqd; + int err; const struct sigevent *sev; }; @@ -21,8 +23,21 @@ static void *start(void *p) int s = args->sock; void (*func)(union sigval) = args->sev->sigev_notify_function; union sigval val = args->sev->sigev_value; + struct sigevent sev2; + static const char zeros[32]; + int err = 0; + sev2.sigev_notify = SIGEV_THREAD; + sev2.sigev_signo = s; + sev2.sigev_value.sival_ptr = (void *)&zeros; + + err = 0; + if (syscall(SYS_mq_notify, args->mqd, &sev2) < 0) + err = errno; + args->err = err; sem_post(&args->sem); + if (err) return 0; + n = recv(s, buf, sizeof(buf), MSG_NOSIGNAL|MSG_WAITALL); close(s); if (n==sizeof buf && buf[sizeof buf - 1] == 1) @@ -36,8 +51,6 @@ int mq_notify(mqd_t mqd, const struct sigevent *sev) pthread_attr_t attr; pthread_t td; int s; - struct sigevent sev2; - static const char zeros[32]; int cs; if (!sev || sev->sigev_notify != SIGEV_THREAD) @@ -46,6 +59,7 @@ int mq_notify(mqd_t mqd, const struct sigevent *sev) s = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, 0); if (s < 0) return -1; args.sock = s; + args.mqd = mqd; if (sev->sigev_notify_attributes) attr = *sev->sigev_notify_attributes; else pthread_attr_init(&attr); @@ -63,13 +77,9 @@ int mq_notify(mqd_t mqd, const struct sigevent *sev) pthread_setcancelstate(cs, 0); sem_destroy(&args.sem); - sev2.sigev_notify = SIGEV_THREAD; - sev2.sigev_signo = s; - sev2.sigev_value.sival_ptr = (void *)&zeros; - - if (syscall(SYS_mq_notify, mqd, &sev2) < 0) { - pthread_cancel(td); + if (args.err) { __syscall(SYS_close, s); + errno = args.err; return -1; } -- 2.21.0