Skip to content
56 changes: 40 additions & 16 deletions src/privsep-root.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ ps_root_readerrorcb(struct psr_ctx *psr_ctx)
{ .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
{ .iov_base = NULL, .iov_len = 0 },
};
struct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) };
ssize_t len;

#define PSR_ERROR(e) \
Expand All @@ -98,39 +99,62 @@ ps_root_readerrorcb(struct psr_ctx *psr_ctx)
if (eloop_waitfd(fd) == -1)
PSR_ERROR(errno);

len = recv(fd, psr_error, sizeof(*psr_error), MSG_PEEK);
/* We peek at the psr_error structure to tell us how much of a buffer
* we need to read the whole packet. */
len = recvmsg(fd, &msg, MSG_PEEK | MSG_WAITALL);
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);
/* After this point, we MUST do another recvmsg even on a failure
* to remove the message after peeking. */
if ((size_t)len < sizeof(*psr_error))
goto recv;

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 we failed to malloc then psr_mdatalen will be smaller
* than psr_datalen.
* The following recvmsg will get MSG_TRUNC so the malloc error
* will be reported there but more importantly the
* message will be correctly discarded from the queue. */
if (d != NULL) {
psr_ctx->psr_mdata = d;
psr_ctx->psr_mdatalen = psr_error->psr_datalen;
}
}
if (psr_error->psr_datalen != 0) {
if (psr_ctx->psr_usemdata)
/* Set our buffers */
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_len = psr_ctx->psr_mdatalen;
} else {
iov[1].iov_base = psr_ctx->psr_data;
iov[1].iov_len = psr_ctx->psr_datalen;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
iov[1].iov_len = psr_error->psr_datalen;
/* We might require less than the buffer size */
if (iov[1].iov_len > psr_error->psr_datalen)
iov[1].iov_len = psr_error->psr_datalen;
}

len = readv(fd, iov, __arraycount(iov));
recv:
/* fd is SOCK_SEQPACKET and we mark the boundary with MSG_EOR
* so this can never stall if the receive buffers are bigger
* than the actual message. */
len = recvmsg(fd, &msg, MSG_WAITALL);
if (len == -1)
PSR_ERROR(errno);
else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen)
else if ((size_t)len < sizeof(*psr_error))
PSR_ERROR(EINVAL);
else if (msg.msg_flags & MSG_TRUNC)
PSR_ERROR(ENOBUFS);
else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen) {
logerrx("%s: recvmsg returned %zd, expecting %zu", __func__,
len, sizeof(*psr_error) + psr_error->psr_datalen);
PSR_ERROR(EBADMSG);
}
Comment thread
rsmarples marked this conversation as resolved.
return len;
}

Expand Down