From 3e0ac44cdf415865ff70f5cad75e6ff18e754d6b Mon Sep 17 00:00:00 2001 From: Mike Miller Date: Wed, 29 Jul 2026 11:37:35 -0700 Subject: [PATCH] Implement accept4 accept4 is wired to syscall_stub, so it returns ENOSYS. Go's net package uses it for every accepted connection, which is what #1889 and #2553 both report: "accept tcp [::]:9999: accept4: function not implemented". It is accept plus two flags that apply to the accepted fd: SOCK_CLOEXEC sets FD_CLOEXEC on it and SOCK_NONBLOCK sets O_NONBLOCK on it, neither touching the listener. Any other flag is EINVAL. The socketcall entry is filled in to match. Verified against real Linux with the same static i386 binary, over a loopback TCP connection: before after / real Linux accept4(flags=0) ENOSYS connected fd SOCK_CLOEXEC sets FD_CLOEXEC ENOSYS set listener FD_CLOEXEC untouched ok ok SOCK_NONBLOCK sets O_NONBLOCK ENOSYS set nonblocking accepted fd reads ENOSYS EAGAIN peer address filled in ENOSYS AF_INET bogus flags ENOSYS EINVAL --- fs/sock.c | 20 +++++++++++++++++++- fs/sock.h | 1 + kernel/calls.c | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/fs/sock.c b/fs/sock.c index 4c62f1c485..761a5c2140 100644 --- a/fs/sock.c +++ b/fs/sock.c @@ -450,6 +450,24 @@ int_t sys_accept(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr) { return client_f; } +int_t sys_accept4(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr, int_t flags) { + STRACE("accept4(%d, 0x%x, 0x%x, %d) ", sock_fd, sockaddr_addr, sockaddr_len_addr, flags); + if (flags & ~(SOCK_NONBLOCK_ | SOCK_CLOEXEC_)) + return _EINVAL; + int_t client_f = sys_accept(sock_fd, sockaddr_addr, sockaddr_len_addr); + if (client_f < 0) + return client_f; + // The flags apply to the accepted fd, not the listener. + if (flags & SOCK_CLOEXEC_) + bit_set(client_f, current->files->cloexec); + if (flags & SOCK_NONBLOCK_) { + struct fd *client = f_get(client_f); + if (client != NULL) + fd_setflags(client, fd_getflags(client) | O_NONBLOCK_); + } + return client_f; +} + static void copy_unix_name(char *sockaddr, dword_t *sockaddr_len, struct fd *sock) { struct sockaddr_ *fake_addr = (void *) sockaddr; fake_addr->family = PF_LOCAL_; @@ -1214,7 +1232,7 @@ static struct socket_call { {(syscall_t) sys_getsockopt, 5}, {(syscall_t) sys_sendmsg, 3}, {(syscall_t) sys_recvmsg, 3}, - {NULL}, // accept4 + {(syscall_t) sys_accept4, 4}, {NULL}, // recvmmsg {(syscall_t) sys_sendmmsg, 4}, }; diff --git a/fs/sock.h b/fs/sock.h index ce40ca5d6a..2df3064ea0 100644 --- a/fs/sock.h +++ b/fs/sock.h @@ -16,6 +16,7 @@ int_t sys_bind(fd_t sock_fd, addr_t sockaddr_addr, uint_t sockaddr_len); int_t sys_connect(fd_t sock_fd, addr_t sockaddr_addr, uint_t sockaddr_len); int_t sys_listen(fd_t sock_fd, int_t backlog); int_t sys_accept(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr); +int_t sys_accept4(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr, int_t flags); int_t sys_getsockname(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr); int_t sys_getpeername(fd_t sock_fd, addr_t sockaddr_addr, addr_t sockaddr_len_addr); int_t sys_socketpair(dword_t domain, dword_t type, dword_t protocol, addr_t sockets_addr); diff --git a/kernel/calls.c b/kernel/calls.c index 23e3306b87..b215b8bfc2 100644 --- a/kernel/calls.c +++ b/kernel/calls.c @@ -234,7 +234,7 @@ syscall_t syscall_table[] = { [361] = (syscall_t) sys_bind, [362] = (syscall_t) sys_connect, [363] = (syscall_t) sys_listen, - [364] = (syscall_t) syscall_stub, // accept4 + [364] = (syscall_t) sys_accept4, [365] = (syscall_t) sys_getsockopt, [366] = (syscall_t) sys_setsockopt, [367] = (syscall_t) sys_getsockname,