mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 23:42:34 +00:00
Fixed hang on race condition with UDP-RECV and fork
This commit is contained in:
parent
55518fa690
commit
5570bf4d62
7 changed files with 107 additions and 8 deletions
7
CHANGES
7
CHANGES
|
@ -27,6 +27,13 @@ Corrections:
|
||||||
not writing complete pages.
|
not writing complete pages.
|
||||||
Test: O_DIRECT
|
Test: O_DIRECT
|
||||||
|
|
||||||
|
There was a race condition in the way Socat UDP-RECVFROM and similar
|
||||||
|
addresses with option fork prevents one packet from triggering
|
||||||
|
multiple processes. The symptom was that Socat master process seemed to
|
||||||
|
hang and did not process further packets. The fix makes use of
|
||||||
|
pselect() system call.
|
||||||
|
Thanks to Fulvio Scapin for reporting this issue.
|
||||||
|
|
||||||
Porting:
|
Porting:
|
||||||
In gcc version 10 the default changed from -fcommon to -fno-common.
|
In gcc version 10 the default changed from -fcommon to -fno-common.
|
||||||
Consequently, linking filan and procan failed with error
|
Consequently, linking filan and procan failed with error
|
||||||
|
|
31
compat.h
31
compat.h
|
@ -626,6 +626,33 @@ typedef int sig_atomic_t;
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* default: long */
|
||||||
|
#if !defined(HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC) || !HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC
|
||||||
|
# undef HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC
|
||||||
|
# define HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC 5
|
||||||
|
#endif
|
||||||
|
#ifndef F_tv_nsec
|
||||||
|
# if HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC==1
|
||||||
|
#define F_tv_nsec "%09hd"
|
||||||
|
# elif HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC==2
|
||||||
|
#define F_tv_nsec "%09hu"
|
||||||
|
# elif HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC==3
|
||||||
|
#define F_tv_nsec "%09d"
|
||||||
|
# elif HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC==4
|
||||||
|
#define F_tv_nsec "%09u"
|
||||||
|
# elif HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC==5
|
||||||
|
#define F_tv_nsec "%09ld"
|
||||||
|
# elif HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC==6
|
||||||
|
#define F_tv_nsec "%09lu"
|
||||||
|
# elif HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC==7
|
||||||
|
#define F_tv_nsec "%09Ld"
|
||||||
|
# elif HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC==8
|
||||||
|
#define F_tv_nsec "%09Lu"
|
||||||
|
# else
|
||||||
|
#error "HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC is out of range:" HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/* default: long */
|
/* default: long */
|
||||||
#if !defined(HAVE_TYPEOF_RLIM_MAX) || !HAVE_TYPEOF_RLIM_MAX
|
#if !defined(HAVE_TYPEOF_RLIM_MAX) || !HAVE_TYPEOF_RLIM_MAX
|
||||||
# undef HAVE_TYPEOF_RLIM_MAX
|
# undef HAVE_TYPEOF_RLIM_MAX
|
||||||
|
@ -653,6 +680,10 @@ typedef int sig_atomic_t;
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* sigset_t printing - not an exact solution yet */
|
||||||
|
#define F_sigset "0x%4lx"
|
||||||
|
typedef unsigned long T_sigset;
|
||||||
|
|
||||||
/* default: socklen_t */
|
/* default: socklen_t */
|
||||||
#if !defined(HAVE_TYPEOF_STRUCT_CMSGHDR_CMSG_LEN) || !HAVE_TYPEOF_STRUCT_CMSGHDR_CMSG_LEN
|
#if !defined(HAVE_TYPEOF_STRUCT_CMSGHDR_CMSG_LEN) || !HAVE_TYPEOF_STRUCT_CMSGHDR_CMSG_LEN
|
||||||
# undef HAVE_TYPEOF_STRUCT_CMSGHDR_CMSG_LEN
|
# undef HAVE_TYPEOF_STRUCT_CMSGHDR_CMSG_LEN
|
||||||
|
|
|
@ -63,6 +63,9 @@
|
||||||
/* Define if you have the select function. */
|
/* Define if you have the select function. */
|
||||||
#undef HAVE_SELECT
|
#undef HAVE_SELECT
|
||||||
|
|
||||||
|
/* Define if you have the pselect function. */
|
||||||
|
#undef HAVE_PSELECT
|
||||||
|
|
||||||
/* Define if you have the poll function. */
|
/* Define if you have the poll function. */
|
||||||
#undef HAVE_POLL
|
#undef HAVE_POLL
|
||||||
|
|
||||||
|
@ -599,6 +602,7 @@
|
||||||
#undef HAVE_TYPEOF_ST64_BLOCKS
|
#undef HAVE_TYPEOF_ST64_BLOCKS
|
||||||
|
|
||||||
#undef HAVE_TYPEOF_STRUCT_TIMEVAL_TV_USEC
|
#undef HAVE_TYPEOF_STRUCT_TIMEVAL_TV_USEC
|
||||||
|
#undef HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC
|
||||||
|
|
||||||
#undef HAVE_TYPEOF_RLIM_MAX
|
#undef HAVE_TYPEOF_RLIM_MAX
|
||||||
|
|
||||||
|
|
|
@ -769,7 +769,7 @@ AC_PROG_GCC_TRADITIONAL
|
||||||
AC_FUNC_MEMCMP
|
AC_FUNC_MEMCMP
|
||||||
AC_TYPE_SIGNAL
|
AC_TYPE_SIGNAL
|
||||||
AC_FUNC_STRFTIME
|
AC_FUNC_STRFTIME
|
||||||
AC_CHECK_FUNCS(putenv select poll socket strtod strtol)
|
AC_CHECK_FUNCS(putenv select pselect poll socket strtod strtol)
|
||||||
AC_CHECK_FUNCS(strtoul uname getpgid getsid gethostbyname getaddrinfo)
|
AC_CHECK_FUNCS(strtoul uname getpgid getsid gethostbyname getaddrinfo)
|
||||||
AC_CHECK_FUNCS(setgroups inet_aton)
|
AC_CHECK_FUNCS(setgroups inet_aton)
|
||||||
AC_CHECK_FUNCS()
|
AC_CHECK_FUNCS()
|
||||||
|
@ -1851,6 +1851,8 @@ fi
|
||||||
|
|
||||||
AC_TYPEOF_COMPONENT([#include <sys/time.h>], struct timeval, tv_usec, HAVE_TYPEOF_STRUCT_TIMEVAL_TV_USEC, sc_cv_type_struct_timeval_tv_usec)
|
AC_TYPEOF_COMPONENT([#include <sys/time.h>], struct timeval, tv_usec, HAVE_TYPEOF_STRUCT_TIMEVAL_TV_USEC, sc_cv_type_struct_timeval_tv_usec)
|
||||||
|
|
||||||
|
AC_TYPEOF_COMPONENT([#include <sys/time.h>], struct timespec, tv_nsec, HAVE_TYPEOF_STRUCT_TIMESPEC_TV_NSEC, sc_cv_type_struct_timespec_tv_nsec)
|
||||||
|
|
||||||
AC_TYPEOF_COMPONENT([#include <sys/types.h>
|
AC_TYPEOF_COMPONENT([#include <sys/types.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/resource.h>],
|
#include <sys/resource.h>],
|
||||||
|
|
44
sycls.c
44
sycls.c
|
@ -858,6 +858,50 @@ int Select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_PSELECT
|
||||||
|
/* we only show the first word of the fd_set's; hope this is enough for most
|
||||||
|
cases. */
|
||||||
|
int Pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
|
const struct timespec *timeout, const sigset_t *sigmask) {
|
||||||
|
int result, _errno;
|
||||||
|
if (!diag_in_handler) diag_flush();
|
||||||
|
#if WITH_SYCLS
|
||||||
|
#if HAVE_FDS_BITS
|
||||||
|
Debug8("pselect(%d, &0x%lx, &0x%lx, &0x%lx, %s%lu."F_tv_nsec", "F_sigset")",
|
||||||
|
n, readfds?readfds->fds_bits[0]:0, writefds?writefds->fds_bits[0]:0,
|
||||||
|
exceptfds?exceptfds->fds_bits[0]:0,
|
||||||
|
timeout?"&":"NULL/", timeout?timeout->tv_sec:0,
|
||||||
|
timeout?timeout->tv_nsec:0, *(T_sigset *)sigmask);
|
||||||
|
#else
|
||||||
|
Debug8("pselect(%d, &0x%lx, &0x%lx, &0x%lx, %s%lu.%06u)",
|
||||||
|
n, readfds?readfds->__fds_bits[0]:0, writefds?writefds->__fds_bits[0]:0,
|
||||||
|
exceptfds?exceptfds->__fds_bits[0]:0,
|
||||||
|
timeout?"&":"NULL/", timeout?timeout->tv_sec:0,
|
||||||
|
timeout?timeout->tv_nsec:0);
|
||||||
|
#endif
|
||||||
|
#endif /* WITH_SYCLS */
|
||||||
|
result = pselect(n, readfds, writefds, exceptfds, timeout, sigmask);
|
||||||
|
_errno = errno;
|
||||||
|
if (!diag_in_handler) diag_flush();
|
||||||
|
#if WITH_SYCLS
|
||||||
|
#if HAVE_FDS_BITS
|
||||||
|
Debug5("pselect -> (, 0x%lx, 0x%lx, 0x%lx), "F_sigset", %d",
|
||||||
|
readfds?readfds->fds_bits[0]:0, writefds?writefds->fds_bits[0]:0,
|
||||||
|
exceptfds?exceptfds->fds_bits[0]:0, *(T_sigset *)sigmask,
|
||||||
|
result);
|
||||||
|
#else
|
||||||
|
Debug6("pselect -> (, 0x%lx, 0x%lx, 0x%lx), %d",
|
||||||
|
readfds?readfds->__fds_bits[0]:0, writefds?writefds->__fds_bits[0]:0,
|
||||||
|
exceptfds?exceptfds->__fds_bits[0]:0,
|
||||||
|
result);
|
||||||
|
#endif
|
||||||
|
#endif /* WITH_SYCLS */
|
||||||
|
errno = _errno;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif /* HAVE_PSELECT */
|
||||||
|
|
||||||
#if WITH_SYCLS
|
#if WITH_SYCLS
|
||||||
|
|
||||||
pid_t Fork(void) {
|
pid_t Fork(void) {
|
||||||
|
|
2
sycls.h
2
sycls.h
|
@ -85,6 +85,8 @@ int Chmod(const char *path, mode_t mode);
|
||||||
int Poll(struct pollfd *ufds, unsigned int nfds, int timeout);
|
int Poll(struct pollfd *ufds, unsigned int nfds, int timeout);
|
||||||
int Select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
int Select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
struct timeval *timeout);
|
struct timeval *timeout);
|
||||||
|
int Pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||||
|
const struct timespec *timeout, const sigset_t *sigmask);
|
||||||
#if WITH_SYCLS
|
#if WITH_SYCLS
|
||||||
pid_t Fork(void);
|
pid_t Fork(void);
|
||||||
#endif /* WITH_SYCLS */
|
#endif /* WITH_SYCLS */
|
||||||
|
|
23
xio-socket.c
23
xio-socket.c
|
@ -1389,27 +1389,27 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
|
||||||
xfd->salen = palen;
|
xfd->salen = palen;
|
||||||
|
|
||||||
if (dofork) {
|
if (dofork) {
|
||||||
sigset_t mask_sigchldusr1;
|
sigset_t oldset, mask_sigchldusr1;
|
||||||
|
|
||||||
/* we must prevent that the current packet triggers another fork;
|
/* we must prevent that the current packet triggers another fork;
|
||||||
therefore we wait for a signal from the recent child: USR1
|
therefore we wait for a signal from the recent child: USR1
|
||||||
indicates that is has consumed the last packet; CHLD means it has
|
indicates that is has consumed the last packet; CHLD means it has
|
||||||
terminated */
|
terminated */
|
||||||
/* block SIGCHLD and SIGUSR1 until parent is ready to react */
|
/* block SIGCHLD and SIGUSR1 until parent is ready to react */
|
||||||
sigemptyset(&mask_sigchldusr1);
|
Sigprocmask(SIG_BLOCK, NULL, &mask_sigchldusr1);
|
||||||
sigaddset(&mask_sigchldusr1, SIGCHLD);
|
sigaddset(&mask_sigchldusr1, SIGCHLD);
|
||||||
sigaddset(&mask_sigchldusr1, SIGUSR1);
|
sigaddset(&mask_sigchldusr1, SIGUSR1);
|
||||||
Sigprocmask(SIG_BLOCK, &mask_sigchldusr1, NULL);
|
Sigprocmask(SIG_SETMASK, &mask_sigchldusr1, &oldset);
|
||||||
|
|
||||||
if ((pid = xio_fork(false, level)) < 0) {
|
if ((pid = xio_fork(false, level)) < 0) {
|
||||||
Close(xfd->fd);
|
Close(xfd->fd);
|
||||||
Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
|
Sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||||
return STAT_RETRYLATER;
|
return STAT_RETRYLATER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid == 0) { /* child */
|
if (pid == 0) { /* child */
|
||||||
/* no reason to block SIGCHLD in child process */
|
/* no reason to block SIGCHLD in child process */
|
||||||
Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
|
Sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||||
xfd->ppid = Getppid(); /* send parent a signal when packet has
|
xfd->ppid = Getppid(); /* send parent a signal when packet has
|
||||||
been consumed */
|
been consumed */
|
||||||
|
|
||||||
|
@ -1431,12 +1431,21 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
|
||||||
|
|
||||||
/* server: continue loop with listen */
|
/* server: continue loop with listen */
|
||||||
xio_waitingfor = pid;
|
xio_waitingfor = pid;
|
||||||
|
|
||||||
|
#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 */
|
/* now we are ready to handle signals */
|
||||||
Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL);
|
Sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||||
|
|
||||||
while (!xio_hashappened) {
|
while (!xio_hashappened) {
|
||||||
Sleep(UINT_MAX); /* any signal lets us continue */
|
Sleep(1); /* any signal speeds up return */
|
||||||
}
|
}
|
||||||
|
#endif /* ! HAVE_PSELECT */
|
||||||
xio_waitingfor = 0; /* so this child will not set hashappened again */
|
xio_waitingfor = 0; /* so this child will not set hashappened again */
|
||||||
xio_hashappened = false;
|
xio_hashappened = false;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue