Files
fips/src/native
Johnathan Corgan bc809c4747 fix(native): stop a queued datagram being discarded as end of file
Both halves of a flow's socket pair decided end of file from a zero-byte read
plus a latched POLLHUP. POLLHUP latches when the peer closes and stays set while
its messages are still queued, so the rule was true in a case that is not end of
file.

The cost is a lost payload, not a lost empty datagram. A client that sends an
empty datagram, then a message, then closes leaves both queued. The zero-byte
read of the empty one satisfied every term of the rule, the daemon's drain loop
freed the flow, and the message behind it was never read. The client half lost
the mirror case the same way.

Measured on Linux 6.8 over an AF_UNIX SOCK_SEQPACKET pair. With an empty
datagram and then a three-byte datagram queued from a closed peer, revents is
0x0011, FIONREAD is 3 and recvmsg returns 0: every term of the old rule holds
while a real message waits.

End of file now also requires that nothing is queued behind the read. FIONREAD
answers that in one direction only, and it is the direction that matters: bytes
queued prove a further message is waiting, so it cannot be end of file. A zero
answer proves nothing, because a zero-length message contributes no bytes. That
one-directional reading is also what makes the check safe on every platform,
whichever socket type it chose and however its kernel signals a close.

One case therefore survives, and the documentation now says so rather than
asserting the opposite. A zero-length datagram that is the last message before a
close cannot be told from the close: reading it drains the queue, and the socket
is then identical to a drained one in revents, in FIONREAD, under MSG_PEEK and
in the recvmsg return. Separating those needs a payload that is never zero bytes
on the wire, which is a protocol change and is not made here.

The check is shared rather than written twice, because the end-of-file rule
belongs to the pair rather than to the end that reads it, and the two halves had
already drifted apart once.

Each guard is covered by a test that fails without it: the daemon half reports
end of file where a datagram is due, and the client half reports EPIPE.
2026-08-30 08:27:00 +00:00
..