mirror of
https://github.com/jmcorgan/fips.git
synced 2026-10-05 19:18:25 +00:00
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.