From 942003502fa62005a442cd44347d399c8f9970ae Mon Sep 17 00:00:00 2001 From: Martin Osvald Date: Fri, 30 Jan 2026 10:46:18 +0100 Subject: [PATCH] Handle EINTR on recvmsg() Resolves: RHEL-143222 --- socat-1.7.4.1-handle-EINTR-on-recvmsg.patch | 161 ++++++++++++++++++++ socat.spec | 9 +- 2 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 socat-1.7.4.1-handle-EINTR-on-recvmsg.patch diff --git a/socat-1.7.4.1-handle-EINTR-on-recvmsg.patch b/socat-1.7.4.1-handle-EINTR-on-recvmsg.patch new file mode 100644 index 0000000..24bef64 --- /dev/null +++ b/socat-1.7.4.1-handle-EINTR-on-recvmsg.patch @@ -0,0 +1,161 @@ +diff --git a/CHANGES b/CHANGES +index 794b7a7..b2046e1 100644 +--- a/CHANGES ++++ b/CHANGES +@@ -91,6 +91,11 @@ Corrections: + + Fixed some more non functional minor issues. + ++ UDP-RECVFROM with fork sometimes terminated when multiple packets ++ arrived. This issue was introduced with a bug fix in version 1.7.4.0. ++ Reason was not handling EAGAIN on recvmsg(). ++ Thanks to Jamie McQuillan for reporting this issue. ++ + Porting: + In gcc version 10 the default changed from -fcommon to -fno-common. + Consequently, linking filan and procan failed with error +diff --git a/xio-socket.c b/xio-socket.c +index d6b935c..4254394 100644 +--- a/xio-socket.c ++++ b/xio-socket.c +@@ -1214,6 +1214,7 @@ void xiosigaction_hasread(int signum + return; + } + if (pid == xio_waitingfor) { ++ xio_waitingfor = 0; /* so this child will not set hashappened again */ + xio_hashappened = true; + xio_childstatus = WEXITSTATUS(status); + Debug("xiosigaction_hasread() ->"); +@@ -1253,6 +1254,7 @@ void xiosigaction_hasread(int signum + PH_INIT, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY, PH_PREOPEN, PH_FD, + PH_CONNECTED, PH_LATE, PH_LATE2 + OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, cloexec, OPT_RANGE, tcpwrap ++ EINTR is not handled specially. + */ + int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, + struct sockaddr *us, socklen_t uslen, +@@ -1382,6 +1384,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, + socklen_t palen = sizeof(_peername); /* peer address size */ + char ctrlbuff[1024]; /* ancillary messages */ + struct msghdr msgh = {0}; ++ int rc; + + socket_init(pf, pa); + +@@ -1392,6 +1395,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, + drop = true; + } + ++ Info("Recvfrom: Checking/waiting for next packet"); + /* loop until select()/poll() returns valid */ + do { + struct pollfd readfd; +@@ -1424,9 +1428,9 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, + #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN + msgh.msg_controllen = sizeof(ctrlbuff); + #endif +- if (xiogetpacketsrc(xfd->fd, &msgh) < 0) { +- return STAT_RETRYLATER; +- } ++ while ((rc = xiogetpacketsrc(xfd->fd, &msgh)) < 0 && ++ errno == EINTR) ; ++ if (rc < 0) return STAT_RETRYLATER; + palen = msgh.msg_namelen; + + Notice1("receiving packet from %s"/*"src"*/, +@@ -1497,24 +1501,21 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, + break; + } + +- /* server: continue loop with listen */ + xio_waitingfor = pid; + ++ do { + #if HAVE_PSELECT +- { ++ { + struct timespec timeout = { LONG_MAX, 0 }; + Pselect(0, NULL, NULL, NULL, &timeout, &oldset); + Sigprocmask(SIG_SETMASK, &oldset, NULL); +- } ++ } + #else /* ! HAVE_PSELECT */ +- /* now we are ready to handle signals */ +- Sigprocmask(SIG_SETMASK, &oldset, NULL); +- +- while (!xio_hashappened) { +- Sleep(1); /* any signal speeds up return */ +- } ++ /* now we are ready to handle signals */ ++ Sigprocmask(SIG_SETMASK, &oldset, NULL); ++ Sleep(1); /* any signal speeds up return */ + #endif /* ! HAVE_PSELECT */ +- xio_waitingfor = 0; /* so this child will not set hashappened again */ ++ } while (!xio_hashappened) ; + xio_hashappened = false; + + if (xio_childstatus != 0) { +@@ -1639,9 +1640,12 @@ int retropt_socket_pf(struct opt *opts, int *pf) { + } + + +-/* this function calls recvmsg(..., MSG_PEEK, ...) to obtain information about +- the arriving packet. in msgh the msg_name pointer must refer to an (empty) +- sockaddr storage. */ ++/* This function calls recvmsg(..., MSG_PEEK, ...) to obtain information about ++ the arriving packet, thus it does not "consume" the packet. ++ In msgh the msg_name pointer must refer to an (empty) sockaddr storage. ++ Returns STAT_OK on success, or STAT_RETRYLATER when an error occurred, ++ including EINTR. ++ */ + int xiogetpacketsrc(int fd, struct msghdr *msgh) { + char peekbuff[1]; + #if HAVE_STRUCT_IOVEC +diff --git a/xioread.c b/xioread.c +index 24f3c88..b89e78a 100644 +--- a/xioread.c ++++ b/xioread.c +@@ -118,6 +118,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { + socklen_t fromlen = sizeof(from); + char infobuff[256]; + char ctrlbuff[1024]; /* ancillary messages */ ++ int rc; + + msgh.msg_name = &from; + msgh.msg_namelen = fromlen; +@@ -127,9 +128,10 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { + #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN + msgh.msg_controllen = sizeof(ctrlbuff); + #endif +- if (xiogetpacketsrc(pipe->fd, &msgh) < 0) { +- return -1; +- } ++ while ((rc = xiogetpacketsrc(pipe->fd, &msgh)) < 0 && ++ errno == EINTR) ; ++ if (rc < 0) return -1; ++ + do { + bytes = + Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen); +@@ -313,6 +315,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { + char infobuff[256]; + struct msghdr msgh = {0}; + char ctrlbuff[1024]; /* ancillary messages */ ++ int rc; + + socket_init(pipe->para.socket.la.soa.sa_family, &from); + /* get source address */ +@@ -324,9 +327,10 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { + #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN + msgh.msg_controllen = sizeof(ctrlbuff); + #endif +- if (xiogetpacketsrc(pipe->fd, &msgh) < 0) { +- return -1; +- } ++ while ((rc = xiogetpacketsrc(pipe->fd, &msgh)) < 0 && ++ errno == EINTR) ; ++ if (rc < 0) return -1; ++ + xiodopacketinfo(&msgh, true, false); + if (xiocheckpeer(pipe, &from, &pipe->para.socket.la) < 0) { + Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen); /* drop */ diff --git a/socat.spec b/socat.spec index 02c6618..081fe9b 100644 --- a/socat.spec +++ b/socat.spec @@ -3,7 +3,7 @@ Summary: Bidirectional data relay between two data channels ('netcat++') Name: socat Version: 1.7.4.1 -Release: 8%{?dist} +Release: 9%{?dist} License: GPLv2 Url: http://www.dest-unreach.org/socat/ Source: http://www.dest-unreach.org/socat/download/%{name}-%{version}.tar.gz @@ -16,6 +16,9 @@ Patch3: socat-1.7.4.1-CVE-2024-54661.patch # https://issues.redhat.com/browse/RHEL-107884 # Based on: https://repo.or.cz/socat.git/commit/ed4780553fd05bb8ed8a29462698090482be3393 Patch4: socat-1.7.4.1-tcp-address-with-connect-timeout.patch +# https://issues.redhat.com/browse/RHEL-143222 +# Based on: https://repo.or.cz/socat.git/commit/45d87df2fd32756b56f8a48c2d6cdb476342aa20 +Patch5: socat-1.7.4.1-handle-EINTR-on-recvmsg.patch BuildRequires: make BuildRequires: gcc @@ -80,6 +83,10 @@ export OD_C=/usr/bin/od %doc %{_mandir}/man1/* %changelog +* Fri Jan 30 2026 Martin Osvald - 1.7.4.1-9 +- Handle EINTR on recvmsg() + Resolves: RHEL-143222 + * Thu Aug 07 2025 Martin Osvald - 1.7.4.1-8 - Fix for: Client may exit 0 despite connection being reset Resolves: RHEL-107884