From 40780218f5dfff88643b662594d89f05b4f6ee06 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 4 Jul 2026 21:00:24 -0700 Subject: [PATCH 1/3] Add and use known string for NoDiscard (#21810) --- Zend/zend_string.h | 1 + build/gen_stub.php | 1 + ext/date/php_date_arginfo.h | 36 +++++++++--------------------------- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 971902cabd2b..e0d1c026a2f2 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -709,6 +709,7 @@ default: ZEND_UNREACHABLE(); _(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \ _(ZEND_STR_DEPRECATED_CAPITALIZED, "Deprecated") \ _(ZEND_STR_SINCE, "since") \ + _(ZEND_STR_NODISCARD, "NoDiscard") \ _(ZEND_STR_GET, "get") \ _(ZEND_STR_SET, "set") \ _(ZEND_STR_8_DOT_0, "8.0") \ diff --git a/build/gen_stub.php b/build/gen_stub.php index e60e61885206..1e1d53c5146c 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2976,6 +2976,7 @@ class StringBuilder { // NEW in 8.6 private const PHP_86_KNOWN = [ "arguments" => "ZEND_STR_ARGUMENTS", + "NoDiscard" => "ZEND_STR_NODISCARD", ]; /** diff --git a/ext/date/php_date_arginfo.h b/ext/date/php_date_arginfo.h index 4a5a4ad32dec..db2b4d5ea9e1 100644 --- a/ext/date/php_date_arginfo.h +++ b/ext/date/php_date_arginfo.h @@ -1025,65 +1025,47 @@ static zend_class_entry *register_class_DateTimeImmutable(zend_class_entry *clas ZVAL_STR(&attribute_Deprecated_func___wakeup_0->args[1].value, attribute_Deprecated_func___wakeup_0_arg1_str); attribute_Deprecated_func___wakeup_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_modify_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_modify_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "modify", sizeof("modify") - 1), attribute_name_NoDiscard_func_modify_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_modify_0, true); + zend_attribute *attribute_NoDiscard_func_modify_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "modify", sizeof("modify") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_modify_0_arg0_str = zend_string_init("as DateTimeImmutable::modify() does not modify the object itself", strlen("as DateTimeImmutable::modify() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_modify_0->args[0].value, attribute_NoDiscard_func_modify_0_arg0_str); attribute_NoDiscard_func_modify_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_add_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_add_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "add", sizeof("add") - 1), attribute_name_NoDiscard_func_add_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_add_0, true); + zend_attribute *attribute_NoDiscard_func_add_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "add", sizeof("add") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_add_0_arg0_str = zend_string_init("as DateTimeImmutable::add() does not modify the object itself", strlen("as DateTimeImmutable::add() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_add_0->args[0].value, attribute_NoDiscard_func_add_0_arg0_str); attribute_NoDiscard_func_add_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_sub_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_sub_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "sub", sizeof("sub") - 1), attribute_name_NoDiscard_func_sub_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_sub_0, true); + zend_attribute *attribute_NoDiscard_func_sub_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "sub", sizeof("sub") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_sub_0_arg0_str = zend_string_init("as DateTimeImmutable::sub() does not modify the object itself", strlen("as DateTimeImmutable::sub() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_sub_0->args[0].value, attribute_NoDiscard_func_sub_0_arg0_str); attribute_NoDiscard_func_sub_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_settimezone_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_settimezone_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimezone", sizeof("settimezone") - 1), attribute_name_NoDiscard_func_settimezone_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_settimezone_0, true); + zend_attribute *attribute_NoDiscard_func_settimezone_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimezone", sizeof("settimezone") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_settimezone_0_arg0_str = zend_string_init("as DateTimeImmutable::setTimezone() does not modify the object itself", strlen("as DateTimeImmutable::setTimezone() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_settimezone_0->args[0].value, attribute_NoDiscard_func_settimezone_0_arg0_str); attribute_NoDiscard_func_settimezone_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_settime_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_settime_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settime", sizeof("settime") - 1), attribute_name_NoDiscard_func_settime_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_settime_0, true); + zend_attribute *attribute_NoDiscard_func_settime_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settime", sizeof("settime") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_settime_0_arg0_str = zend_string_init("as DateTimeImmutable::setTime() does not modify the object itself", strlen("as DateTimeImmutable::setTime() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_settime_0->args[0].value, attribute_NoDiscard_func_settime_0_arg0_str); attribute_NoDiscard_func_settime_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_setdate_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_setdate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setdate", sizeof("setdate") - 1), attribute_name_NoDiscard_func_setdate_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_setdate_0, true); + zend_attribute *attribute_NoDiscard_func_setdate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setdate", sizeof("setdate") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_setdate_0_arg0_str = zend_string_init("as DateTimeImmutable::setDate() does not modify the object itself", strlen("as DateTimeImmutable::setDate() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_setdate_0->args[0].value, attribute_NoDiscard_func_setdate_0_arg0_str); attribute_NoDiscard_func_setdate_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_setisodate_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_setisodate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setisodate", sizeof("setisodate") - 1), attribute_name_NoDiscard_func_setisodate_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_setisodate_0, true); + zend_attribute *attribute_NoDiscard_func_setisodate_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setisodate", sizeof("setisodate") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_setisodate_0_arg0_str = zend_string_init("as DateTimeImmutable::setISODate() does not modify the object itself", strlen("as DateTimeImmutable::setISODate() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_setisodate_0->args[0].value, attribute_NoDiscard_func_setisodate_0_arg0_str); attribute_NoDiscard_func_setisodate_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_settimestamp_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_settimestamp_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimestamp", sizeof("settimestamp") - 1), attribute_name_NoDiscard_func_settimestamp_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_settimestamp_0, true); + zend_attribute *attribute_NoDiscard_func_settimestamp_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "settimestamp", sizeof("settimestamp") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_settimestamp_0_arg0_str = zend_string_init("as DateTimeImmutable::setTimestamp() does not modify the object itself", strlen("as DateTimeImmutable::setTimestamp() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_settimestamp_0->args[0].value, attribute_NoDiscard_func_settimestamp_0_arg0_str); attribute_NoDiscard_func_settimestamp_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); - zend_string *attribute_name_NoDiscard_func_setmicrosecond_0 = zend_string_init_interned("NoDiscard", sizeof("NoDiscard") - 1, true); - zend_attribute *attribute_NoDiscard_func_setmicrosecond_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setmicrosecond", sizeof("setmicrosecond") - 1), attribute_name_NoDiscard_func_setmicrosecond_0, 1); - zend_string_release_ex(attribute_name_NoDiscard_func_setmicrosecond_0, true); + zend_attribute *attribute_NoDiscard_func_setmicrosecond_0 = zend_add_function_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "setmicrosecond", sizeof("setmicrosecond") - 1), ZSTR_KNOWN(ZEND_STR_NODISCARD), 1); zend_string *attribute_NoDiscard_func_setmicrosecond_0_arg0_str = zend_string_init("as DateTimeImmutable::setMicrosecond() does not modify the object itself", strlen("as DateTimeImmutable::setMicrosecond() does not modify the object itself"), 1); ZVAL_STR(&attribute_NoDiscard_func_setmicrosecond_0->args[0].value, attribute_NoDiscard_func_setmicrosecond_0_arg0_str); attribute_NoDiscard_func_setmicrosecond_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); From aa12a652c33755f2b932b34442f2109b376d5ea7 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 5 Jul 2026 12:28:12 +0800 Subject: [PATCH 2/3] ext/soap: Add `UNEXPECTED()` in obvious error path (#22596) This added `UNEXPECTED()` in several obvious error path for optimization in ext/soap. --- ext/soap/php_encoding.c | 4 ++-- ext/soap/php_http.c | 10 +++++----- ext/soap/php_packet_soap.c | 10 +++++----- ext/soap/php_schema.c | 4 ++-- ext/soap/php_sdl.c | 10 +++++----- ext/soap/soap.c | 6 +++--- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index f02a16638025..e474798df6da 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -2052,7 +2052,7 @@ static int calc_dimension_12(const char* str) static void soap_array_position_add_digit(int *position, int digit) { - if (*position > (INT_MAX - digit) / 10) { + if (UNEXPECTED(*position > (INT_MAX - digit) / 10)) { soap_error0(E_ERROR, "Encoding: array index out of range"); } @@ -2697,7 +2697,7 @@ static zval *to_zval_array(zval *ret, encodeTypePtr type, xmlNodePtr data) i = dimension; while (i > 0) { i--; - if (pos[i] == INT_MAX) { + if (UNEXPECTED(pos[i] == INT_MAX)) { efree(dims); efree(pos); zval_ptr_dtor(ret); diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index 4fe272ea6f13..b02942f3dccb 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -1488,7 +1488,7 @@ static zend_string* get_http_body(php_stream *stream, bool close, zend_string *h if (buf_size > 0) { size_t len_size = 0; - if (http_buf_size + buf_size + 1 < 0) { + if (UNEXPECTED(http_buf_size + buf_size + 1 < 0)) { if (http_buf) { zend_string_release_ex(http_buf, 0); } @@ -1503,7 +1503,7 @@ static zend_string* get_http_body(php_stream *stream, bool close, zend_string *h while (len_size < buf_size) { ssize_t len_read = php_stream_read(stream, http_buf->val + http_buf_size, buf_size - len_size); - if (len_read <= 0) { + if (UNEXPECTED(len_read <= 0)) { /* Error or EOF */ done = true; break; @@ -1517,7 +1517,7 @@ static zend_string* get_http_body(php_stream *stream, bool close, zend_string *h if (ch == '\r') { ch = php_stream_getc(stream); } - if (ch != '\n') { + if (UNEXPECTED(ch != '\n')) { /* Something wrong in chunked encoding */ if (http_buf) { zend_string_release_ex(http_buf, 0); @@ -1555,13 +1555,13 @@ static zend_string* get_http_body(php_stream *stream, bool close, zend_string *h } } else if (header_length) { - if (header_length < 0 || header_length >= INT_MAX) { + if (UNEXPECTED(header_length < 0 || header_length >= INT_MAX)) { return NULL; } http_buf = zend_string_alloc(header_length, 0); while (http_buf_size < header_length) { ssize_t len_read = php_stream_read(stream, http_buf->val + http_buf_size, header_length - http_buf_size); - if (len_read <= 0) { + if (UNEXPECTED(len_read <= 0)) { break; } http_buf_size += len_read; diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c index 68ed9a7a7689..29ed1294f364 100644 --- a/ext/soap/php_packet_soap.c +++ b/ext/soap/php_packet_soap.c @@ -59,11 +59,11 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z /* Parse XML packet */ response = soap_xmlParseMemory(ZSTR_VAL(buffer), ZSTR_LEN(buffer)); - if (!response) { + if (UNEXPECTED(!response)) { add_soap_fault(this_ptr, "Client", "looks like we got no XML document", NULL, NULL, soap_lang_en); return false; } - if (xmlGetIntSubset(response) != NULL) { + if (UNEXPECTED(xmlGetIntSubset(response) != NULL)) { add_soap_fault(this_ptr, "Client", "DTD are not supported by SOAP", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; @@ -90,7 +90,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z } trav = trav->next; } - if (env == NULL) { + if (UNEXPECTED(env == NULL)) { add_soap_fault(this_ptr, "Client", "looks like we got XML without \"Envelope\" element", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; @@ -139,7 +139,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z while (trav != NULL && trav->type != XML_ELEMENT_NODE) { trav = trav->next; } - if (body == NULL) { + if (UNEXPECTED(body == NULL)) { add_soap_fault(this_ptr, "Client", "Body must be present in a SOAP envelope", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; @@ -165,7 +165,7 @@ bool parse_packet_soap(zval *this_ptr, zend_string *buffer, sdlFunctionPtr fn, z } attr = attr->next; } - if (trav != NULL && soap_version == SOAP_1_2) { + if (UNEXPECTED(trav != NULL && soap_version == SOAP_1_2)) { add_soap_fault(this_ptr, "Client", "A SOAP 1.2 envelope can contain only Header and Body", NULL, NULL, soap_lang_en); xmlFreeDoc(response); return false; diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c index d97a0eac05b5..b675b97b469f 100644 --- a/ext/soap/php_schema.c +++ b/ext/soap/php_schema.c @@ -63,12 +63,12 @@ static int schema_parse_int(const xmlChar *value, const char *name, bool allow_n if (type != IS_LONG) { errno = 0; lval = ZEND_STRTOL(str, NULL, 10); - if (oflow_info || (errno == ERANGE && lval != 0)) { + if (UNEXPECTED(oflow_info || (errno == ERANGE && lval != 0))) { soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name); } } - if (ZEND_LONG_EXCEEDS_INT(lval) || (!allow_negative && lval < 0)) { + if (UNEXPECTED(ZEND_LONG_EXCEEDS_INT(lval) || (!allow_negative && lval < 0))) { soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name); } diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c index 07c5dbf4f12e..73886af5a567 100644 --- a/ext/soap/php_sdl.c +++ b/ext/soap/php_sdl.c @@ -1524,22 +1524,22 @@ static sdlPtr get_sdl_from_cache(const char *fn, const char *uri, size_t uri_len char *in, *buf; f = open(fn, O_RDONLY|O_BINARY); - if (f < 0) { + if (UNEXPECTED(f < 0)) { return NULL; } - if (fstat(f, &st) != 0) { + if (UNEXPECTED(fstat(f, &st) != 0)) { close(f); return NULL; } buf = in = emalloc(st.st_size); - if (read(f, in, st.st_size) != st.st_size) { + if (UNEXPECTED(read(f, in, st.st_size) != st.st_size)) { close(f); efree(in); return NULL; } close(f); - if (strncmp(in,"wsdl",4) != 0 || in[4] != WSDL_CACHE_VERSION || in[5] != '\0') { + if (UNEXPECTED(strncmp(in,"wsdl",4) != 0 || in[4] != WSDL_CACHE_VERSION || in[5] != '\0')) { unlink(fn); efree(buf); return NULL; @@ -2096,7 +2096,7 @@ static void add_sdl_to_cache(const char *fn, const char *uri, time_t t, sdlPtr s zend_string *temp_file_path; f = php_open_temporary_fd_ex(SOAP_GLOBAL(cache_dir), "tmp.wsdl.", &temp_file_path, PHP_TMP_FILE_SILENT); - if (f < 0) {return;} + if (UNEXPECTED(f < 0)) {return;} zend_hash_init(&tmp_types, 0, NULL, NULL, 0); zend_hash_init(&tmp_encoders, 0, NULL, NULL, 0); diff --git a/ext/soap/soap.c b/ext/soap/soap.c index a3e03e0fb58b..171b529b3355 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -1008,7 +1008,7 @@ PHP_METHOD(SoapServer, __construct) goto cleanup; } // TODO: this still accepts mixed keys arrays and not all numerically indexed arrays are packed - if (HT_IS_PACKED(Z_ARRVAL_P(class_map_zv))) { + if (UNEXPECTED(HT_IS_PACKED(Z_ARRVAL_P(class_map_zv)))) { zend_argument_value_error(2, "\"classmap\" option must be an associative array"); goto cleanup; } @@ -2224,7 +2224,7 @@ PHP_METHOD(SoapClient, __construct) xmlCharEncodingHandlerPtr encoding; encoding = xmlFindCharEncodingHandler(Z_STRVAL_P(tmp)); - if (encoding == NULL) { + if (UNEXPECTED(encoding == NULL)) { php_error_docref(NULL, E_ERROR, "Invalid 'encoding' option - '%s'", Z_STRVAL_P(tmp)); } else { xmlCharEncCloseFunc(encoding); @@ -2233,7 +2233,7 @@ PHP_METHOD(SoapClient, __construct) } if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && Z_TYPE_P(tmp) == IS_ARRAY) { - if (HT_IS_PACKED(Z_ARRVAL_P(tmp))) { + if (UNEXPECTED(HT_IS_PACKED(Z_ARRVAL_P(tmp)))) { php_error_docref(NULL, E_ERROR, "'classmap' option must be an associative array"); } ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); From 2ddbc0a67229cd8f870a2e3777fe769abdc959b5 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 5 Jul 2026 03:17:38 -0400 Subject: [PATCH 3/3] main/poll: Record wait() error on every backend (#22326) php_poll_wait() reported a stale error code when a backend's wait syscall failed without recording one: epoll, poll and kqueue returned -1 without setting it. Record it from errno in those backends so php_poll_get_error() reflects the actual failure. eventport and wsapoll already did, the latter from WSAGetLastError(). Closes GH-22326 --- main/poll/poll_backend_epoll.c | 2 ++ main/poll/poll_backend_kqueue.c | 5 +++++ main/poll/poll_backend_poll.c | 7 +++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/main/poll/poll_backend_epoll.c b/main/poll/poll_backend_epoll.c index 685339da77d2..394b0dc52446 100644 --- a/main/poll/poll_backend_epoll.c +++ b/main/poll/poll_backend_epoll.c @@ -195,6 +195,8 @@ static int epoll_backend_wait( events[i].revents = epoll_events_from_native(backend_data->events[i].events); events[i].data = backend_data->events[i].data.ptr; } + } else if (nfds < 0) { + php_poll_set_current_errno_error(ctx); } return nfds; diff --git a/main/poll/poll_backend_kqueue.c b/main/poll/poll_backend_kqueue.c index 59b5efa9cacb..00ba7abc9ee6 100644 --- a/main/poll/poll_backend_kqueue.c +++ b/main/poll/poll_backend_kqueue.c @@ -320,6 +320,11 @@ static int kqueue_backend_wait( int nfds = kevent( backend_data->kqueue_fd, NULL, 0, backend_data->events, required_capacity, timeout); + if (nfds < 0) { + php_poll_set_current_errno_error(ctx); + return -1; + } + if (nfds > 0) { if (ctx->raw_events) { /* Raw events mode - direct 1:1 mapping, no grouping */ diff --git a/main/poll/poll_backend_poll.c b/main/poll/poll_backend_poll.c index cca81b6fc4fb..05a51a04d31d 100644 --- a/main/poll/poll_backend_poll.c +++ b/main/poll/poll_backend_poll.c @@ -215,8 +215,11 @@ static int poll_backend_wait( int timeout_ms = php_poll_timespec_to_ms(timeout); int nfds = poll(backend_data->temp_fds, fd_count, timeout_ms); - if (nfds <= 0) { - return nfds; /* Return 0 for timeout, -1 for error */ + if (nfds < 0) { + php_poll_set_current_errno_error(ctx); + return -1; + } else if (nfds == 0) { + return 0; /* timeout */ } /* Process results - iterate through struct pollfd array directly */