Skip to content

Commit b709538

Browse files
glebiusrsmarples
andauthored
privsep: enforce message boundaries with MSG_EOR on our messages (#533)
privsep: enforce message boundaries with MSG_EOR on our messages The nature of the SOCK_SEQPACKET, that privsep modules uses, is stream. See: https://pubs.opengroup.org/onlinepubs/9799919799/functions/V2_chap02.html#tag_16_10_06 To guarantee that a reader will never read two messages in one read operation, the writer shall put end of record markers. The problem exposed itself in FreeBSD 15.0 that started to follow the specification better than before. Other SOCK_SEQPACKET usage considerations: a) as long as our reader provides a receive buffer that would fit the largest message our writer would ever send, we are good with regards to not a reading a partial message b) as long as our writer always write full messages with one write, we don't need use of MSG_WAITALL in reader. Fixes #530 Co-authored-by: Roy Marples <roy@marples.name>
1 parent 6dcb156 commit b709538

5 files changed

Lines changed: 65 additions & 42 deletions

File tree

src/dhcpcd.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
393393

394394
eloop_event_delete(ctx->eloop, ctx->fork_fd);
395395
exit_code = EXIT_SUCCESS;
396-
if (write(ctx->fork_fd, &exit_code, sizeof(exit_code)) == -1)
396+
if (send(ctx->fork_fd, &exit_code, sizeof(exit_code), MSG_EOR) == -1)
397397
logerr(__func__);
398398
close(ctx->fork_fd);
399399
ctx->fork_fd = -1;
@@ -1449,8 +1449,8 @@ dhcpcd_signal_cb(int sig, void *arg)
14491449

14501450
if (sig != SIGCHLD && ctx->options & DHCPCD_FORKED) {
14511451
if (sig != SIGHUP &&
1452-
write(ctx->fork_fd, &sig, sizeof(sig)) == -1)
1453-
logerr("%s: write", __func__);
1452+
send(ctx->fork_fd, &sig, sizeof(sig), MSG_EOR) == -1)
1453+
logerr("%s: send", __func__);
14541454
return;
14551455
}
14561456

@@ -2712,8 +2712,8 @@ main(int argc, char **argv, char **envp)
27122712
#ifdef USE_SIGNALS
27132713
/* If still attached, detach from the launcher */
27142714
if (ctx.options & DHCPCD_STARTED && ctx.fork_fd != -1) {
2715-
if (write(ctx.fork_fd, &i, sizeof(i)) == -1)
2716-
logerr("%s: write", __func__);
2715+
if (send(ctx.fork_fd, &i, sizeof(i), MSG_EOR) == -1)
2716+
logerr("%s: send", __func__);
27172717
}
27182718
#endif
27192719

src/logerr.c

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
* SUCH DAMAGE.
2727
*/
2828

29+
#include <sys/socket.h>
2930
#include <sys/time.h>
31+
3032
#include <errno.h>
3133
#include <stdbool.h>
3234
#include <stdarg.h>
@@ -215,18 +217,25 @@ vlogmessage(int pri, const char *fmt, va_list args)
215217
int len = 0;
216218

217219
if (ctx->log_fd != -1) {
220+
pid_t pid = getpid();
218221
char buf[LOGERR_SYSLOGBUF];
219-
pid_t pid;
220-
221-
memcpy(buf, &pri, sizeof(pri));
222-
pid = getpid();
223-
memcpy(buf + sizeof(pri), &pid, sizeof(pid));
224-
len = vsnprintf(buf + sizeof(pri) + sizeof(pid),
225-
sizeof(buf) - sizeof(pri) - sizeof(pid),
226-
fmt, args);
227-
if (len != -1)
228-
len = (int)write(ctx->log_fd, buf,
229-
((size_t)++len) + sizeof(pri) + sizeof(pid));
222+
struct iovec iov[] = {
223+
{ .iov_base = &pri, .iov_len = sizeof(pri) },
224+
{ .iov_base = &pid, .iov_len = sizeof(pid) },
225+
{ .iov_base = buf },
226+
};
227+
228+
len = vsnprintf(buf, sizeof(buf), fmt, args);
229+
if (len != -1) {
230+
if ((size_t)len >= sizeof(buf))
231+
len = (int)sizeof(buf) - 1;
232+
iov[2].iov_len = (size_t)(len + 1);
233+
struct msghdr msg = {
234+
.msg_iov = iov,
235+
.msg_iovlen = sizeof(iov) / sizeof(iov[0]),
236+
};
237+
len = (int)sendmsg(ctx->log_fd, &msg, MSG_EOR);
238+
}
230239
return len;
231240
}
232241

