1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
--- src/privsep-root.c.orig 2025-11-14 15:38:04 UTC
+++ src/privsep-root.c
@@ -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) \
@@ -98,37 +99,58 @@ 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)
+ 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);
+ /* psr_mdatalen could be smaller then psr_datalen
+ * if the above malloc failed. */
+ iov[1].iov_len =
+ MIN(psr_ctx->psr_mdatalen, psr_error->psr_datalen);
+ } else {
iov[1].iov_base = psr_ctx->psr_data;
+ /* This should never be the case */
+ iov[1].iov_len =
+ MIN(psr_ctx->psr_datalen, 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(EINVAL);
+ else if (msg.msg_flags & MSG_TRUNC)
+ PSR_ERROR(ENOBUFS);
else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen)
PSR_ERROR(EINVAL);
return len;
|