>Synopsis: AF_UNIX SOCK_SEQPACKET sendmsg w/ MSG_EOR truncates recvmsg
>Category: system
>Environment:
        System      : OpenBSD 7.3
        Details     : OpenBSD 7.3 (GENERIC.MP) #1125: Sat Mar 25 10:36:29 MDT 
2023
                         
dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP

        Architecture: OpenBSD.amd64
        Machine     : amd64
>Description:
        sendmsg with MSG_EOR on a SOCK_SEQPACKET socket appears to cause the
        receiving end to get truncated output (and MSG_TRUNC set).  I'm only
        sending a 153-byte record, so unlikely a problem with system limits.
        This behavior does not exhibit on FreeBSD nor Linux, and I didn't see
        where MSG_EOR could result in a truncated message in any man pages;
        especially when I'm using AF_UNIX SOCK_SEQPACKET.
        Using flags=0 with sendmsg appears to work fine, so perhaps I'm
        misunderstanding MSG_EOR entirely.  Or it's both a misunderstanding
        on my part AND also a bug in OpenBSD.

>How-To-Repeat:

Here is a standalone C program reproducing the problem.  I extracted
it from code I've used on FreeBSD and Linux systems since 2021.

It can be compiled with: cc -o seqtrunc -Wall seqtrunc.c
Comments are inline:

