]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
privsep: Fix reading results from root process
authorRoy Marples <roy@marples.name>
Fri, 31 Oct 2025 07:29:10 +0000 (07:29 +0000)
committerRoy Marples <roy@marples.name>
Fri, 31 Oct 2025 07:29:10 +0000 (07:29 +0000)
Should be the final fallout from the new eloop.

src/privsep-root.c
src/privsep.c
src/privsep.h

index 250500845e49c21cf849fa36084d58691bbb0532..890daffc4cc3ff16f7a7b6e1fc93b6422bb76e57 100644 (file)
@@ -71,6 +71,9 @@ struct psr_ctx {
        struct psr_error psr_error;
        size_t psr_datalen;
        void *psr_data;
+       size_t psr_mdatalen;
+       void *psr_mdata;
+       bool psr_usemdata;
 };
 
 static void
@@ -81,14 +84,15 @@ ps_root_readerrorcb(void *arg, unsigned short events)
        struct psr_error *psr_error = &psr_ctx->psr_error;
        struct iovec iov[] = {
                { .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
-               { .iov_base = psr_ctx->psr_data,
-                 .iov_len = psr_ctx->psr_datalen },
+               { .iov_base = NULL, .iov_len = 0 },
        };
        ssize_t len;
        int exit_code = EXIT_FAILURE;
 
-       if (events & ELE_HANGUP)
+       if (events & ELE_HANGUP) {
+               logerrx("%s: hangup", __func__);
                goto out;
+       }
 
        if (events != ELE_READ)
                logerrx("%s: unexpected event 0x%04x", __func__, events);
@@ -100,11 +104,39 @@ ps_root_readerrorcb(void *arg, unsigned short events)
                goto out;                       \
        } while (0 /* CONSTCOND */)
 
-       len = readv(PS_ROOT_FD(ctx), iov, __arraycount(iov));
+       len = recv(PS_ROOT_FD(ctx), psr_error, sizeof(*psr_error), MSG_PEEK);
        if (len == -1)
                PSR_ERROR(errno);
        else if ((size_t)len < sizeof(*psr_error))
                PSR_ERROR(EINVAL);
+
+       if (psr_error->psr_datalen > SSIZE_MAX)
+               PSR_ERROR(ENOBUFS);
+       if (psr_ctx->psr_usemdata &&
+           psr_error->psr_datalen > psr_ctx->psr_mdatalen)
+       {
+               void *d = realloc(psr_ctx->psr_mdata, psr_error->psr_datalen);
+               if (d == NULL)
+                       PSR_ERROR(errno);
+               psr_ctx->psr_mdata = d;
+               psr_ctx->psr_mdatalen = psr_error->psr_datalen;
+       }
+       if (psr_error->psr_datalen != 0) {
+               if (psr_ctx->psr_usemdata)
+                       iov[1].iov_base = psr_ctx->psr_mdata;
+               else {
+                       if (psr_error->psr_datalen > psr_ctx->psr_datalen)
+                               PSR_ERROR(ENOBUFS);
+                       iov[1].iov_base = psr_ctx->psr_data;
+               }
+               iov[1].iov_len = psr_error->psr_datalen;
+       }
+
+       len = readv(PS_ROOT_FD(ctx), iov, __arraycount(iov));
+       if (len == -1)
+               PSR_ERROR(errno);
+       else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen)
+               PSR_ERROR(EINVAL);
        exit_code = EXIT_SUCCESS;
 
 out:
@@ -119,6 +151,7 @@ ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len)
 
        pc->psr_data = data;
        pc->psr_datalen = len;
+       pc->psr_usemdata = false;
        err = eloop_start(ctx->ps_eloop);
        if (err < 0)
                return err;
@@ -127,73 +160,35 @@ ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len)
        return pc->psr_error.psr_result;
 }
 
