NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/60638: AF_UNIX SOCK_SEQPACKET recvmsg truncates messages sent w/ MSG_EOR



>Number:         60638
>Category:       kern
>Synopsis:       AF_UNIX SOCK_SEQPACKET recvmsg truncates messages sent w/ MSG_EOR
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Aug 24 20:40:00 +0000 2026
>Originator:     Eric Wong <e%80x24.org@localhost>
>Release:        NetBSD 11.0
>Organization:
	self
>Environment:
System: NetBSD netbsd110-amd64 11.0 NetBSD 11.0 (GENERIC) #0: Thu Jul 30 15:23:12 UTC 2026 mkrepro%mkrepro.NetBSD.org@localhost:/usr/src/sys/arch/amd64/compile/GENERIC amd64
Architecture: x86_64
Machine: amd64
>Description:
	AF_UNIX SOCK_SEQPACKET messages sent with MSG_EOR are truncated
	upon recvmsg small values under 2K even when receiver supplies
	adequately large buffers.
	-
	While I have workaround for sending large buffers by putting the
	payload in an extra FD via SCM_RIGHTS, 1972 bytes seems like an
	unusually small value for a buffer (smaller than _SC_PAGE_SIZE
	or PIPE_BUF) and the workaround costs extra FDs.
	-
	The problem doesn't appear to manifest w/o MSG_EOR, but MSG_EOR
	seems like the right thing to demarcate individual messages
	when using SOCK_SEQPACKET with local Unix sockets.
	-
	OpenBSD fixed a similar issue several years ago (see code comments).
	MSG_EOR seems required for my code to work with FreeBSD 15.x+ (it
	worked fine w/o MSG_EOR in FreeBSD <=14.x).  I've used Linux with
	and without MSG_EOR since 2013 for AF_UNIX+SOCK_SEQPACKET and haven't
	noticed unusually small truncation lengths.
>How-To-Repeat:
/*
 * compile + run following code:
 *	cc -o /path/to/run /this/file.c -Wall && /path/to/run
 *
 * Using sendmsg(..., MSG_EOR) on an AF_UNIX + SOCK_SEQPACKET socket
 * appears to cause the recvmsg() caller to receive truncated data
 * (and set MSG_TRUNC) on NetBSD 11.0.
 *
 * OpenBSD had a similar bug fixed in 2023:
 * <https://marc.info/?i=fac37d6e4b2996a6%cvs.openbsd.org@localhost>
 *
 * Feel free to use this as test case under what ever license fits
 * best (CC-0, MIT, BSD-[23], ISC).
 * I extracted this test case from a codebase which I've been
 * using on both FreeBSD 12.x+ and Linux since 2021.
 * I normally send data between processes, but this truncation
 * happens within the same process, too.
 */
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <err.h>

#define NSEND 2048
#define NRECV (4096 * 33)

// the most FDs I ever intend to send:
#define SEND_FD_CAPA 10
#define SEND_FD_SPACE (SEND_FD_CAPA * sizeof(int))
union my_cmsg {
	struct cmsghdr hdr;
	char pad[sizeof(struct cmsghdr) + 16 + SEND_FD_SPACE];
};

static void do_sendmsg(int fd)
{
	/*
	 * FreeBSD 15.x seems to require MSG_EOR nowadays for proper
	 * message boundaries (didn't need it before 14.x).  Linux
	 * doesn't seem to care...
	 */
	int sflags = MSG_EOR;

	/*
	 * I normally have several FDs to send, but the
	 * discrepancy happens even with nfds == 0
	 */
	int i, nfds = 4;
	struct msghdr msg = { 0 };
	union my_cmsg cmsg = { 0 };
	char to_send[NSEND];
	struct iovec iov;
	int *fdp;
	ssize_t sent;

	memset(to_send, 'a', sizeof(to_send));
	to_send[NSEND - 1] = 'b'; // for kdump || strace output

	iov.iov_base = to_send;
	iov.iov_len = sizeof(to_send);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = &cmsg.hdr;
	msg.msg_controllen = CMSG_SPACE(nfds * sizeof(int));
	cmsg.hdr.cmsg_level = SOL_SOCKET;
	cmsg.hdr.cmsg_type = SCM_RIGHTS;
	cmsg.hdr.cmsg_len = CMSG_LEN(nfds * sizeof(int));
	fdp = (int *)CMSG_DATA(&cmsg.hdr);
	for (i = 0; i < nfds; i++)
		*fdp++ = i;
	sent = sendmsg(fd, &msg, sflags);
	fprintf(stderr, "%d sent: %zd of %zu\n",
		(int)getpid(), sent, iov.iov_len);
}

static void do_recvmsg(int fd)
{
	union my_cmsg cmsg = { 0 };
	struct msghdr msg = { 0 };
	struct iovec iov;
	ssize_t r;
	char recvbuf[NRECV];

	iov.iov_base = recvbuf;
	iov.iov_len = sizeof(recvbuf);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = &cmsg.hdr;
	msg.msg_controllen = CMSG_SPACE(SEND_FD_SPACE);

	r = recvmsg(fd, &msg, 0);
	/*
	 * FreeBSD and Linux receive everything sent, here, but
	 * NetBSD only gets 1972 bytes and sets MSG_TRUNC here:
	 */
	fprintf(stderr, "%d recv: %zd of %zu "
			"EOR=%d TRUNC=%d CTRUNC=%d OOB=%d\n",
			getpid(), r, iov.iov_len,
			msg.msg_flags & MSG_EOR,
			msg.msg_flags & MSG_TRUNC,
			msg.msg_flags & MSG_CTRUNC,
			msg.msg_flags & MSG_OOB);
	if (r > 0 && cmsg.hdr.cmsg_level == SOL_SOCKET &&
			cmsg.hdr.cmsg_type == SCM_RIGHTS) {
		size_t len = cmsg.hdr.cmsg_len;
		int *fdp = (int *)CMSG_DATA(&cmsg.hdr);
		size_t i;
		for (i = 0; CMSG_LEN((i + 1) * sizeof(int)) <= len; i++)
			fprintf(stderr, "%d recv fd=%d\n",
				(int)getpid(), *fdp++);
	}

	if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC))
		errx(EXIT_FAILURE, "truncated (BAD)");
	else
		warnx("no truncation (GOOD)");
}

int main(void)
{
	int fds[2];
	int rc = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds);
	assert(rc == 0);

	do_sendmsg(fds[0]);
	do_recvmsg(fds[1]);

	return 0;
}
>Fix:
	<how to correct or work around the problem, if known (multiple lines)>

>Unformatted:
 	<Please check that the above is correct for the bug being reported,>
 	<and append source date of snapshot, if applicable (one line).>



Home | Main Index | Thread Index | Old Index