@@ -390,24 +399,33 @@ int
390399
logreadfd(int fd)
391400
{
392401
struct logctx *ctx = &_logctx;
393-
char buf[LOGERR_SYSLOGBUF];
394402
int len, pri;
395-
396-
len = (int)read(fd, buf, sizeof(buf));
397-
if (len == -1)
403+
pid_t pid;
404+
char buf[LOGERR_SYSLOGBUF] = { '\0' };
405+
struct iovec iov[] = {
406+
{ .iov_base = &pri, .iov_len = sizeof(pri) },
407+
{ .iov_base = &pid, .iov_len = sizeof(pid) },
408+
{ .iov_base = buf, .iov_len = sizeof(buf) },
409+
};
410+
struct msghdr msg = {
411+
.msg_iov = iov,
412+
.msg_iovlen = sizeof(iov) / sizeof(iov[0])
413+
};
414+
415+
len = (int)recvmsg(fd, &msg, MSG_WAITALL);
416+
if (len == -1 || len == 0)
398417
return -1;
399-
400-
/* Ensure we have pri, pid and a terminator */
401-
if (len < (int)(sizeof(pri) + sizeof(pid_t) + 1) ||
402-
buf[len - 1] != '\0')
403-
{
404-
errno = EINVAL;
418+
/* Ensure we received the minimum and at least one character to log */
419+
if ((size_t)len < sizeof(pri) + sizeof(pid) + 1 ||
420+
msg.msg_flags & MSG_TRUNC) {
421+
errno = EMSGSIZE;
405422
return -1;
406423
}
424+
/* Ensure what we receive is NUL terminated */
425+
buf[(size_t)len - (sizeof(pri) + sizeof(pid)) - 1] = '\0';
407426

408-
memcpy(&pri, buf, sizeof(pri));
409-
memcpy(&ctx->log_pid, buf + sizeof(pri), sizeof(ctx->log_pid));
410-
logmessage(pri, "%s", buf + sizeof(pri) + sizeof(ctx->log_pid));
427+
ctx->log_pid = pid;
428+
logmessage(pri, "%s", buf);
411429
ctx->log_pid = 0;
412430
return len;
413431
}

src/logerr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ __printflike(2, 3) void logerrmessage(int pri, const char *fmt, ...);
7676
#define logerr(...) log_err(__VA_ARGS__)
7777
#define logerrx(...) log_errx(__VA_ARGS__)
7878

79-
/* For logging in a chroot */
79+
/* For logging in a chroot using SOCK_SEQPACKET */
8080
int loggetfd(void);
8181
void logsetfd(int);
8282
int logreadfd(int);

src/privsep-root.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,15 @@ ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result,
210210
{ .iov_base = &psr, .iov_len = sizeof(psr) },
211211
{ .iov_base = data, .iov_len = len },
212212
};
213+
struct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) };
213214
ssize_t err;
214215
int fd = PS_ROOT_FD(ctx);
215216

216217
#ifdef PRIVSEP_DEBUG
217218
logdebugx("%s: result %zd errno %d", __func__, result, errno);
218219
#endif
219220

220-
err = writev(fd, iov, __arraycount(iov));
221+
err = sendmsg(fd, &msg, MSG_EOR);
221222

