Audit report AR-08
Reported privately in an independent security review, 2026-08-07.
- Report date
- 2026-08-07
- Kind
- Bug
- Status on master
- Fixed in bitcoin#35796
- Proposed issue state
- Closed
- Linked bugs
- Bug 80: macOS IPC listener stops accepting connections after a queued client disconnects
Summary
On macOS, a client that disconnects while still queued for accept() makes Cap'n Proto's accepted-socket setup throw EINVAL, and the IPC listener stops accepting connections for good. The report restates the public issue mp#319. Real bug in Cap'n Proto's kj library; fixed in Bitcoin Core release builds by a depends patch (bitcoin#35796), still present for builds against an older system Cap'n Proto.
The report
The report cites mp#319 and summarizes it: a queued Unix-socket client that disconnects before the server accepts it can make Cap'n Proto's accepted-socket setup throw EINVAL on macOS; the exception escapes the accept loop, and the listener permanently stops accepting connections. It lists a dead queued Unix socket on macOS among its suggested regression tests. It gives no separate reproduction or fix, and marks the finding as already public.
Proposed change
None beyond what the public issue discusses.
Assessment
Correct. After accept(), kj's acceptImpl calls setsockopt(TCP_NODELAY) on the new socket and tolerates EINVAL only on FreeBSD. macOS returns EINVAL for a Unix socket whose peer has already closed, so the call throws. The exception escapes the accept().then(...) chain that mp::ListenConnections (include/mp/proxy-io.h) runs as a daemonized task, and nothing restarts the listener. The node keeps running, but every later client gets "Connection refused".
No hostile peer is needed: any process that connects and exits while queued, including a killed C++ client, leaves such a socket. It was first seen in macOS CI for the test added in mp#310 on 2026-07-17, and reproduced against bitcoin-node with a saturated connection limit.
The defect is in Cap'n Proto, not in libmultiprocess. It was fixed upstream in capnproto#2748, and Bitcoin Core ships that commit as a depends patch since bitcoin#35796 (merged 2026-09-09). A retry loop inside libmultiprocess was considered in mp#310 and rejected, because an unconditional retry would spin forever on permanent errors such as descriptor exhaustion.
Status
Fixed on Bitcoin Core master for depends builds. mp#319 stays open because builds against a system Cap'n Proto need a Cap'n Proto release that contains capnproto#2748. No further action in libmultiprocess.
Reported privately in an independent security review on 2026-08-07.