/*
 * Using sendmsg(..., MSG_EOR) on a SOCK_SEQPACKET socket
 * appears to cause the recvmsg() caller to receive truncated
 * data (and set MSG_TRUNC) on OpenBSD 7.3.
 *
 * I'm not sure if:
 * a) this is a bug in OpenBSD
 * b) I'm misunderstanding MSG_EOR with SOCK_SEQPACKET
 * c) both of the above
 *
 * I extracted this test case from a codebase which I've been
 * using on FreeBSD 12.x and numerous Linux installs 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>

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

#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)
{
        /*
         * Is setting MSG_EOR wrong here? OpenBSD recvmsg ends
         * up truncating data and setting MSG_TRUNC below.
         * OTOH, FreeBSD and Linux work fine with (sflags = 0),
         * here, too...
         */
        int sflags = MSG_EOR;

        /*
         * I normally have several FDs to send, but the
         * discrepancy happens even with nfds == 0
         */
        int i, nfds = 1;
        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
         * OpenBSD only gets 144 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++);
        }
}

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:
        Setting `sflags = 0' in the above code seems to avoid the
        problem, but I'm not sure if it's correct to do so because
        I want to rely on the kernel to handle record boundaries.

dmesg:
OpenBSD 7.3 (GENERIC.MP) #1125: Sat Mar 25 10:36:29 MDT 2023
    dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP
real mem = 17162948608 (16367MB)
avail mem = 16623382528 (15853MB)
random: good seed from bootblocks
mpath0 at root
scsibus0 at mpath0: 256 targets
mainbus0 at root
bios0 at mainbus0: SMBIOS rev. 2.8 @ 0xbffffc80 (17 entries)
bios0: vendor SeaBIOS version "1.14.0-2" date 04/01/2014
bios0: QEMU Standard PC (i440FX + PIIX, 1996)
acpi0 at bios0: ACPI 1.0
acpi0: sleep states S3 S4 S5
acpi0: tables DSDT FACP APIC HPET WAET
acpi0: wakeup devices
acpitimer0 at acpi0: 3579545 Hz, 24 bits
acpimadt0 at acpi0 addr 0xfee00000: PC-AT compat
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: QEMU Virtual CPU version 2.5+, 3300.16 MHz, 06-06-03
cpu0: 
FPU,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,SSE3,CX16,x2APIC,HV,NXE,LONG,LAHF,MELTDOWN
cpu0: 32KB 64b/line 8-way D-cache, 32KB 64b/line 8-way I-cache, 4MB 64b/line 
16-way L2 cache, 16MB 64b/line 16-way L3 cache
cpu0: smt 0, core 0, package 0
mtrr: Pentium Pro MTRR support, 8 var ranges, 88 fixed ranges
cpu0: apic clock running at 1000MHz
cpu1 at mainbus0: apid 1 (application processor)
cpu1: QEMU Virtual CPU version 2.5+, 3300.22 MHz, 06-06-03
cpu1: 
FPU,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,SSE3,CX16,x2APIC,HV,NXE,LONG,LAHF,MELTDOWN
cpu1: 32KB 64b/line 8-way D-cache, 32KB 64b/line 8-way I-cache, 4MB 64b/line 
16-way L2 cache, 16MB 64b/line 16-way L3 cache
cpu1: smt 0, core 0, package 1
cpu2 at mainbus0: apid 2 (application processor)
cpu2: QEMU Virtual CPU version 2.5+, 3300.22 MHz, 06-06-03
cpu2: 
FPU,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,SSE3,CX16,x2APIC,HV,NXE,LONG,LAHF,MELTDOWN
cpu2: 32KB 64b/line 8-way D-cache, 32KB 64b/line 8-way I-cache, 4MB 64b/line 
16-way L2 cache, 16MB 64b/line 16-way L3 cache
cpu2: smt 0, core 0, package 2
cpu3 at mainbus0: apid 3 (application processor)
cpu3: QEMU Virtual CPU version 2.5+, 3300.22 MHz, 06-06-03
cpu3: 
FPU,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,SSE3,CX16,x2APIC,HV,NXE,LONG,LAHF,MELTDOWN
cpu3: 32KB 64b/line 8-way D-cache, 32KB 64b/line 8-way I-cache, 4MB 64b/line 
16-way L2 cache, 16MB 64b/line 16-way L3 cache
cpu3: smt 0, core 0, package 3
cpu4 at mainbus0: apid 4 (application processor)
cpu4: QEMU Virtual CPU version 2.5+, 3300.44 MHz, 06-06-03
cpu4: 
FPU,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,SSE3,CX16,x2APIC,HV,NXE,LONG,LAHF,MELTDOWN
cpu4: 32KB 64b/line 8-way D-cache, 32KB 64b/line 8-way I-cache, 4MB 64b/line 
16-way L2 cache, 16MB 64b/line 16-way L3 cache
cpu4: smt 0, core 0, package 4
cpu5 at mainbus0: apid 5 (application processor)
cpu5: QEMU Virtual CPU version 2.5+, 3300.21 MHz, 06-06-03
cpu5: 
FPU,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,SSE3,CX16,x2APIC,HV,NXE,LONG,LAHF,MELTDOWN
cpu5: 32KB 64b/line 8-way D-cache, 32KB 64b/line 8-way I-cache, 4MB 64b/line 
16-way L2 cache, 16MB 64b/line 16-way L3 cache
cpu5: smt 0, core 0, package 5
cpu6 at mainbus0: apid 6 (application processor)
cpu6: QEMU Virtual CPU version 2.5+, 3300.38 MHz, 06-06-03
cpu6: 
FPU,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,SSE3,CX16,x2APIC,HV,NXE,LONG,LAHF,MELTDOWN
cpu6: 32KB 64b/line 8-way D-cache, 32KB 64b/line 8-way I-cache, 4MB 64b/line 
16-way L2 cache, 16MB 64b/line 16-way L3 cache
cpu6: smt 0, core 0, package 6
cpu7 at mainbus0: apid 7 (application processor)
cpu7: QEMU Virtual CPU version 2.5+, 3300.36 MHz, 06-06-03
cpu7: 
FPU,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,SSE3,CX16,x2APIC,HV,NXE,LONG,LAHF,MELTDOWN
cpu7: 32KB 64b/line 8-way D-cache, 32KB 64b/line 8-way I-cache, 4MB 64b/line 
16-way L2 cache, 16MB 64b/line 16-way L3 cache
cpu7: smt 0, core 0, package 7
ioapic0 at mainbus0: apid 0 pa 0xfec00000, version 11, 24 pins
acpihpet0 at acpi0: 100000000 Hz
acpiprt0 at acpi0: bus 0 (PCI0)
"ACPI0006" at acpi0 not configured
acpipci0 at acpi0 PCI0
com0 at acpi0 COM1 addr 0x3f8/0x8 irq 4: ns16550a, 16 byte fifo
acpicmos0 at acpi0
"PNP0A06" at acpi0 not configured
"PNP0A06" at acpi0 not configured
"PNP0A06" at acpi0 not configured
"QEMU0002" at acpi0 not configured
"ACPI0010" at acpi0 not configured
acpicpu0 at acpi0: C1(@1 halt!)
acpicpu1 at acpi0: C1(@1 halt!)
acpicpu2 at acpi0: C1(@1 halt!)
acpicpu3 at acpi0: C1(@1 halt!)
acpicpu4 at acpi0: C1(@1 halt!)
acpicpu5 at acpi0: C1(@1 halt!)
acpicpu6 at acpi0: C1(@1 halt!)
acpicpu7 at acpi0: C1(@1 halt!)
pvbus0 at mainbus0: KVM
pvclock0 at pvbus0
pci0 at mainbus0 bus 0
pchb0 at pci0 dev 0 function 0 "Intel 82441FX" rev 0x02
pcib0 at pci0 dev 1 function 0 "Intel 82371SB ISA" rev 0x00
pciide0 at pci0 dev 1 function 1 "Intel 82371SB IDE" rev 0x00: DMA, channel 0 
wired to compatibility, channel 1 wired to compatibility
pciide0: channel 0 disabled (no drives)
atapiscsi0 at pciide0 channel 1 drive 0
scsibus1 at atapiscsi0: 2 targets
cd0 at scsibus1 targ 0 lun 0: <QEMU, QEMU DVD-ROM, 2.5+> removable
cd0(pciide0:1:0): using PIO mode 4, DMA mode 2
piixpm0 at pci0 dev 1 function 3 "Intel 82371AB Power" rev 0x03: apic 0 int 9
iic0 at piixpm0
vga1 at pci0 dev 2 function 0 "Bochs VGA" rev 0x02
wsdisplay0 at vga1 mux 1: console (80x25, vt100 emulation)
wsdisplay0: screen 1-5 added (80x25, vt100 emulation)
virtio0 at pci0 dev 3 function 0 "Qumranet Virtio Network" rev 0x00
vio0 at virtio0: address 52:54:00:12:34:56
virtio0: msix shared
virtio1 at pci0 dev 4 function 0 "Qumranet Virtio Storage" rev 0x00
vioblk0 at virtio1
scsibus2 at vioblk0: 1 targets
sd0 at scsibus2 targ 0 lun 0: <VirtIO, Block Device, >
sd0: 8192MB, 512 bytes/sector, 16777216 sectors
virtio1: msix per-VQ
isa0 at pcib0
isadma0 at isa0
fdc0 at isa0 port 0x3f0/6 irq 6 drq 2
pckbc0 at isa0 port 0x60/5 irq 1 irq 12
pckbd0 at pckbc0 (kbd slot)
wskbd0 at pckbd0: console keyboard, using wsdisplay0
pms0 at pckbc0 (aux slot)
wsmouse0 at pms0 mux 0
pcppi0 at isa0 port 0x61
spkr0 at pcppi0
lpt0 at isa0 port 0x378/4 irq 7
vscsi0 at root
scsibus3 at vscsi0: 256 targets
softraid0 at root
scsibus4 at softraid0: 256 targets
root on sd0a (8243d8bd4db9b04b.a) swap on sd0b dump on sd0b

usbdevs:
usbdevs: no USB controllers found

Reply via email to