-#ifdef PRIVSEP_GETIFADDRS
-static void
-ps_root_mreaderrorcb(void *arg, unsigned short events)
-{
-       struct psr_ctx *psr_ctx = arg;
-       struct dhcpcd_ctx *ctx = psr_ctx->psr_ctx;
-       struct psr_error *psr_error = &psr_ctx->psr_error;
-       struct iovec iov[] = {
-               { .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
-               { .iov_base = NULL, .iov_len = 0 },
-       };
-       ssize_t len;
-       int exit_code = EXIT_FAILURE;
-
-       if (events != ELE_READ)
-               logerrx("%s: unexpected event 0x%04x", __func__, events);
-
-       len = recv(PS_ROOT_FD(ctx), psr_error, sizeof(*psr_error), MSG_PEEK);
-       if (len == -1)
-               PSR_ERROR(errno);
-       else if ((size_t)len < sizeof(*psr_error))
-               PSR_ERROR(EINVAL);
-
-       if (psr_error->psr_datalen > SSIZE_MAX)
-               PSR_ERROR(ENOBUFS);
-       else if (psr_error->psr_datalen != 0) {
-               psr_ctx->psr_data = malloc(psr_error->psr_datalen);
-               if (psr_ctx->psr_data == NULL)
-                       PSR_ERROR(errno);
-               psr_ctx->psr_datalen = psr_error->psr_datalen;
-               iov[1].iov_base = psr_ctx->psr_data;
-               iov[1].iov_len = psr_ctx->psr_datalen;
-       }
-
-       len = readv(PS_ROOT_FD(ctx), iov, __arraycount(iov));
-       if (len == -1)
-               PSR_ERROR(errno);
-       else if ((size_t)len != sizeof(*psr_error) + psr_ctx->psr_datalen)
-               PSR_ERROR(EINVAL);
-       exit_code = EXIT_SUCCESS;
-
-out:
-       eloop_exit(ctx->ps_eloop, exit_code);
-}
-
 ssize_t
 ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len)
 {
-       struct psr_ctx psr_ctx = {
-           .psr_ctx = ctx,
-       };
-       int fd = PS_ROOT_FD(ctx), err;
-
-       if (eloop_event_add(ctx->ps_eloop, fd, ELE_READ,
-           ps_root_mreaderrorcb, &psr_ctx) == -1)
-               return -1;
+       struct psr_ctx *pc = ctx->ps_root->psp_data;
+       int err;
+       void *d;
 
-       err = eloop_start(ctx->ps_eloop, &ctx->sigset);
+       pc->psr_usemdata = true;
+       err = eloop_start(ctx->ps_eloop);
        if (err < 0)
                return err;
 
-       errno = psr_ctx.psr_error.psr_errno;
-       *data = psr_ctx.psr_data;
-       *len = psr_ctx.psr_datalen;
-       return psr_ctx.psr_error.psr_result;
+       if (pc->psr_error.psr_datalen != 0) {
+               if (pc->psr_error.psr_datalen > pc->psr_mdatalen) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               d = malloc(pc->psr_error.psr_datalen);
+               if (d == NULL)
+                       return -1;
+               memcpy(d, pc->psr_mdata, pc->psr_error.psr_datalen);
+       } else
+               d = NULL;
+
+       errno = pc->psr_error.psr_errno;
+       *data = d;
+       *len = pc->psr_error.psr_datalen;
+       return pc->psr_error.psr_result;
 }
-#endif
 
 static ssize_t
 ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result,
@@ -863,6 +858,17 @@ ps_root_log(void *arg, unsigned short events)
                logerr(__func__);
 }
 
+static void
+ps_root_freepsdata(void *arg)
+{
+       struct psr_ctx *pc = arg;
+
+       if (pc == NULL)
+               return;
+       free(pc->psr_mdata);
+       free(pc);
+}
+
 pid_t
 ps_root_start(struct dhcpcd_ctx *ctx)
 {
@@ -891,8 +897,7 @@ ps_root_start(struct dhcpcd_ctx *ctx)
                return -1;
 #endif
 
-
-       pc = malloc(sizeof(*pc));
+       pc = calloc(1, sizeof(*pc));
        if (pc == NULL)
                return -1;
        pc->psr_ctx = ctx;
@@ -903,6 +908,7 @@ ps_root_start(struct dhcpcd_ctx *ctx)
                free(pc);
                return -1;
        }
+       psp->psp_freedata = ps_root_freepsdata;
        strlcpy(psp->psp_name, "privileged proxy", sizeof(psp->psp_name));
        pid = ps_startprocess(psp, ps_root_recvmsg, NULL,
            ps_root_startcb, PSF_ELOOP);
@@ -970,6 +976,7 @@ int
 ps_root_stop(struct dhcpcd_ctx *ctx)
 {
        struct ps_process *psp = ctx->ps_root;
+       int err;
 
        if (!(ctx->options & DHCPCD_PRIVSEP) ||
            ctx->eloop == NULL)
@@ -1001,7 +1008,9 @@ ps_root_stop(struct dhcpcd_ctx *ctx)
                        return -1;
        } /* else the root process has already exited :( */
 
-       return ps_stopwait(ctx);
+       err = ps_stopwait(ctx);
+       ps_freeprocess(ctx->ps_root);
+       return err;
 }
 
 ssize_t
index 88ab11a1e85f80d54dbd61d13a25e1e9a4f51b32..1fff0798b0964603dd02d573bf0e77198309b5fc 100644 (file)
@@ -775,6 +775,11 @@ ps_freeprocess(struct ps_process *psp)
 
        TAILQ_REMOVE(&ctx->ps_processes, psp, next);
 
+       if (psp->psp_freedata != NULL)
+               psp->psp_freedata(psp->psp_data);
+       else
+               free(psp->psp_data);
+
        if (psp->psp_fd != -1) {
                eloop_event_delete(ctx->eloop, psp->psp_fd);
                close(psp->psp_fd);
index 7d4e6e111fde502cad50484712d691cb47a702f6..37380d4c8b8251be5650ab6cddffeee877b70e35 100644 (file)
@@ -185,6 +185,7 @@ struct ps_process {
        uint16_t psp_proto;
        const char *psp_protostr;
        void *psp_data;
+       void (*psp_freedata)(void *);
        bool psp_started;
 
 #ifdef INET