222223
/* Error sending the message? Try sending the error of sending. */
223224
if (err == -1) {
@@ -227,7 +228,7 @@ ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result,
227228
psr.psr_errno = errno;
228229
iov[1].iov_base = NULL;
229230
iov[1].iov_len = 0;
230-
err = writev(fd, iov, __arraycount(iov));
231+
err = sendmsg(fd, &msg, MSG_EOR);
231232
}
232233

233234
return err;

src/privsep.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd,
895895
{ .iov_base = NULL, }, /* payload 2 */
896896
{ .iov_base = NULL, }, /* payload 3 */
897897
};
898-
int iovlen;
898+
struct msghdr m = { .msg_iov = iov, .msg_iovlen = 1 };
899899
ssize_t len;
900900

901901
if (msg != NULL) {
@@ -909,32 +909,34 @@ ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd,
909909
iovp->iov_base = msg->msg_name;
910910
iovp->iov_len = msg->msg_namelen;
911911
iovp++;
912+
m.msg_iovlen++;
912913

913914
cmsg_padlen =
914915
CALC_CMSG_PADLEN(msg->msg_controllen, msg->msg_namelen);
915916
assert(cmsg_padlen <= sizeof(padding));
916917
iovp->iov_len = cmsg_padlen;
917918
iovp->iov_base = cmsg_padlen != 0 ? padding : NULL;
918919
iovp++;
920+
m.msg_iovlen++;
919921

920922
iovp->iov_base = msg->msg_control;
921923
iovp->iov_len = msg->msg_controllen;
922-
iovlen = 4;
924+
iovp++;
925+
m.msg_iovlen++;
923926

924927
for (i = 0; i < (int)msg->msg_iovlen; i++) {
925-
if ((size_t)(iovlen + i) > __arraycount(iov)) {
928+
if ((size_t)(m.msg_iovlen++) > __arraycount(iov)) {
926929
errno = ENOBUFS;
927930
return -1;
928931
}
929-
iovp++;
930932
iovp->iov_base = msg->msg_iov[i].iov_base;
931933
iovp->iov_len = msg->msg_iov[i].iov_len;
934+
iovp++;
932935
}
933-
iovlen += i;
934-
} else
935-
iovlen = 1;
936+
}
937+
938+
len = sendmsg(fd, &m, MSG_EOR);
936939

937-
len = writev(fd, iov, iovlen);
938940
if (len == -1) {
939941
if (ctx->options & DHCPCD_FORKED &&
940942
!(ctx->options & DHCPCD_PRIVSEPROOT))
@@ -1028,6 +1030,7 @@ ps_sendcmdmsg(int fd, uint16_t cmd, const struct msghdr *msg)
10281030
{ .iov_base = &psm, .iov_len = sizeof(psm) },
10291031
{ .iov_base = data, .iov_len = 0 },
10301032
};
1033+
struct msghdr m = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) };
10311034
size_t dl = sizeof(data);
10321035
socklen_t cmsg_padlen =
10331036
CALC_CMSG_PADLEN(msg->msg_controllen, msg->msg_namelen);
@@ -1063,7 +1066,8 @@ ps_sendcmdmsg(int fd, uint16_t cmd, const struct msghdr *msg)
10631066
psm.ps_namelen + psm.ps_controllen + psm.ps_datalen + cmsg_padlen;
10641067
if (psm.ps_datalen != 0)
10651068
memcpy(p, msg->msg_iov[0].iov_base, psm.ps_datalen);
1066-
return writev(fd, iov, __arraycount(iov));
1069+
1070+
return sendmsg(fd, &m, MSG_EOR);
10671071

10681072
nobufs:
10691073
errno = ENOBUFS;
@@ -1089,7 +1093,7 @@ ps_recvmsg(int rfd, unsigned short events, uint16_t cmd, int wfd)
10891093
if (!(events & ELE_READ))
10901094
logerrx("%s: unexpected event 0x%04x", __func__, events);
10911095

1092-
len = recvmsg(rfd, &msg, 0);
1096+
len = recvmsg(rfd, &msg, MSG_WAITALL);
10931097
if (len == -1) {
10941098
logerr("%s: recvmsg", __func__);
10951099
return len;

0 commit comments

Comments
 (0)