diff options
| author | Wez Furlong <wez@php.net> | 2004-09-17 12:44:56 +0000 |
|---|---|---|
| committer | Wez Furlong <wez@php.net> | 2004-09-17 12:44:56 +0000 |
| commit | 99e290f882c9116e74418b9271a75d557533c4f5 (patch) | |
| tree | 737c6e4ec61d02067b60372407542e3235d511e6 /main/network.c | |
| parent | 9085689d6faec9eeae6802638ff2dea233d536b8 (diff) | |
| download | php-git-99e290f882c9116e74418b9271a75d557533c4f5.tar.gz | |
Fix for Bug #24189: possibly unsafe select(2) usage.
We avoid the problem by using poll(2).
On systems without poll(2) (older bsd-ish systems, and win32), we emulate
poll(2) using select(2) and check for valid descriptors before attempting
to access them via the descriptor sets.
If an out-of-range descriptor is detected, an E_WARNING is raised suggesting
that PHP should be recompiled with a larger FD_SETSIZE (and also with a
suggested value).
Most uses of select(2) in the source are to poll a single descriptor, so
a couple of handy wrapper functions have been added to make this easier.
A configure option --enable-fd-setsize has been added to both the unix and
win32 builds; on unix we default to 16384 and on windows we default to 256.
Windows FD_SETSIZE imposes a limit on the maximum number of descriptors that
can be select()ed at once, whereas the unix FD_SETSIZE limit is based on the
highest numbered descriptor; 256 should be plenty for PHP scripts under windows
(the default OS setting is 64).
The win32 specific parts are untested; will do that now.
Diffstat (limited to 'main/network.c')
| -rw-r--r-- | main/network.c | 120 |
1 files changed, 102 insertions, 18 deletions
diff --git a/main/network.c b/main/network.c index 1de7ff98c4..86d71cd14d 100644 --- a/main/network.c +++ b/main/network.c @@ -295,9 +295,6 @@ PHPAPI int php_network_connect_socket(php_socket_t sockfd, int error = 0; socklen_t len; int ret = 0; - fd_set rset; - fd_set wset; - fd_set eset; SET_SOCKET_BLOCKING_MODE(sockfd, orig_flags); @@ -325,18 +322,11 @@ PHPAPI int php_network_connect_socket(php_socket_t sockfd, goto ok; } - FD_ZERO(&rset); - FD_ZERO(&eset); - FD_SET(sockfd, &rset); - FD_SET(sockfd, &eset); - - wset = rset; - - if ((n = select(sockfd + 1, &rset, &wset, &eset, timeout)) == 0) { + if ((n = php_pollfd_for(sockfd, PHP_POLLREADABLE|POLLOUT, timeout)) == 0) { error = PHP_TIMEOUT_ERROR_VALUE; } - if(FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { + if (n > 0) { len = sizeof(error); /* BSD-derived systems set errno correctly @@ -692,21 +682,17 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock, TSRMLS_DC) { php_socket_t clisock = -1; - fd_set rset; int error = 0, n; php_sockaddr_storage sa; socklen_t sl; - - FD_ZERO(&rset); - FD_SET(srvsock, &rset); - n = select(srvsock + 1, &rset, NULL, NULL, timeout); + n = php_pollfd_for(srvsock, PHP_POLLREADABLE, timeout); if (n == 0) { error = PHP_TIMEOUT_ERROR_VALUE; } else if (n == -1) { error = php_socket_errno(); - } else if (FD_ISSET(srvsock, &rset)) { + } else { sl = sizeof(sa); clisock = accept(srvsock, (struct sockaddr*)&sa, &sl); @@ -1026,6 +1012,104 @@ PHPAPI int php_set_sock_blocking(int socketd, int block TSRMLS_DC) return ret; } +PHPAPI void _php_emit_fd_setsize_warning(int max_fd) +{ + TSRMLS_FETCH(); + +#ifdef PHP_WIN32 + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "PHP needs to be recompiled with a larger value of FD_SETSIZE.\n" + "If this binary is from an official www.php.net package, file a bug report\n" + "at http://bugs.php.net, including the following information:\n" + "FD_SETSIZE=%d, but you are using %d.\n" + " --enable-fd-setsize=%d is recommended, but you may want to set it\n" + "to match to maximum number of sockets each script will work with at\n" + "one time, in order to avoid seeing this error again at a later date.", + FD_SETSIZE, max_fd, (max_fd + 128) & ~127); +#else + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "You MUST recompile PHP with a larger value of FD_SETSIZE.\n" + "It is set to %d, but you have descriptors numbered at least as high as %d.\n" + " --enable-fd-setsize=%d is recommended, but you may want to set it\n" + "to equal the maximum number of open files supported by your system,\n" + "in order to avoid seeing this error again at a later date.", + FD_SETSIZE, max_fd, (max_fd + 1024) & ~1023); +#endif +} + +#if defined(PHP_USE_POLL_2_EMULATION) + +/* emulate poll(2) using select(2), safely. */ + +PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout) +{ + fd_set rset, wset, eset; + php_socket_t max_fd = SOCK_ERR; + unsigned int i, n; + struct timeval tv; + + /* check the highest numbered descriptor */ + for (i = 0; i < nfds; i++) { + if (ufds[i].fd > max_fd) + max_fd = ufds[i].fd; + } + + PHP_SAFE_MAX_FD(max_fd, nfds + 1); + + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_ZERO(&eset); + + for (i = 0; i < nfds; i++) { + if (ufds[i].fd >= FD_SETSIZE) { + /* unsafe to set */ + ufds[i].revents = POLLNVAL; + continue; + } + if (ufds[i].events & PHP_POLLREADABLE) { + FD_SET(ufds[i].fd, &rset); + } + if (ufds[i].events & POLLOUT) { + FD_SET(ufds[i].fd, &wset); + } + if (ufds[i].events & POLLPRI) { + FD_SET(ufds[i].fd, &eset); + } + } + + if (timeout >= 0) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000; + } + + n = select(max_fd + 1, &rset, &wset, &eset, timeout >= 0 ? &tv : NULL); + + if (n >= 0) { + for (i = 0; i < nfds; i++) { + if (ufds[i].fd >= FD_SETSIZE) { + continue; + } + + ufds[i].revents = 0; + + if (FD_ISSET(ufds[i].fd, &rset)) { + /* could be POLLERR or POLLHUP but can't tell without probing */ + ufds[i].revents |= POLLIN; + } + if (FD_ISSET(ufds[i].fd, &wset)) { + ufds[i].revents |= POLLOUT; + } + if (FD_ISSET(ufds[i].fd, &eset)) { + ufds[i].revents |= POLLPRI; + } + } + } + + return n; +} + +#endif + /* * Local variables: |
