Skip to content

Commit 246d13c

Browse files
committed
Merge branch 'PHP-8.1' into PHP-8.2
* PHP-8.1: [ci skip] NEWS [ci skip] NEWS Return immediately when FD_SETSIZE is exceeded (#9602)
2 parents d2b7d67 + 878b20b commit 246d13c

File tree

8 files changed

+185
-6
lines changed

8 files changed

+185
-6
lines changed

ext/mysqli/tests/gh9590.phpt

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
--TEST--
2+
Bug GH-9602 (stream_select does not abort upon exception or empty valid fd set)
3+
--SKIPIF--
4+
<?php
5+
require_once('skipif.inc');
6+
require_once('connect.inc');
7+
require_once('skipifconnectfailure.inc');
8+
9+
if (!$IS_MYSQLND)
10+
die("skip mysqlnd only feature, compile PHP using --with-mysqli=mysqlnd");
11+
12+
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
13+
die("skip cannot connect");
14+
15+
if (mysqli_get_server_version($link) < 50012)
16+
die("skip Test needs SQL function SLEEP() available as of MySQL 5.0.12");
17+
18+
if (!function_exists('posix_setrlimit') || !posix_setrlimit(POSIX_RLIMIT_NOFILE, 2048, -1))
19+
die('skip Failed to set POSIX_RLIMIT_NOFILE');
20+
?>
21+
--FILE--
22+
<?php
23+
posix_setrlimit(POSIX_RLIMIT_NOFILE, 2048, -1);
24+
25+
$fds = [];
26+
for ($i = 0; $i < 1023; $i++) {
27+
$fds[] = @fopen(__DIR__ . "/GH-9590-tmpfile.$i", 'w');
28+
}
29+
30+
require_once('connect.inc');
31+
32+
function get_connection() {
33+
global $host, $user, $passwd, $db, $port, $socket;
34+
35+
if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))
36+
printf("[001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error());
37+
return $link;
38+
}
39+
40+
41+
$mysqli1 = get_connection();
42+
$mysqli2 = get_connection();
43+
44+
var_dump(mysqli_query($mysqli1, "SELECT SLEEP(0.10)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
45+
var_dump(mysqli_query($mysqli2, "SELECT SLEEP(0.20)", MYSQLI_ASYNC | MYSQLI_USE_RESULT));
46+
47+
$links = $errors = $reject = array($mysqli1, $mysqli2);
48+
var_dump(mysqli_poll($links, $errors, $reject, 0, 50000));
49+
50+
mysqli_close($mysqli1);
51+
mysqli_close($mysqli2);
52+
53+
print "done!";
54+
?>
55+
--EXPECTF--
56+
bool(true)
57+
bool(true)
58+
59+
Warning: mysqli_poll(): You MUST recompile PHP with a larger value of FD_SETSIZE.
60+
It is set to 1024, but you have descriptors numbered at least as high as %d.
61+
--enable-fd-setsize=%d is recommended, but you may want to set it
62+
to equal the maximum number of open files supported by your system,
63+
in order to avoid seeing this error again at a later date. in %s on line %d
64+
bool(false)
65+
done!
66+
--CLEAN--
67+
<?php
68+
for ($i = 0; $i < 1023; $i++) {
69+
@unlink(__DIR__ . "/GH-9590-tmpfile.$i");
70+
}
71+
?>

ext/mysqlnd/mysqlnd_connection.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2256,7 +2256,9 @@ mysqlnd_poll(MYSQLND **r_array, MYSQLND **e_array, MYSQLND ***dont_poll, long se
22562256
DBG_RETURN(FAIL);
22572257
}
22582258

2259-
PHP_SAFE_MAX_FD(max_fd, max_set_count);
2259+
if (!PHP_SAFE_MAX_FD(max_fd, max_set_count)) {
2260+
DBG_RETURN(FAIL);
2261+
}
22602262

22612263
/* Solaris + BSD do not like microsecond values which are >= 1 sec */
22622264
if (usec > 999999) {

ext/sockets/sockets.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,9 @@ PHP_FUNCTION(socket_select)
605605
RETURN_THROWS();
606606
}
607607

608-
PHP_SAFE_MAX_FD(max_fd, 0); /* someone needs to make this look more like stream_socket_select */
608+
if (!PHP_SAFE_MAX_FD(max_fd, 0)) {
609+
RETURN_FALSE;
610+
}
609611

610612
/* If seconds is not set to null, build the timeval, else we wait indefinitely */
611613
if (!sec_is_null) {

ext/standard/streamsfuncs.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,9 @@ PHP_FUNCTION(stream_select)
799799
RETURN_THROWS();
800800
}
801801

802-
PHP_SAFE_MAX_FD(max_fd, max_set_count);
802+
if (!PHP_SAFE_MAX_FD(max_fd, max_set_count)) {
803+
RETURN_FALSE;
804+
}
803805

804806
if (secnull && !usecnull) {
805807
if (usec != 0) {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
Bug GH-9590 001 (stream_select does not abort upon exception or empty valid fd set)
3+
--SKIPIF--
4+
<?php
5+
if (!function_exists('posix_setrlimit') || !posix_setrlimit(POSIX_RLIMIT_NOFILE, 2048, -1)) {
6+
die('skip Failed to set POSIX_RLIMIT_NOFILE');
7+
}
8+
?>
9+
--FILE--
10+
<?php
11+
12+
posix_setrlimit(POSIX_RLIMIT_NOFILE, 2048, -1);
13+
14+
$fds = [];
15+
for ($i = 0; $i < 1023; $i++) {
16+
$fds[] = @fopen(__DIR__ . "/GH-9590-001-tmpfile.$i", 'w');
17+
}
18+
19+
list($a, $b) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
20+
21+
$r = [$a];
22+
$w = $e = [];
23+
var_dump(stream_select($r, $w, $e, PHP_INT_MAX));
24+
25+
?>
26+
--EXPECTF--
27+
Warning: stream_select(): You MUST recompile PHP with a larger value of FD_SETSIZE.
28+
It is set to 1024, but you have descriptors numbered at least as high as %d.
29+
--enable-fd-setsize=%d is recommended, but you may want to set it
30+
to equal the maximum number of open files supported by your system,
31+
in order to avoid seeing this error again at a later date. in %s on line %d
32+
bool(false)
33+
--CLEAN--
34+
<?php
35+
for ($i = 0; $i < 1023; $i++) {
36+
@unlink(__DIR__ . "/GH-9590-001-tmpfile.$i");
37+
}
38+
?>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
Bug GH-9590 002 (stream_select does not abort upon exception or empty valid fd set)
3+
--SKIPIF--
4+
<?php
5+
if (!function_exists('posix_setrlimit') || !posix_setrlimit(POSIX_RLIMIT_NOFILE, 2048, -1)) {
6+
die('skip Failed to set POSIX_RLIMIT_NOFILE');
7+
}
8+
?>
9+
--FILE--
10+
<?php
11+
12+
posix_setrlimit(POSIX_RLIMIT_NOFILE, 2048, -1);
13+
14+
$fds = [];
15+
for ($i = 0; $i < 1023; $i++) {
16+
$fds[] = @fopen(__DIR__ . "/GH-9590-002-tmpfile.$i", 'w');
17+
}
18+
19+
list($a, $b) = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
20+
21+
set_error_handler(function($errno, $errstr) { throw new \Exception($errstr); });
22+
23+
$r = [$a];
24+
$w = $e = [];
25+
var_dump(stream_select($r, $w, $e, PHP_INT_MAX));
26+
27+
?>
28+
--EXPECTF--
29+
Fatal error: Uncaught Exception: stream_select(): You MUST recompile PHP with a larger value of FD_SETSIZE.
30+
It is set to 1024, but you have descriptors numbered at least as high as %d.
31+
--enable-fd-setsize=%d is recommended, but you may want to set it
32+
to equal the maximum number of open files supported by your system,
33+
in order to avoid seeing this error again at a later date. in %s:%d
34+
Stack trace:%a
35+
--CLEAN--
36+
<?php
37+
for ($i = 0; $i < 1023; $i++) {
38+
@unlink(__DIR__ . "/GH-9590-002-tmpfile.$i");
39+
}
40+
?>

main/network.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1200,7 +1200,14 @@ PHPAPI int php_poll2(php_pollfd *ufds, unsigned int nfds, int timeout)
12001200
}
12011201
#endif
12021202

1203-
PHP_SAFE_MAX_FD(max_fd, nfds + 1);
1203+
if (!PHP_SAFE_MAX_FD(max_fd, nfds + 1)) {
1204+
#ifdef PHP_WIN32
1205+
WSASetLastError(WSAEINVAL);
1206+
#else
1207+
errno = ERANGE;
1208+
#endif
1209+
return -1;
1210+
}
12041211

12051212
FD_ZERO(&rset);
12061213
FD_ZERO(&wset);

main/php_network.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,18 +211,35 @@ static inline int php_pollfd_for_ms(php_socket_t fd, int events, int timeout)
211211
/* emit warning and suggestion for unsafe select(2) usage */
212212
PHPAPI void _php_emit_fd_setsize_warning(int max_fd);
213213

214+
static inline bool _php_check_fd_setsize(php_socket_t *max_fd, int setsize)
215+
{
216+
#ifdef PHP_WIN32
217+
if (setsize + 1 >= FD_SETSIZE) {
218+
_php_emit_fd_setsize_warning(setsize);
219+
return false;
220+
}
221+
#else
222+
if (*max_fd >= FD_SETSIZE) {
223+
_php_emit_fd_setsize_warning(*max_fd);
224+
*max_fd = FD_SETSIZE - 1;
225+
return false;
226+
}
227+
#endif
228+
return true;
229+
}
230+
214231
#ifdef PHP_WIN32
215232
/* it is safe to FD_SET too many fd's under win32; the macro will simply ignore
216233
* descriptors that go beyond the default FD_SETSIZE */
217234
# define PHP_SAFE_FD_SET(fd, set) FD_SET(fd, set)
218235
# define PHP_SAFE_FD_CLR(fd, set) FD_CLR(fd, set)
219236
# define PHP_SAFE_FD_ISSET(fd, set) FD_ISSET(fd, set)
220-
# define PHP_SAFE_MAX_FD(m, n) do { if (n + 1 >= FD_SETSIZE) { _php_emit_fd_setsize_warning(n); }} while(0)
237+
# define PHP_SAFE_MAX_FD(m, n) _php_check_fd_setsize(&m, n)
221238
#else
222239
# define PHP_SAFE_FD_SET(fd, set) do { if (fd < FD_SETSIZE) FD_SET(fd, set); } while(0)
223240
# define PHP_SAFE_FD_CLR(fd, set) do { if (fd < FD_SETSIZE) FD_CLR(fd, set); } while(0)
224241
# define PHP_SAFE_FD_ISSET(fd, set) ((fd < FD_SETSIZE) && FD_ISSET(fd, set))
225-
# define PHP_SAFE_MAX_FD(m, n) do { if (m >= FD_SETSIZE) { _php_emit_fd_setsize_warning(m); m = FD_SETSIZE - 1; }} while(0)
242+
# define PHP_SAFE_MAX_FD(m, n) _php_check_fd_setsize(&m, n)
226243
#endif
227244

228245

0 commit comments

Comments
 (0)