Problem Description
On macOS hosts, elfuse emulates Linux netlink route sockets (AF_NETLINK, NETLINK_ROUTE) using a host pipe.
Currently, the write end of this pipe (pipefd[1]) is closed immediately at socket creation in netlink_socket(). This makes the read end of the pipe (pipefd[0]) permanently readable, returning EOF (0 bytes) when read.
When guest applications (such as avahi-daemon) run event loops that wait for netlink messages using ppoll() or select(), the emulated descriptor immediately returns POLLIN because the write end is closed. The application's subsequent recvmsg or read call returns 0 bytes. Because no sender credentials can be resolved from a 0-byte read, the application ignores the read and immediately queries ppoll() again. This causes the guest application to spin in an infinite busy-loop consuming 100% CPU.
Proposed Fix
Implement a non-blocking self-pipe signaling mechanism inside src/syscall/netlink.c:
- Retain the write descriptor: Keep the write descriptor of the pipe (
pipe_wr) open in the netlink socket state (netlink_state_t) instead of closing it at socket creation. Ensure it is closed during netlink_close() to prevent file descriptor leaks.
- Signal readability: Write a dummy byte to
pipe_wr when populating synthetic responses in the netlink buffer.
- Clear readability: Read the dummy byte back from the host descriptor once the guest has fully drained the buffer.
- Return
-EAGAIN: Change netlink_recvmsg and netlink_read to return -EAGAIN (instead of 0) when the buffer is empty, indicating standard non-blocking socket behavior.
Location: src/syscall/netlink.c.
Problem Description
On macOS hosts,
elfuseemulates Linux netlink route sockets (AF_NETLINK,NETLINK_ROUTE) using a host pipe.Currently, the write end of this pipe (
pipefd[1]) is closed immediately at socket creation innetlink_socket(). This makes the read end of the pipe (pipefd[0]) permanently readable, returning EOF (0bytes) when read.When guest applications (such as
avahi-daemon) run event loops that wait for netlink messages usingppoll()orselect(), the emulated descriptor immediately returnsPOLLINbecause the write end is closed. The application's subsequentrecvmsgorreadcall returns0bytes. Because no sender credentials can be resolved from a0-byte read, the application ignores the read and immediately queriesppoll()again. This causes the guest application to spin in an infinite busy-loop consuming 100% CPU.Proposed Fix
Implement a non-blocking self-pipe signaling mechanism inside
src/syscall/netlink.c:pipe_wr) open in the netlink socket state (netlink_state_t) instead of closing it at socket creation. Ensure it is closed duringnetlink_close()to prevent file descriptor leaks.pipe_wrwhen populating synthetic responses in the netlink buffer.-EAGAIN: Changenetlink_recvmsgandnetlink_readto return-EAGAIN(instead of0) when the buffer is empty, indicating standard non-blocking socket behavior.Location:
src/syscall/netlink.c.