Handle EINTR on recvmsg()

Resolves: RHEL-143222
This commit is contained in:
Martin Osvald 2026-01-30 10:46:18 +01:00
parent 912c717fe7
commit 942003502f
2 changed files with 169 additions and 1 deletions

View File

@ -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 */

View File

@ -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 <mosvald@redhat.com> - 1.7.4.1-9
- Handle EINTR on recvmsg()
Resolves: RHEL-143222
* Thu Aug 07 2025 Martin Osvald <mosvald@redhat.com> - 1.7.4.1-8
- Fix for: Client may exit 0 despite connection being reset
Resolves: RHEL-107884