From 7e7d6d6380e992d6acd61e8e18cb96ee2d98941c Mon Sep 17 00:00:00 2001 From: Leo Gaskin Date: Sat, 6 Sep 2025 14:06:23 +0200 Subject: [PATCH 1/4] Treat accept failing with SOCK_EAGAIN as success in CLI web server This ensures that no useless "Failed to poll event" error messages are logged during normal server operation, as the SOCK_EAGAIN error simply indicates another worker is already serving the request. Closes GH-19964 --- NEWS | 4 ++++ sapi/cli/php_cli_server.c | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 82b1cd77d7c32..2edaf09ce70ea 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,10 @@ PHP NEWS configured). (nielsdos) . Fixed bug GH-20002 (Broken build on *BSD with MSAN). (outtersg) +- CLI: + . Fix useless "Failed to poll event" error logs due to EAGAIN in CLI server + with PHP_CLI_SERVER_WORKERS. (leotaku) + - Curl: . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead of the curl_copy_handle() function to clone a CurlHandle. (timwolla) diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 9eaeb4779f428..8d67fb2864c6d 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -2721,14 +2721,16 @@ static zend_result php_cli_server_do_event_for_each_fd_callback(void *_params, p struct sockaddr *sa = pemalloc(server->socklen, 1); client_sock = accept(server->server_sock, sa, &socklen); if (!ZEND_VALID_SOCKET(client_sock)) { - int err = php_socket_errno(); - if (err != SOCK_EAGAIN && php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { + pefree(sa, 1); + if (php_socket_errno() == SOCK_EAGAIN) { + return SUCCESS; + } + if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { char *errstr = php_socket_strerror(php_socket_errno(), NULL, 0); php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "Failed to accept a client (reason: %s)", errstr); efree(errstr); } - pefree(sa, 1); return FAILURE; } if (SUCCESS != php_set_sock_blocking(client_sock, 0)) { From 64c1d43b68dd812ce4d3dec0a9b469fd4c060f30 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:51:23 +0200 Subject: [PATCH 2/4] Fix GH-19926: reset internal pointer earlier while splicing array while COW violation flag is still set (#19929) --- NEWS | 2 ++ ext/standard/array.c | 8 ++++++-- ext/standard/tests/array/gh19926.phpt | 20 +++++++++++++++++++ ext/standard/tests/array/gh19926_pointer.phpt | 19 ++++++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/array/gh19926.phpt create mode 100644 ext/standard/tests/array/gh19926_pointer.phpt diff --git a/NEWS b/NEWS index 2edaf09ce70ea..c2db743c84e01 100644 --- a/NEWS +++ b/NEWS @@ -62,6 +62,8 @@ PHP NEWS (alexandre-daubois) . Fixed bug GH-20043 (array_unique assertion failure with RC1 array causing an exception on sort). (nielsdos) + . Fixed bug GH-19926 (reset internal pointer earlier while splicing array + while COW violation flag is still set). (alexandre-daubois) - Streams: . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). diff --git a/ext/standard/array.c b/ext/standard/array.c index b7b5f82d61c53..4896ac44a727d 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3376,6 +3376,12 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H HT_SET_ITERATORS_COUNT(in_hash, 0); in_hash->pDestructor = NULL; + /* Set internal pointer to 0 directly instead of calling zend_hash_internal_pointer_reset(). + * This avoids the COW violation assertion and delays advancing to the first valid position + * until after we've switched to the new array structure (out_hash). The iterator will be + * advanced when actually accessed, at which point it will find valid indexes in the new array. */ + in_hash->nInternalPointer = 0; + if (UNEXPECTED(GC_DELREF(in_hash) == 0)) { /* Array was completely deallocated during the operation */ zend_array_destroy(in_hash); @@ -3394,8 +3400,6 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H in_hash->nNextFreeElement = out_hash.nNextFreeElement; in_hash->arData = out_hash.arData; in_hash->pDestructor = out_hash.pDestructor; - - zend_hash_internal_pointer_reset(in_hash); } /* }}} */ diff --git a/ext/standard/tests/array/gh19926.phpt b/ext/standard/tests/array/gh19926.phpt new file mode 100644 index 0000000000000..b714db9eb2b32 --- /dev/null +++ b/ext/standard/tests/array/gh19926.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-19926 (Assertion failure zend_hash_internal_pointer_reset_ex) +--FILE-- + +--EXPECT-- +Exception caught, no assertion failure diff --git a/ext/standard/tests/array/gh19926_pointer.phpt b/ext/standard/tests/array/gh19926_pointer.phpt new file mode 100644 index 0000000000000..c134f3b594b3d --- /dev/null +++ b/ext/standard/tests/array/gh19926_pointer.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-19926 (internal pointer behavior after array_splice) +--FILE-- + +--EXPECT-- +Before array_splice: int(3) +After array_splice: int(999) From 467e05b13af71813f998c17075219270a20ecd0f Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 6 Oct 2025 16:57:28 +0200 Subject: [PATCH 3/4] Fix misplaced entry in NEWS --- NEWS | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index bfd460e74649d..1b5783de1200b 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ PHP NEWS . Fixed bug GH-19773 (SIGSEGV due to uninitialized soap_globals->lang_en). (nielsdos, KaseyJenkins) +- Standard: + . Fixed bug GH-19926 (reset internal pointer earlier while splicing array + while COW violation flag is still set). (alexandre-daubois) + - URI: . Fixed Uri\WhatWg\Url::withPort() when an invalid value is passed. (timwolla) @@ -103,8 +107,6 @@ PHP NEWS (alexandre-daubois) . Fixed GH-14402 (SplPriorityQueue, SplMinHeap, and SplMaxHeap lost their data on serialize()). (alexandre-daubois) - . Fixed bug GH-19926 (reset internal pointer earlier while splicing array - while COW violation flag is still set). (alexandre-daubois) - URI: . Fixed bug GH-19780 (InvalidUrlException should check $errors argument). From b7aeb0a69f94aeaa983c6dbba2c01b584fdfeabe Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 6 Sep 2025 14:20:24 +0200 Subject: [PATCH 4/4] Fix GH-19570: unable to fseek in /dev/zero and /dev/null On Linux, these two character devices are exceptions in that they can be seeked. Check their major/minor device number. Co-authored-by: divinity76 --- NEWS | 2 ++ ext/standard/tests/streams/gh19570.phpt | 22 ++++++++++++++++++++++ main/streams/plain_wrapper.c | 25 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 ext/standard/tests/streams/gh19570.phpt diff --git a/NEWS b/NEWS index c2db743c84e01..7a42307caec46 100644 --- a/NEWS +++ b/NEWS @@ -64,6 +64,8 @@ PHP NEWS causing an exception on sort). (nielsdos) . Fixed bug GH-19926 (reset internal pointer earlier while splicing array while COW violation flag is still set). (alexandre-daubois) + . Fixed bug GH-19570 (unable to fseek in /dev/zero and /dev/null). + (nielsdos, divinity76) - Streams: . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). diff --git a/ext/standard/tests/streams/gh19570.phpt b/ext/standard/tests/streams/gh19570.phpt new file mode 100644 index 0000000000000..e3d486ea83cf9 --- /dev/null +++ b/ext/standard/tests/streams/gh19570.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-19570 (unable to fseek in /dev/zero and /dev/null) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(0) +int(0) +int(0) diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 9ac049f746847..09528e926f72c 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -43,6 +43,10 @@ # include #endif +#ifdef __linux__ +# include +#endif + #define php_stream_fopen_from_fd_int(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC) #define php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_REL_CC) #define php_stream_fopen_from_file_int(file, mode) _php_stream_fopen_from_file_int((file), (mode) STREAMS_CC) @@ -255,7 +259,28 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC) static void detect_is_seekable(php_stdio_stream_data *self) { #if defined(S_ISFIFO) && defined(S_ISCHR) if (self->fd >= 0 && do_fstat(self, 0) == 0) { +#ifdef __linux__ + if (S_ISCHR(self->sb.st_mode)) { + /* Some character devices are exceptions, check their major/minor ID + * https://www.kernel.org/doc/Documentation/admin-guide/devices.txt */ + if (major(self->sb.st_rdev) == 1) { + unsigned m = minor(self->sb.st_rdev); + self->is_seekable = + m == 1 || /* /dev/mem */ + m == 2 || /* /dev/kmem */ + m == 3 || /* /dev/null */ + m == 4 || /* /dev/port (seekable, offset = I/O port) */ + m == 5 || /* /dev/zero */ + m == 7; /* /dev/full */ + } else { + self->is_seekable = false; + } + } else { + self->is_seekable = !S_ISFIFO(self->sb.st_mode); + } +#else self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode)); +#endif self->is_pipe = S_ISFIFO(self->sb.st_mode); } #elif defined(PHP_WIN32)