Skip to content

Commit

Permalink
Report error when file descriptor number is too large for select() in…
Browse files Browse the repository at this point in the history
… socket

code or when there are too many descriptors (PR#18634).


git-svn-id: https://svn.r-project.org/R/trunk@85689 00db46b3-68df-0310-9c12-caf00c1e9a41
  • Loading branch information
kalibera committed Dec 15, 2023
1 parent 521a8a7 commit 777ad47
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 3 deletions.
27 changes: 27 additions & 0 deletions src/main/connections.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@
Using a dynamic upper limit would not be hard, but not very useful
because of the non-dynamic fd limit.
The current implementation of socket connections uses select(). On
POSIX systems, only FD_SETSIZE descriptors are supported and they
must have numbers between 0 and FD_SETSIZE-1, inclusive. On Linux and macOS,
FD_SETSIZE is normally 1024. On macOS, the limit could be overcome via
_DARWIN_UNLIMITED_SELECT (not used by R), but a POSIX solution would
be to use poll() instead of select(). On Windows, by default 1024 different
descriptors are supported in a single select() call, but these include
valid socket file descriptors of arbitrary numbers, much larger
than FD_SETSIZE. On Windows, the limit can be set in the program
by setting FD_SETSIZE before including WinSock headers, R sets it to
1024 (in sock.h and here in connections.c).
*/

#ifdef HAVE_CONFIG_H
Expand Down Expand Up @@ -6323,6 +6335,13 @@ attribute_hidden SEXP do_sockselect(SEXP call, SEXP op, SEXP args, SEXP rho)
int nsock, i;
SEXP insock, write, val, insockfd;
double timeout;
int fdlim;

#ifdef Win32
fdlim = 1024; /* keep in step with sock.h */
#else
fdlim = FD_SETSIZE;
#endif

checkArity(op, args);

Expand Down Expand Up @@ -6358,8 +6377,16 @@ attribute_hidden SEXP do_sockselect(SEXP call, SEXP op, SEXP args, SEXP rho)
warning(_("a server socket connection cannot be writeable"));
} else
error(_("not a socket connection"));
#ifdef Unix
if (INTEGER(insockfd)[i] >= fdlim && !immediate)
error(_("file descriptor is too large for select()"));
#endif
}

#ifdef Win32
if (nsock > fdlim && !immediate)
error(_("too many file descriptors for select()"));
#endif
if (! immediate)
Rsockselect(nsock, INTEGER(insockfd), LOGICAL(val), LOGICAL(write),
timeout);
Expand Down
21 changes: 20 additions & 1 deletion src/modules/internet/Rsock.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 1998-2021 The R Core Team
* Copyright (C) 1998-2023 The R Core Team
* Copyright (C) 1996, 1997 Robert Gentleman and Ross Ihaka
*
* This program is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -196,6 +196,7 @@ setSelectMask(InputHandler *handlers, fd_set *readMask)

while(tmp) {
if(tmp->fileDescriptor > 0) {
/* FD_SETSIZE limit checked by addInputHandler */
FD_SET(tmp->fileDescriptor, readMask);
maxfd = maxfd < tmp->fileDescriptor ? tmp->fileDescriptor : maxfd;
}
Expand Down Expand Up @@ -237,6 +238,8 @@ static int R_SocketWait(int sockfd, int write, int timeout)
set_timeval(&tv, timeout);

#ifdef Unix
if (sockfd >= FD_SETSIZE)
return -EINVAL;
maxfd = setSelectMask(R_InputHandlers, &rfd);
#else
FD_ZERO(&rfd);
Expand Down Expand Up @@ -324,7 +327,15 @@ int R_SocketWaitMultiple(int nsock, int *insockfd, int *ready, int *write,
FD_ZERO(&rfd);
#endif
FD_ZERO(&wfd);
#ifdef Win32
if (nsock > FD_SETSIZE)
return -WSAEINVAL;
#endif
for (i = 0; i < nsock; i++) {
#ifdef Unix
if (insockfd[i] >= FD_SETSIZE)
return -EINVAL;
#endif
if(write[i]) FD_SET(insockfd[i], &wfd);
else FD_SET(insockfd[i], &rfd);
if(maxfd < insockfd[i]) maxfd = insockfd[i];
Expand Down Expand Up @@ -432,6 +443,10 @@ int R_SockConnect(int port, char *host, int timeout)
set_timeval(&tv, timeout);

#ifdef Unix
if (s >= FD_SETSIZE) {
errno = EINVAL;
CLOSE_N_RETURN(-1);
}
maxfd = setSelectMask(R_InputHandlers, &rfd);
#else
FD_ZERO(&rfd);
Expand Down Expand Up @@ -567,6 +582,10 @@ int R_SockListen(int sockp, char *buf, int len, int timeout)
set_timeval(&tv, timeout);

#ifdef Unix
if (sockp >= FD_SETSIZE) {
errno = EINVAL;
return -1;
}
maxfd = setSelectMask(R_InputHandlers, &rfd);
#else
FD_ZERO(&rfd);
Expand Down
3 changes: 2 additions & 1 deletion src/modules/internet/sock.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 1998-2020 The R Core Team
* Copyright (C) 1998-2023 The R Core Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -42,6 +42,7 @@ ssize_t Sock_write(int fd, const void *buf, size_t nbytes, Sock_error_t perr);
#ifndef Win32
# define SOCKET int
#else
/* keep FD_SETSIZE in step with connections.c, but this is the default */
# define FD_SETSIZE 1024
# include<winsock2.h>
#endif
Expand Down
27 changes: 26 additions & 1 deletion src/modules/internet/sockconn.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 2001-2022 The R Core Team.
* Copyright (C) 2001-2023 The R Core Team.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -55,6 +55,14 @@ static Rboolean sock_open(Rconnection con)
warning("port %d cannot be opened", this->port);
return FALSE;
}
#ifdef Unix
if (sock1 >= FD_SETSIZE) {
/* R_SockListen below would fail */
R_SockClose(sock1);
warning(_("file descriptor is too large for select()"));
return FALSE;
}
#endif
{
RCNTXT cntxt;

Expand All @@ -81,6 +89,14 @@ static Rboolean sock_open(Rconnection con)
return FALSE;
}
}
#ifdef Unix
if (sock >= FD_SETSIZE && (con->canwrite || con->blocking)) {
/* Reading/writing via such socket would fail */
R_SockClose(sock);
warning(_("file descriptor is too large for select()"));
return FALSE;
}
#endif
free(con->description);
size_t sz = strlen(buf) + 10;
con->description = (char *) malloc(sz); // FIXME check allocation
Expand Down Expand Up @@ -272,6 +288,15 @@ Rconnection in_R_newservsock(int port)
port);
/* for Solaris 12.5 */ new = NULL;
}
#ifdef Unix
if (sock >= FD_SETSIZE) {
/* R_SockListen (accept) called from sock_open would fail */
free(new->private); free(new->description); free(new->class); free(new);
R_SockClose(sock);
error(_("file descriptor is too large for select()"));
/* for Solaris 12.5 */ new = NULL;
}
#endif
((Rservsockconn)new->private)-> fd = sock;
new->isopen = TRUE;

Expand Down

0 comments on commit 777ad47

Please sign in to comment.