From 9cdac1cb60aa9590b36623243f5462d604307667 Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Sun, 25 Oct 2026 12:54:46 +0300 Subject: [PATCH] splice() pipes closed with fdclose(), the loop forgetting them first sockmap waits on its splice() pipes as on its sockets, and on an event loop a pipe waited on is registered there like a socket. They were closed with close(), past the socket functions, so the loop kept the registration of a closed descriptor, which epoll and kqueue drop on the close: a socket given the same number later was taken for it, never registered, and its fiber waited on it forever. fdclose() tells the loop first. Co-Authored-By: Claude Opus 5.5 --- src/common.c | 10 ++++++++++ src/mainclient.c | 8 ++++---- src/proxy.h | 3 +++ src/sockmap.c | 8 ++++---- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/common.c b/src/common.c index 5016d14..03d5435 100644 --- a/src/common.c +++ b/src/common.c @@ -492,6 +492,16 @@ struct sockfuncs so = { def_closesocket }; +#ifndef _WIN32 +/* close() of a descriptor a fiber may have waited on, a pipe of splice(): + its loop forgets it first, or a socket given the same number later would + be taken for it, and never registered. */ +int fdclose(int fd){ + if(fiberclosefunc) fiberclosefunc(fd); + return close(fd); +} +#endif + /* Whether s, found readable, is: data or its end there. A fiber's wait may answer with what a socket was reported ready for, until an operation finds it not to be: whatever takes readability for an end, diff --git a/src/mainclient.c b/src/mainclient.c index 47ea0f5..9bf0a0d 100644 --- a/src/mainclient.c +++ b/src/mainclient.c @@ -393,10 +393,10 @@ void releaseparam(struct clientparam * param) { param->clibuf = param->clibufdata; param->srvbuf = param->srvbufdata; #ifdef WITHSPLICE - if(param->pipecli[0] >= 0) close(param->pipecli[0]); - if(param->pipecli[1] >= 0) close(param->pipecli[1]); - if(param->pipesrv[0] >= 0) close(param->pipesrv[0]); - if(param->pipesrv[1] >= 0) close(param->pipesrv[1]); + if(param->pipecli[0] >= 0) fdclose(param->pipecli[0]); + if(param->pipecli[1] >= 0) fdclose(param->pipecli[1]); + if(param->pipesrv[0] >= 0) fdclose(param->pipesrv[0]); + if(param->pipesrv[1] >= 0) fdclose(param->pipesrv[1]); param->pipecli[0] = param->pipecli[1] = param->pipesrv[0] = param->pipesrv[1] = -1; #endif /* Not shut down on Windows: there shutting down receiving resets the diff --git a/src/proxy.h b/src/proxy.h index 998e7f1..326d451 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -337,6 +337,9 @@ extern int (*fiberpollfunc)(struct pollfd *fds, unsigned nfds, int timeout); extern void (*fiberblockfunc)(SOCKET s, short events); extern void (*fiberclosefunc)(SOCKET s); int sockreadable(SOCKET s); +#ifndef _WIN32 +int fdclose(int fd); +#endif void nonblock(SOCKET sock); int sysresolves(int mode); int acceptagain(void); diff --git a/src/sockmap.c b/src/sockmap.c index c2b547f..e625ef3 100644 --- a/src/sockmap.c +++ b/src/sockmap.c @@ -1009,13 +1009,13 @@ CLEANRET: /* empty pipes stay with the connection for its next mapping; one still holding data, left by an error or a timeout, cannot be used again */ if(inclientpipe && pipecli[0] >= 0){ - close(pipecli[0]); - close(pipecli[1]); + fdclose(pipecli[0]); + fdclose(pipecli[1]); pipecli[0] = pipecli[1] = -1; } if(inserverpipe && pipesrv[0] >= 0){ - close(pipesrv[0]); - close(pipesrv[1]); + fdclose(pipesrv[0]); + fdclose(pipesrv[1]); pipesrv[0] = pipesrv[1] = -1; } #endif