diff --git a/NEWS b/NEWS index ba16baf5930f0..5bf08a820cd8e 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,65 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.1.26 +21 Dec 2023, PHP 8.1.27 + +- Core: + . Fixed oss-fuzz #54325 (Use-after-free of name in var-var with malicious + error handler). (ilutov) + . Fixed oss-fuzz #64209 (In-place modification of filename in + php_message_handler_for_zend). (ilutov) + . Fixed bug GH-12758 / GH-12768 (Invalid opline in OOM handlers within + ZEND_FUNC_GET_ARGS and ZEND_BIND_STATIC). (Florian Engelhardt) + +- DOM: + . Fixed bug GH-12616 (DOM: Removing XMLNS namespace node results in invalid + default: prefix). (nielsdos) + +- FPM: + . Fixed bug GH-12705 (Segmentation fault in fpm_status_export_to_zval). + (Patrick Prasse) + +- Intl: + . Fixed bug GH-12635 (Test bug69398.phpt fails with ICU 74.1). (nielsdos) + +- LibXML: + . Fixed bug GH-12702 (libxml2 2.12.0 issue building from src). (nono303) + +- MySQLnd: + . Avoid using uninitialised struct. (mikhainin) + +- OpenSSL: + . Fixed bug #50713 (openssl_pkcs7_verify() may ignore untrusted CAs). + (Jakub Zelenka) + +- PCRE: + . Fixed bug GH-12628 (The gh11374 test fails on Alpinelinux). (nielsdos) + +- PGSQL: + . Fixed bug GH-12763 wrong argument type for pg_untrace. (degtyarov) + +- PHPDBG: + . Fixed bug GH-12675 (MEMORY_LEAK in phpdbg_prompt.c). (nielsdos) + +- SQLite3: + . Fixed bug GH-12633 (sqlite3_defensive.phpt fails with sqlite 3.44.0). + (SakiTakamachi) + +- Standard: + . Fix memory leak in syslog device handling. (danog) + . Fixed bug GH-12621 (browscap segmentation fault when configured in the + vhost). (nielsdos) + . Fixed bug GH-12655 (proc_open() does not take into account references + in the descriptor array). (nielsdos) + +- Streams: + . Fixed bug #79945 (Stream wrappers in imagecreatefrompng causes segfault). + (Jakub Zelenka) + +- Zip: + . Fixed bug GH-12661 (Inconsistency in ZipArchive::addGlob remove_path Option + Behavior). (Remi) + +23 Nov 2023, PHP 8.1.26 - Core: . Fixed bug GH-12468 (Double-free of doc_comment when overriding static diff --git a/Zend/tests/oss_fuzz_54325.phpt b/Zend/tests/oss_fuzz_54325.phpt new file mode 100644 index 0000000000000..d998acf1ffedb --- /dev/null +++ b/Zend/tests/oss_fuzz_54325.phpt @@ -0,0 +1,19 @@ +--TEST-- +oss-fuzz #54325: Fix use-after-free of name in var-var with malicious error handler +--FILE-- + +--EXPECT-- +string(23) "Undefined variable $oof" +object(stdClass)#2 (0) { +} diff --git a/Zend/tests/oss_fuzz_64209.phpt b/Zend/tests/oss_fuzz_64209.phpt new file mode 100644 index 0000000000000..599ae258e5b2c --- /dev/null +++ b/Zend/tests/oss_fuzz_64209.phpt @@ -0,0 +1,13 @@ +--TEST-- +oss-fuzz #64209: Fix in-place modification of filename in php_message_handler_for_zend +--FILE-- + +--EXPECTF-- +Warning: require(://@): Failed to open stream: No such file or directory in %s on line %d + +Fatal error: Uncaught Error: Failed opening required '://@' (include_path='%s') in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/zend.h b/Zend/zend.h index 0df86327eb517..472b08d68253a 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.1.26-dev" +#define ZEND_VERSION "4.1.27" #define ZEND_ENGINE_3 diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 90704993bb253..a6f86a9eab6f7 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1748,6 +1748,10 @@ ZEND_VM_C_LABEL(fetch_this): } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if (OP1_TYPE == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -1755,6 +1759,9 @@ ZEND_VM_C_LABEL(fetch_this): } else { retval = &EG(uninitialized_zval); } + if (OP1_TYPE == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { @@ -8781,6 +8788,8 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, UNUSED, REF) variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W); + SAVE_OPLINE(); + ht = ZEND_MAP_PTR_GET(EX(func)->op_array.static_variables_ptr); if (!ht) { ht = zend_array_dup(EX(func)->op_array.static_variables); @@ -8790,7 +8799,6 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, UNUSED, REF) value = (zval*)((char*)ht->arData + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))); - SAVE_OPLINE(); if (opline->extended_value & ZEND_BIND_REF) { if (Z_TYPE_P(value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(value, EX(func)->op_array.scope) != SUCCESS)) { @@ -9243,6 +9251,7 @@ ZEND_VM_HANDLER(172, ZEND_FUNC_GET_ARGS, UNUSED|CONST, UNUSED) } if (result_size) { + SAVE_OPLINE(); uint32_t first_extra_arg = EX(func)->op_array.num_args; ht = zend_new_array(result_size); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 530fd7d3e117c..5ee1c2c94daf5 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -9755,6 +9755,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_ad } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if (IS_CONST == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -9762,6 +9766,9 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_ad } else { retval = &EG(uninitialized_zval); } + if (IS_CONST == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { @@ -10693,6 +10700,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FUNC_GET_ARGS_SPEC_CONST_UNUSE } if (result_size) { + SAVE_OPLINE(); uint32_t first_extra_arg = EX(func)->op_array.num_args; ht = zend_new_array(result_size); @@ -17560,6 +17568,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_ad } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if ((IS_TMP_VAR|IS_VAR) == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -17567,6 +17579,9 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_ad } else { retval = &EG(uninitialized_zval); } + if ((IS_TMP_VAR|IS_VAR) == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { @@ -36050,6 +36065,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FUNC_GET_ARGS_SPEC_UNUSED_UNUS } if (result_size) { + SAVE_OPLINE(); uint32_t first_extra_arg = EX(func)->op_array.num_args; ht = zend_new_array(result_size); @@ -47008,6 +47024,10 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_ad } else if (type == BP_VAR_IS || type == BP_VAR_UNSET) { retval = &EG(uninitialized_zval); } else { + if (IS_CV == IS_CV) { + /* Keep name alive in case an error handler tries to free it. */ + zend_string_addref(name); + } zend_error(E_WARNING, "Undefined %svariable $%s", (opline->extended_value & ZEND_FETCH_GLOBAL ? "global " : ""), ZSTR_VAL(name)); if (type == BP_VAR_RW && !EG(exception)) { @@ -47015,6 +47035,9 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_fetch_var_ad } else { retval = &EG(uninitialized_zval); } + if (IS_CV == IS_CV) { + zend_string_release(name); + } } /* GLOBAL or $$name variable may be an INDIRECT pointer to CV */ } else if (Z_TYPE_P(retval) == IS_INDIRECT) { @@ -48450,6 +48473,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_UNUSED_HAN variable_ptr = EX_VAR(opline->op1.var); + SAVE_OPLINE(); + ht = ZEND_MAP_PTR_GET(EX(func)->op_array.static_variables_ptr); if (!ht) { ht = zend_array_dup(EX(func)->op_array.static_variables); @@ -48459,7 +48484,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_UNUSED_HAN value = (zval*)((char*)ht->arData + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))); - SAVE_OPLINE(); if (opline->extended_value & ZEND_BIND_REF) { if (Z_TYPE_P(value) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(value, EX(func)->op_array.scope) != SUCCESS)) { diff --git a/configure.ac b/configure.ac index 464ee54c9cfcf..4c51bb85f4c1c 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.1.26-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.1.27],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER @@ -1534,6 +1534,23 @@ if test "$PHP_UNDEFINED_SANITIZER" = "yes"; then AX_CHECK_COMPILE_FLAG([-fsanitize=undefined], [ CFLAGS="$CFLAGS -fsanitize=undefined" CXXFLAGS="$CXXFLAGS -fsanitize=undefined" + + dnl Clang 17 adds stricter function pointer compatibility checks where pointer args cannot be + dnl cast to void*. In that case, set -fno-sanitize=function. + OLD_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fno-sanitize-recover=undefined" + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +void foo(char *string) {} +int main(void) { + void (*f)(void *) = (void (*)(void *))foo; + f("foo"); +} + ]])],,[ubsan_needs_no_function=yes],) + CFLAGS="$OLD_CFLAGS" + if test "$ubsan_needs_no_function" = yes; then + CFLAGS="$CFLAGS -fno-sanitize=function" + CXXFLAGS="$CFLAGS -fno-sanitize=function" + fi ], [AC_MSG_ERROR([UndefinedBehaviorSanitizer is not available])]) fi diff --git a/ext/dom/document.c b/ext/dom/document.c index 59f00897a69aa..8312d6c59399f 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -23,6 +23,7 @@ #if defined(HAVE_LIBXML) && defined(HAVE_DOM) #include "php_dom.h" #include +#include #ifdef LIBXML_SCHEMAS_ENABLED #include #include diff --git a/ext/dom/element.c b/ext/dom/element.c index c630bec2b5007..f5733c5c48bf3 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -724,6 +724,83 @@ PHP_METHOD(DOMElement, setAttributeNS) } /* }}} end dom_element_set_attribute_ns */ +static void dom_remove_eliminated_ns_single_element(xmlNodePtr node, xmlNsPtr eliminatedNs) +{ + ZEND_ASSERT(node->type == XML_ELEMENT_NODE); + if (node->ns == eliminatedNs) { + node->ns = NULL; + } + + for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) { + if (attr->ns == eliminatedNs) { + attr->ns = NULL; + } + } +} + +static void dom_remove_eliminated_ns(xmlNodePtr node, xmlNsPtr eliminatedNs) +{ + dom_remove_eliminated_ns_single_element(node, eliminatedNs); + + xmlNodePtr base = node; + node = node->children; + while (node != NULL) { + ZEND_ASSERT(node != base); + + if (node->type == XML_ELEMENT_NODE) { + dom_remove_eliminated_ns_single_element(node, eliminatedNs); + + if (node->children) { + node = node->children; + continue; + } + } + + if (node->next) { + node = node->next; + } else { + /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */ + do { + node = node->parent; + if (node == base) { + return; + } + } while (node->next == NULL); + node = node->next; + } + } +} + +static void dom_eliminate_ns(xmlNodePtr nodep, xmlNsPtr nsptr) +{ + if (nsptr->href != NULL) { + xmlFree((char *) nsptr->href); + nsptr->href = NULL; + } + if (nsptr->prefix != NULL) { + xmlFree((char *) nsptr->prefix); + nsptr->prefix = NULL; + } + + /* Remove it from the list and move it to the old ns list */ + xmlNsPtr current_ns = nodep->nsDef; + if (current_ns == nsptr) { + nodep->nsDef = nsptr->next; + } else { + do { + if (current_ns->next == nsptr) { + current_ns->next = nsptr->next; + break; + } + current_ns = current_ns->next; + } while (current_ns != NULL); + } + nsptr->next = NULL; + dom_set_old_ns(nodep->doc, nsptr); + + dom_remove_eliminated_ns(nodep, nsptr); +} + /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNS Since: DOM Level 2 */ @@ -754,14 +831,7 @@ PHP_METHOD(DOMElement, removeAttributeNS) nsptr = dom_get_nsdecl(nodep, (xmlChar *)name); if (nsptr != NULL) { if (xmlStrEqual((xmlChar *)uri, nsptr->href)) { - if (nsptr->href != NULL) { - xmlFree((char *) nsptr->href); - nsptr->href = NULL; - } - if (nsptr->prefix != NULL) { - xmlFree((char *) nsptr->prefix); - nsptr->prefix = NULL; - } + dom_eliminate_ns(nodep, nsptr); } else { RETURN_NULL(); } diff --git a/ext/dom/tests/gh12616_1.phpt b/ext/dom/tests/gh12616_1.phpt new file mode 100644 index 0000000000000..408d871aee6f6 --- /dev/null +++ b/ext/dom/tests/gh12616_1.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix) +--EXTENSIONS-- +dom +--FILE-- +loadXML( + << + CHILDREN + + XML +); + +$doc->documentElement->removeAttributeNS('http://symfony.com/schema/dic/services', ''); +echo $doc->saveXML(); + +$new = new DOMDocument(); +$new->append( + $new->importNode($doc->documentElement, true) +); + +echo $new->saveXML(); + +?> +--EXPECT-- + + + CHILDREN + + + + CHILDREN + diff --git a/ext/dom/tests/gh12616_2.phpt b/ext/dom/tests/gh12616_2.phpt new file mode 100644 index 0000000000000..57138e4c45b3f --- /dev/null +++ b/ext/dom/tests/gh12616_2.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix) +--EXTENSIONS-- +dom +--FILE-- +loadXML( + << + + + + + XML +); + +$doc->documentElement->removeAttributeNS('http://symfony.com/schema/dic/services', 'symfony'); +$xpath = new DOMXPath($doc); +$xpath->registerNamespace('test', 'urn:test'); + +echo $doc->saveXML(); + +$result = $xpath->query('//container/services/test:service[@id="hello"]'); +var_dump($result); + +?> +--EXPECT-- + + + + + + +object(DOMNodeList)#4 (1) { + ["length"]=> + int(1) +} diff --git a/ext/dom/tests/gh12616_3.phpt b/ext/dom/tests/gh12616_3.phpt new file mode 100644 index 0000000000000..871a5e4607a30 --- /dev/null +++ b/ext/dom/tests/gh12616_3.phpt @@ -0,0 +1,152 @@ +--TEST-- +GH-12616 (DOM: Removing XMLNS namespace node results in invalid default: prefix) +--EXTENSIONS-- +dom +--FILE-- +loadXML( + << + + + + + + + + + + XML +); + +$doc->documentElement->firstElementChild->removeAttributeNS('http://symfony.com/schema/dic/services', 'x'); +echo $doc->saveXML(); + +$xpath = new DOMXPath($doc); + +echo "--- Namespaces of child1 ---\n"; + +foreach ($xpath->query("/container/child1/namespace::*") as $ns) { + var_dump($ns); +} + +echo "--- Namespaces of child1/foo (both nodes) ---\n"; + +foreach ($xpath->query("/container/child1/foo/namespace::*") as $ns) { + var_dump($ns); +} + +echo "--- Namespaces of child2 ---\n"; + +foreach ($xpath->query("/container/child2/namespace::*") as $ns) { + var_dump($ns); +} + +?> +--EXPECT-- + + + + + + + + + + + +--- Namespaces of child1 --- +object(DOMNameSpaceNode)#4 (8) { + ["nodeName"]=> + string(9) "xmlns:xml" + ["nodeValue"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["nodeType"]=> + int(18) + ["prefix"]=> + string(3) "xml" + ["localName"]=> + string(3) "xml" + ["namespaceURI"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" +} +--- Namespaces of child1/foo (both nodes) --- +object(DOMNameSpaceNode)#5 (8) { + ["nodeName"]=> + string(9) "xmlns:xml" + ["nodeValue"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["nodeType"]=> + int(18) + ["prefix"]=> + string(3) "xml" + ["localName"]=> + string(3) "xml" + ["namespaceURI"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" +} +object(DOMNameSpaceNode)#8 (8) { + ["nodeName"]=> + string(9) "xmlns:xml" + ["nodeValue"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["nodeType"]=> + int(18) + ["prefix"]=> + string(3) "xml" + ["localName"]=> + string(3) "xml" + ["namespaceURI"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" +} +--- Namespaces of child2 --- +object(DOMNameSpaceNode)#9 (8) { + ["nodeName"]=> + string(9) "xmlns:xml" + ["nodeValue"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["nodeType"]=> + int(18) + ["prefix"]=> + string(3) "xml" + ["localName"]=> + string(3) "xml" + ["namespaceURI"]=> + string(36) "http://www.w3.org/XML/1998/namespace" + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" +} +object(DOMNameSpaceNode)#5 (8) { + ["nodeName"]=> + string(7) "xmlns:x" + ["nodeValue"]=> + string(38) "http://symfony.com/schema/dic/services" + ["nodeType"]=> + int(18) + ["prefix"]=> + string(1) "x" + ["localName"]=> + string(1) "x" + ["namespaceURI"]=> + string(38) "http://symfony.com/schema/dic/services" + ["ownerDocument"]=> + string(22) "(object value omitted)" + ["parentNode"]=> + string(22) "(object value omitted)" +} diff --git a/ext/exif/tests/bug78793.phpt b/ext/exif/tests/bug78793.phpt index 93d728ff6d1cf..babbe927045d3 100644 --- a/ext/exif/tests/bug78793.phpt +++ b/ext/exif/tests/bug78793.phpt @@ -4,7 +4,7 @@ Bug #78793: Use-after-free in exif parsing under memory sanitizer exif --FILE-- +--FILE-- + +--CLEAN-- +--EXPECTF-- + +Warning: imagecreatefrompng(): "php://filter/read=convert.base64-encode/resource=%s" is not a valid PNG file in %s on line %d diff --git a/ext/intl/tests/bug69398-icu74.1.phpt b/ext/intl/tests/bug69398-icu74.1.phpt new file mode 100644 index 0000000000000..cf8ce39d1e270 --- /dev/null +++ b/ext/intl/tests/bug69398-icu74.1.phpt @@ -0,0 +1,19 @@ +--TEST-- +IntlDateFormatter::formatObject(): returns wrong value when time style is NONE. +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +setTime($millitimestamp); +echo IntlDateFormatter::formatObject($date, array(IntlDateFormatter::SHORT, IntlDateFormatter::NONE), 'vi_VN'), "\n"; +echo IntlDateFormatter::formatObject ($date, array(IntlDateFormatter::SHORT, IntlDateFormatter::NONE), 'ko_KR'), "\n"; +?> +--EXPECT-- +4/4/15 +15. 4. 4. diff --git a/ext/intl/tests/bug69398.phpt b/ext/intl/tests/bug69398.phpt index 02c4b7daeff9f..075dd5f4bd0c8 100644 --- a/ext/intl/tests/bug69398.phpt +++ b/ext/intl/tests/bug69398.phpt @@ -3,7 +3,10 @@ IntlDateFormatter::formatObject(): returns wrong value when time style is NONE. --EXTENSIONS-- intl --SKIPIF-- -= 51.1.2'); ?> += 0) die('skip for ICU >= 74.1'); +?> --FILE-- diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h index c0775a07f5dcc..a1011f0b17858 100644 --- a/ext/libxml/php_libxml.h +++ b/ext/libxml/php_libxml.h @@ -35,6 +35,7 @@ extern zend_module_entry libxml_module_entry; #include "zend_smart_str.h" #include +#include #define LIBXML_SAVE_NOEMPTYTAG 1<<2 diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index b1a400499781a..68a0255ee0550 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -346,8 +346,8 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s) } MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic); } + PACKET_FREE(&fields_eof); } while (0); - PACKET_FREE(&fields_eof); break; /* switch break */ } } while (0); diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4 index 2a83fa2455974..0b923206282c4 100644 --- a/ext/opcache/config.m4 +++ b/ext/opcache/config.m4 @@ -29,7 +29,7 @@ if test "$PHP_OPCACHE" != "no"; then if test "$PHP_OPCACHE_JIT" = "yes"; then case $host_cpu in - i[[34567]]86*|x86*|aarch64) + i[[34567]]86*|x86*|aarch64|amd64) ;; *) AC_MSG_WARN([JIT not supported by host architecture]) @@ -48,7 +48,8 @@ if test "$PHP_OPCACHE" != "no"; then DASM_FLAGS="-D X64APPLE=1 -D X64=1" DASM_ARCH="x86" ;; - x86_64*) + *x86_64*|amd64-*-freebsd*) + IR_TARGET=IR_TARGET_X64 DASM_FLAGS="-D X64=1" DASM_ARCH="x86" ;; diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index fc52986ea197e..de40449e4095e 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1135,7 +1135,8 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_str_is_helper(zend_string *str, zva goto try_string_offset; default: zend_jit_illegal_string_offset(dim); - break; + ZVAL_NULL(result); + return; } offset = zval_get_long_func(dim, /* is_strict */ false); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 4a57986130666..86cd2aae06094 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5022,6 +5022,21 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par zend_may_throw_ex(opline, ssa_op, op_array, ssa, op1_info, op2_info))) { goto jit_failure; } + if (ssa_op->op2_def > 0 + && Z_MODE(op2_addr) == IS_REG + && ssa->vars[ssa_op->op2_def].no_val) { + uint8_t type = (op2_info & MAY_BE_LONG) ? IS_LONG : IS_DOUBLE; + uint32_t var_num = EX_VAR_TO_NUM(opline->op2.var); + + if (STACK_MEM_TYPE(stack, var_num) != type + && ssa->vars[ssa_op->op2_def].use_chain < 0 + && !ssa->vars[ssa_op->op2_def].phi_use_chain) { + if (!zend_jit_store_var_type(&dasm_state, var_num, type)) { + return 0; + } + SET_STACK_TYPE(stack, var_num, type, 1); + } + } if (opline->op2_type == IS_CV && ssa_op->op2_def >= 0 && ssa->vars[ssa_op->op2_def].alias == NO_ALIAS) { @@ -5058,6 +5073,21 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par res_use_info, res_info, res_addr)) { goto jit_failure; } + if (ssa_op->op1_def > 0 + && Z_MODE(op1_addr) == IS_REG + && ssa->vars[ssa_op->op1_def].no_val) { + uint8_t type = (op1_info & MAY_BE_LONG) ? IS_LONG : IS_DOUBLE; + uint32_t var_num = EX_VAR_TO_NUM(opline->op1.var); + + if (STACK_MEM_TYPE(stack, var_num) != type + && ssa->vars[ssa_op->op1_def].use_chain < 0 + && !ssa->vars[ssa_op->op1_def].phi_use_chain) { + if (!zend_jit_store_var_type(&dasm_state, var_num, type)) { + return 0; + } + SET_STACK_TYPE(stack, var_num, type, 1); + } + } if (opline->op1_type == IS_CV && ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) { @@ -5140,6 +5170,21 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par op1_info, op1_addr, op1_def_addr)) { goto jit_failure; } + if (ssa_op->op1_def > 0 + && Z_MODE(op1_addr) == IS_REG + && ssa->vars[ssa_op->op1_def].no_val) { + uint8_t type = (op1_info & MAY_BE_LONG) ? IS_LONG : IS_DOUBLE; + uint32_t var_num = EX_VAR_TO_NUM(opline->op1.var); + + if (STACK_MEM_TYPE(stack, var_num) != type + && ssa->vars[ssa_op->op1_def].use_chain < 0 + && !ssa->vars[ssa_op->op1_def].phi_use_chain) { + if (!zend_jit_store_var_type(&dasm_state, var_num, type)) { + return 0; + } + SET_STACK_TYPE(stack, var_num, type, 1); + } + } if (opline->op1_type == IS_CV && ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias == NO_ALIAS) { @@ -6872,9 +6917,30 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } } else if (p->stop == ZEND_JIT_TRACE_STOP_LINK || p->stop == ZEND_JIT_TRACE_STOP_INTERPRETER) { - if (!zend_jit_trace_deoptimization(&dasm_state, 0, NULL, - stack, op_array->last_var + op_array->T, NULL, NULL, NULL, 0)) { - goto jit_failure; + if (ra + && (p-1)->op != ZEND_JIT_TRACE_ENTER + && (p-1)->op != ZEND_JIT_TRACE_BACK + && opline->opcode != ZEND_DO_UCALL + && opline->opcode != ZEND_DO_FCALL + && opline->opcode != ZEND_DO_FCALL_BY_NAME + && opline->opcode != ZEND_INCLUDE_OR_EVAL) { + if (!zend_jit_trace_deoptimization(&dasm_state, 0, NULL, + stack, op_array->last_var + op_array->T, NULL, NULL, NULL, 0)) { + goto jit_failure; + } + for (i = 0; i < op_array->last_var; i++) { + int8_t reg = STACK_REG(stack, i); + uint8_t type = STACK_TYPE(stack, i); + + if (reg == ZREG_NONE + && type != IS_UNKNOWN + && type != STACK_MEM_TYPE(stack, i)) { + if (!zend_jit_store_var_type(&dasm_state, i, type)) { + return 0; + } + SET_STACK_TYPE(stack, i, type, 1); + } + } } if (p->stop == ZEND_JIT_TRACE_STOP_LINK) { const void *timeout_exit_addr = NULL; diff --git a/ext/opcache/tests/jit/gh12512.phpt b/ext/opcache/tests/jit/gh12512.phpt new file mode 100644 index 0000000000000..35307d18cc61f --- /dev/null +++ b/ext/opcache/tests/jit/gh12512.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-12512: missing type store +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--FILE-- + $val) { + if ($val === 2) { + unset($a[$key]); + } + } + return $ret; +} + +function foo($a, bool $b): bool { + if ($b) return true; + $n2 = count($a); + do { + $n = $n2; + $res = bar($a); + $n2 = count($a); + } while ($res === null && $n !== $n2); + + if ($res === null && $n === 0) { + return false; + } + return true; +} + +$a = [1,'a'=>5]; +bar($a); +foo([1,'a'=>5], true); +foo([1,'a'=>5], false); +foo([2,'a'=>5], false); +?> +DONE +--EXPECT-- +DONE diff --git a/ext/opcache/tests/jit/gh12512_2.phpt b/ext/opcache/tests/jit/gh12512_2.phpt new file mode 100644 index 0000000000000..67c5b091494c3 --- /dev/null +++ b/ext/opcache/tests/jit/gh12512_2.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-12512: missing type store +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--FILE-- + $y; + } + if ($exit) { + return $n; + } + } + + return $n; +} +var_dump(foo([1,2,3,4,5,6,7,8], 1)); +var_dump(foo([1,2,3,4,5,6,7,8], 1)); +var_dump(foo([1,2,3,4,5,6,7,8], 0)); +?> +DONE +--EXPECT-- +int(0) +int(0) +int(0) +DONE diff --git a/ext/opcache/tests/jit/gh12748.phpt b/ext/opcache/tests/jit/gh12748.phpt new file mode 100644 index 0000000000000..d7580fdb9ab7c --- /dev/null +++ b/ext/opcache/tests/jit/gh12748.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-12748: Function JIT emits "could not convert to int" warning at the same time as invalid offset Error +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--FILE-- +getMessage(), "\n"; +} +try { + echo "empty():\n"; + var_dump(empty($container[new stdClass()])); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +try { + echo "Coalesce():\n"; + var_dump($container[new stdClass()] ?? 'default'); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +isset(): +bool(false) +empty(): +bool(true) +Coalesce(): +Cannot access offset of type stdClass on string diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 31baa2d0e0250..a6a05fe03db50 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -5382,7 +5382,7 @@ PHP_FUNCTION(openssl_pkcs7_verify) signersfilename, signersfilename_len, 3, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY)); if (certout) { int i; - signers = PKCS7_get0_signers(p7, NULL, (int)flags); + signers = PKCS7_get0_signers(p7, others, (int)flags); if (signers != NULL) { for (i = 0; i < sk_X509_num(signers); i++) { diff --git a/ext/openssl/tests/CertificateGenerator.inc b/ext/openssl/tests/CertificateGenerator.inc index 4783353a4709c..12764c8b63d2c 100644 --- a/ext/openssl/tests/CertificateGenerator.inc +++ b/ext/openssl/tests/CertificateGenerator.inc @@ -85,7 +85,7 @@ class CertificateGenerator openssl_x509_export_to_file($this->ca, $file); } - public function saveNewCertAsFileWithKey( + private function generateCertAndKey( $commonNameForCert, $file, $keyLength = null, $subjectAltName = null ) { $dn = [ @@ -120,36 +120,50 @@ CONFIG; $configFile = $file . '.cnf'; file_put_contents($configFile, $configCode); - try { - $config = [ - 'config' => $configFile, - 'req_extensions' => 'v3_req', - 'x509_extensions' => 'usr_cert', - ]; - - $this->lastKey = self::generateKey($keyLength); - $csr = openssl_csr_new($dn, $this->lastKey, $config); - $this->lastCert = openssl_csr_sign( - $csr, - $this->ca, - $this->caKey, - /* days */ 2, - $config, - ); - if (!$this->lastCert) { - throw new Exception('Failed to create certificate'); - } + $config = [ + 'config' => $configFile, + 'req_extensions' => 'v3_req', + 'x509_extensions' => 'usr_cert', + ]; - $certText = ''; - openssl_x509_export($this->lastCert, $certText); + $this->lastKey = self::generateKey($keyLength); + $csr = openssl_csr_new($dn, $this->lastKey, $config); + $this->lastCert = openssl_csr_sign( + $csr, + $this->ca, + $this->caKey, + /* days */ 2, + $config, + ); - $keyText = ''; - openssl_pkey_export($this->lastKey, $keyText, null, $config); + return $config; + } - file_put_contents($file, $certText . PHP_EOL . $keyText); - } finally { - unlink($configFile); - } + public function saveNewCertAsFileWithKey( + $commonNameForCert, $file, $keyLength = null, $subjectAltName = null + ) { + $config = $this->generateCertAndKey($commonNameForCert, $file, $keyLength, $subjectAltName); + + $certText = ''; + openssl_x509_export($this->lastCert, $certText); + + $keyText = ''; + openssl_pkey_export($this->lastKey, $keyText, null, $config); + + file_put_contents($file, $certText . PHP_EOL . $keyText); + + unlink($config['config']); + } + + public function saveNewCertAndKey( + $commonNameForCert, $certFile, $keyFile, $keyLength = null, $subjectAltName = null + ) { + $config = $this->generateCertAndKey($commonNameForCert, $certFile, $keyLength, $subjectAltName); + + openssl_x509_export_to_file($this->lastCert, $certFile); + openssl_pkey_export_to_file($this->lastKey, $keyFile, null, $config); + + unlink($config['config']); } public function getCertDigest($algo) diff --git a/ext/openssl/tests/bug50713.phpt b/ext/openssl/tests/bug50713.phpt new file mode 100644 index 0000000000000..95eff2e75f90a --- /dev/null +++ b/ext/openssl/tests/bug50713.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #50713 (openssl_pkcs7_verify() may ignore untrusted CAs) +--EXTENSIONS-- +openssl +--FILE-- +saveCaCert($cacertFile); +$certificateGenerator->saveNewCertAndKey('bug50713', $certFile, $keyFile, 1024); + +var_dump(openssl_pkcs7_sign($inFile, $outFile, 'file://' . $certFile, 'file://' . $keyFile, [], PKCS7_NOCERTS)); +var_dump(openssl_pkcs7_verify($outFile, 0, $signersFile, [$cacertFile], $certFile)); +var_dump(strlen(file_get_contents($signersFile)) > 0); +?> +--CLEAN-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/ext/pcre/tests/gh11374.phpt b/ext/pcre/tests/gh11374.phpt index 07f8f4bccfd0a..29f6485cc0440 100644 --- a/ext/pcre/tests/gh11374.phpt +++ b/ext/pcre/tests/gh11374.phpt @@ -1,5 +1,11 @@ --TEST-- GH-11374 (PCRE regular expression without JIT enabled gives different result) +--EXTENSIONS-- +zend_test +--SKIPIF-- + --FILE-- +--FILE-- + +--EXPECT-- +OK diff --git a/ext/soap/tests/bug75306.phpt b/ext/soap/tests/bug75306.phpt index 7501fde59e6b8..2998ddddff431 100644 --- a/ext/soap/tests/bug75306.phpt +++ b/ext/soap/tests/bug75306.phpt @@ -7,11 +7,11 @@ soap $options = array("cache_wsdl" => WSDL_CACHE_NONE); // Need a warm-up for globals for ($i = 0; $i < 10; $i++) { - $client = new SoapClient("ext/soap/tests/test.wsdl", $options); + $client = new SoapClient(__DIR__ . "/test.wsdl", $options); } $usage = memory_get_usage(); for ($i = 0; $i < 10; $i++) { - $client = new SoapClient("ext/soap/tests/test.wsdl", $options); + $client = new SoapClient(__DIR__ . "/test.wsdl", $options); } $usage_delta = memory_get_usage() - $usage; var_dump($usage_delta); diff --git a/ext/sqlite3/tests/sqlite3_defensive.phpt b/ext/sqlite3/tests/sqlite3_defensive.phpt index 033e661d8a393..056f716170c4c 100644 --- a/ext/sqlite3/tests/sqlite3_defensive.phpt +++ b/ext/sqlite3/tests/sqlite3_defensive.phpt @@ -20,7 +20,6 @@ var_dump($db->exec('CREATE TABLE test (a, b);')); // This does not generate an error! var_dump($db->exec('PRAGMA writable_schema = ON;')); -var_dump($db->querySingle('PRAGMA writable_schema;')); // Should be 1 var_dump($db->querySingle('SELECT COUNT(*) FROM sqlite_master;')); @@ -35,8 +34,7 @@ var_dump($db->querySingle('SELECT COUNT(*) FROM sqlite_master;')); bool(true) bool(true) int(1) -int(1) Warning: SQLite3::querySingle(): Unable to prepare statement: 1, table sqlite_master may not be modified in %s on line %d bool(false) -int(1) \ No newline at end of file +int(1) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 1e50a37f2c687..ddaf1368410bc 100755 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -230,6 +230,8 @@ static void basic_globals_ctor(php_basic_globals *basic_globals_p) /* {{{ */ BG(page_uid) = -1; BG(page_gid) = -1; + + BG(syslog_device) = NULL; } /* }}} */ @@ -427,9 +429,6 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ PHP_MSHUTDOWN_FUNCTION(basic) /* {{{ */ { -#ifdef HAVE_SYSLOG_H - PHP_MSHUTDOWN(syslog)(SHUTDOWN_FUNC_ARGS_PASSTHRU); -#endif #ifdef ZTS ts_free_id(basic_globals_id); #ifdef PHP_WIN32 @@ -487,9 +486,6 @@ PHP_RINIT_FUNCTION(basic) /* {{{ */ BG(user_shutdown_function_names) = NULL; PHP_RINIT(filestat)(INIT_FUNC_ARGS_PASSTHRU); -#ifdef HAVE_SYSLOG_H - BASIC_RINIT_SUBMODULE(syslog) -#endif BASIC_RINIT_SUBMODULE(dir) BASIC_RINIT_SUBMODULE(url_scanner_ex) @@ -541,9 +537,7 @@ PHP_RSHUTDOWN_FUNCTION(basic) /* {{{ */ PHP_RSHUTDOWN(filestat)(SHUTDOWN_FUNC_ARGS_PASSTHRU); #ifdef HAVE_SYSLOG_H -#ifdef PHP_WIN32 - BASIC_RSHUTDOWN_SUBMODULE(syslog)(SHUTDOWN_FUNC_ARGS_PASSTHRU); -#endif + BASIC_RSHUTDOWN_SUBMODULE(syslog); #endif BASIC_RSHUTDOWN_SUBMODULE(assert) BASIC_RSHUTDOWN_SUBMODULE(url_scanner_ex) diff --git a/ext/standard/browscap.c b/ext/standard/browscap.c index 53504098fb85a..222681915122d 100644 --- a/ext/standard/browscap.c +++ b/ext/standard/browscap.c @@ -228,7 +228,7 @@ static zend_string *browscap_intern_str( } else { interned = zend_string_copy(str); if (persistent) { - interned = zend_new_interned_string(str); + interned = zend_new_interned_string(interned); } zend_hash_add_new_ptr(&ctx->str_interned, interned, interned); } @@ -397,10 +397,6 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callb } /* }}} */ -static void str_interned_dtor(zval *zv) { - zend_string_release(Z_STR_P(zv)); -} - static int browscap_read_file(char *filename, browser_data *browdata, int persistent) /* {{{ */ { zend_file_handle fh; @@ -430,7 +426,9 @@ static int browscap_read_file(char *filename, browser_data *browdata, int persis ctx.bdata = browdata; ctx.current_entry = NULL; ctx.current_section_name = NULL; - zend_hash_init(&ctx.str_interned, 8, NULL, str_interned_dtor, persistent); + /* No dtor because we don't inc the refcount for the reference stored within the hash table's entry value + * as the hash table is only temporary anyway. */ + zend_hash_init(&ctx.str_interned, 8, NULL, NULL, persistent); zend_parse_ini_file(&fh, persistent, ZEND_INI_SCANNER_RAW, (zend_ini_parser_cb_t) php_browscap_parser_cb, &ctx); diff --git a/ext/standard/php_ext_syslog.h b/ext/standard/php_ext_syslog.h index 5e091fb5280df..fd7958fad2929 100644 --- a/ext/standard/php_ext_syslog.h +++ b/ext/standard/php_ext_syslog.h @@ -22,11 +22,7 @@ #include "php_syslog.h" PHP_MINIT_FUNCTION(syslog); -PHP_RINIT_FUNCTION(syslog); -#ifdef PHP_WIN32 PHP_RSHUTDOWN_FUNCTION(syslog); -#endif -PHP_MSHUTDOWN_FUNCTION(syslog); #endif diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index a57e66bd97954..3f8eaafd6d281 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -1096,6 +1096,7 @@ PHP_FUNCTION(proc_open) descriptors[ndesc].index = (int)nindex; + ZVAL_DEREF(descitem); if (Z_TYPE_P(descitem) == IS_RESOURCE) { if (set_proc_descriptor_from_resource(descitem, &descriptors[ndesc], ndesc) == FAILURE) { goto exit_fail; diff --git a/ext/standard/syslog.c b/ext/standard/syslog.c index 70fa8e241abdd..9d392f212ecc7 100644 --- a/ext/standard/syslog.c +++ b/ext/standard/syslog.c @@ -90,29 +90,14 @@ PHP_MINIT_FUNCTION(syslog) /* AIX doesn't have LOG_PERROR */ REGISTER_LONG_CONSTANT("LOG_PERROR", LOG_PERROR, CONST_CS | CONST_PERSISTENT); /*log to stderr*/ #endif - BG(syslog_device)=NULL; return SUCCESS; } /* }}} */ -PHP_RINIT_FUNCTION(syslog) -{ - BG(syslog_device) = NULL; - return SUCCESS; -} - - -#ifdef PHP_WIN32 PHP_RSHUTDOWN_FUNCTION(syslog) { - closelog(); - return SUCCESS; -} -#endif - -PHP_MSHUTDOWN_FUNCTION(syslog) -{ + php_closelog(); if (BG(syslog_device)) { free(BG(syslog_device)); BG(syslog_device) = NULL; diff --git a/ext/standard/tests/file/005_variation2.phpt b/ext/standard/tests/file/005_variation2.phpt index 55d1d6666098c..270a6cfbdd84c 100644 --- a/ext/standard/tests/file/005_variation2.phpt +++ b/ext/standard/tests/file/005_variation2.phpt @@ -28,6 +28,11 @@ function stat_fn( $filename ) { echo "*** Testing fileattime(), filemtime(), filectime() & touch() : usage variations ***\n"; echo "\n*** testing touch ***\n"; + +$dir = __DIR__ . '/005_variation2'; +mkdir($dir); +chdir($dir); + $b = touch(false); $c = touch(''); $d = touch(' '); @@ -47,6 +52,7 @@ stat_fn('|'); var_dump(unlink(' ')); var_dump(unlink('|')); +rmdir($dir); echo "Done"; ?> diff --git a/ext/standard/tests/file/file.inc b/ext/standard/tests/file/file.inc index d4ad02a363bd5..c972784f9da36 100644 --- a/ext/standard/tests/file/file.inc +++ b/ext/standard/tests/file/file.inc @@ -590,9 +590,9 @@ $all_stat_keys = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, "rdev", "size", "atime", "mtime", "ctime", "blksize", "blocks"); -$stat_time_diff_keys = array(8, 'atime'); - function compare_stats($stat1, $stat2, $fields, $op = "==", $flag = false ) { + $stat_time_diff_keys = array(8, 'atime'); + // dump the stat if requested if ( $flag == true ) { var_dump($stat1); @@ -608,10 +608,10 @@ function compare_stats($stat1, $stat2, $fields, $op = "==", $flag = false ) { { case "==": if ( $stat1[ $fields[$index] ] != $stat2[ $fields[$index] ] ) { - if ( ! in_array( $index, $stat_time_diff_keys ) ) { + if ( ! in_array( $fields[$index], $stat_time_diff_keys ) ) { $result = false; echo "Error: stat1 do not match with stat2 at key value: $fields[$index]\n"; - } elseif (abs($stat1[ $fields[$index] ] - $stat2[ $fields[$index] ]) > 1) { + } elseif (abs($stat1[ $fields[$index] ] - $stat2[ $fields[$index] ]) > 2) { $result = false; echo "Error: stat1 differs too much from stat2 at key value: $fields[$index]\n"; } diff --git a/ext/standard/tests/file/is_readable_basic.phpt b/ext/standard/tests/file/is_readable_basic.phpt index 7f1b85501a357..38d075439a3f2 100644 --- a/ext/standard/tests/file/is_readable_basic.phpt +++ b/ext/standard/tests/file/is_readable_basic.phpt @@ -15,7 +15,7 @@ require __DIR__.'/file.inc'; echo "*** Testing is_readable(): basic functionality ***\n"; // create a file -$filename = __DIR__."/is_readable.tmp"; +$filename = __DIR__."/is_readable_basic.tmp"; create_file($filename); $counter = 1; diff --git a/ext/standard/tests/file/is_readable_error.phpt b/ext/standard/tests/file/is_readable_error.phpt index 8df7ff92d526c..c013f7fbb7049 100644 --- a/ext/standard/tests/file/is_readable_error.phpt +++ b/ext/standard/tests/file/is_readable_error.phpt @@ -3,7 +3,7 @@ Test is_readable() function: error conditions --FILE-- diff --git a/ext/standard/tests/general_functions/gh12655.phpt b/ext/standard/tests/general_functions/gh12655.phpt new file mode 100644 index 0000000000000..c0235ee6ae6fb --- /dev/null +++ b/ext/standard/tests/general_functions/gh12655.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-12655 (proc_open(): Argument #2 ($descriptor_spec) must only contain arrays and streams [Descriptor item must be either an array or a File-Handle]) +--FILE-- + [ "pipe", "r" ], // stdin is a pipe that the child will read from + 1 => [ "pipe", "w" ], // stdout is a pipe that the child will write to + 2 => [ "pipe", "w" ], // stderr is a file to write to +]; + +foreach ( $descriptor_spec as $fd => &$d ) +{ + // don't do anything, just the fact that we used "&$d" will sink the ship! +} + +$proc = proc_open(PHP_BINARY, $descriptor_spec, $pipes); +echo $proc === false ? "FAILED\n" : "SUCCEEDED\n"; + +?> +--EXPECT-- +SUCCEEDED diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h index 05f658e91bdb6..9d0ca087ce6f7 100644 --- a/ext/zend_test/php_test.h +++ b/ext/zend_test/php_test.h @@ -52,6 +52,9 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test) HashTable global_weakmap; int replace_zend_execute_ex; int register_passes; + int observe_opline_in_zendmm; + zend_mm_heap* zend_orig_heap; + zend_mm_heap* zend_test_heap; zend_test_fiber *active_fiber; ZEND_END_MODULE_GLOBALS(zend_test) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index c1c65450ea907..c2a6bd18a5433 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -30,8 +30,15 @@ #include "zend_interfaces.h" #include "zend_weakrefs.h" #include "Zend/Optimizer/zend_optimizer.h" +#include "Zend/zend_alloc.h" #include "test_arginfo.h" +// `php.h` sets `NDEBUG` when not `PHP_DEBUG` which will make `assert()` from +// assert.h a no-op. In order to have `assert()` working on NDEBUG builds, we +// undefine `NDEBUG` and re-include assert.h +#undef NDEBUG +#include "assert.h" + #if defined(HAVE_LIBXML) && !defined(PHP_WIN32) # include # include @@ -364,6 +371,78 @@ static ZEND_FUNCTION(zend_test_crash) php_printf("%s", invalid); } +static bool has_opline(zend_execute_data *execute_data) +{ + return execute_data + && execute_data->func + && ZEND_USER_CODE(execute_data->func->type) + && execute_data->opline + ; +} + +void * zend_test_custom_malloc(size_t len) +{ + if (has_opline(EG(current_execute_data))) { + assert(EG(current_execute_data)->opline->lineno != (uint32_t)-1); + } + return _zend_mm_alloc(ZT_G(zend_orig_heap), len ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC); +} + +void zend_test_custom_free(void *ptr) +{ + if (has_opline(EG(current_execute_data))) { + assert(EG(current_execute_data)->opline->lineno != (uint32_t)-1); + } + _zend_mm_free(ZT_G(zend_orig_heap), ptr ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC); +} + +void * zend_test_custom_realloc(void * ptr, size_t len) +{ + if (has_opline(EG(current_execute_data))) { + assert(EG(current_execute_data)->opline->lineno != (uint32_t)-1); + } + return _zend_mm_realloc(ZT_G(zend_orig_heap), ptr, len ZEND_FILE_LINE_EMPTY_CC ZEND_FILE_LINE_EMPTY_CC); +} + +static PHP_INI_MH(OnUpdateZendTestObserveOplineInZendMM) +{ + if (new_value == NULL) { + return FAILURE; + } + + int int_value = zend_ini_parse_bool(new_value); + + if (int_value == 1) { + // `zend_mm_heap` is a private struct, so we have not way to find the + // actual size, but 4096 bytes should be enough + ZT_G(zend_test_heap) = malloc(4096); + memset(ZT_G(zend_test_heap), 0, 4096); + zend_mm_set_custom_handlers( + ZT_G(zend_test_heap), + zend_test_custom_malloc, + zend_test_custom_free, + zend_test_custom_realloc + ); + ZT_G(zend_orig_heap) = zend_mm_get_heap(); + zend_mm_set_heap(ZT_G(zend_test_heap)); + } else if (ZT_G(zend_test_heap)) { + free(ZT_G(zend_test_heap)); + ZT_G(zend_test_heap) = NULL; + zend_mm_set_heap(ZT_G(zend_orig_heap)); + } + return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); +} + +static ZEND_FUNCTION(zend_test_is_pcre_bundled) +{ + ZEND_PARSE_PARAMETERS_NONE(); +#if HAVE_BUNDLED_PCRE + RETURN_TRUE; +#else + RETURN_FALSE; +#endif +} + static zend_object *zend_test_class_new(zend_class_entry *class_type) { zend_object *obj = zend_objects_new(class_type); @@ -548,6 +627,7 @@ static ZEND_METHOD(ZendTestChildClassWithMethodWithParameterAttribute, override) PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("zend_test.replace_zend_execute_ex", "0", PHP_INI_SYSTEM, OnUpdateBool, replace_zend_execute_ex, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.register_passes", "0", PHP_INI_SYSTEM, OnUpdateBool, register_passes, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_BOOLEAN("zend_test.observe_opline_in_zendmm", "0", PHP_INI_ALL, OnUpdateZendTestObserveOplineInZendMM, observe_opline_in_zendmm, zend_zend_test_globals, zend_test_globals) PHP_INI_END() void (*old_zend_execute_ex)(zend_execute_data *execute_data); @@ -690,6 +770,13 @@ PHP_RSHUTDOWN_FUNCTION(zend_test) zend_weakrefs_hash_del(&ZT_G(global_weakmap), (zend_object *)(uintptr_t)objptr); } ZEND_HASH_FOREACH_END(); zend_hash_destroy(&ZT_G(global_weakmap)); + + if (ZT_G(zend_test_heap)) { + free(ZT_G(zend_test_heap)); + ZT_G(zend_test_heap) = NULL; + zend_mm_set_heap(ZT_G(zend_orig_heap)); + } + return SUCCESS; } diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 364792a5665e8..07d7fa7e19927 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -125,6 +125,8 @@ function zend_test_crash(?string $message = null): void {} #if defined(HAVE_LIBXML) && !defined(PHP_WIN32) function zend_test_override_libxml_global_state(): void {} #endif + + function zend_test_is_pcre_bundled(): bool {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index a8b84cbdeea62..6e3f645e3d143 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7effabe18579113dbfc4c61231d93c8c262d959a */ + * Stub hash: ae75eda2b4b40224858d680c3fcf3d7cd2056bb6 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -91,9 +91,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_override_libxml_global ZEND_END_ARG_INFO() #endif -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_ZendSubNS_namespaced_func, 0, 0, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_is_pcre_bundled, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() +#define arginfo_ZendTestNS2_ZendSubNS_namespaced_func arginfo_zend_test_is_pcre_bundled + #define arginfo_class__ZendTestClass_is_object arginfo_zend_get_map_ptr_last ZEND_BEGIN_ARG_INFO_EX(arginfo_class__ZendTestClass___toString, 0, 0, 0) @@ -112,7 +114,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class__ZendTestChildClass_returnsThrowable, 0, 0, Exception, 0) ZEND_END_ARG_INFO() -#define arginfo_class__ZendTestTrait_testMethod arginfo_ZendTestNS2_ZendSubNS_namespaced_func +#define arginfo_class__ZendTestTrait_testMethod arginfo_zend_test_is_pcre_bundled ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZendTestParameterAttribute___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, parameter, IS_STRING, 0) @@ -156,6 +158,7 @@ static ZEND_FUNCTION(zend_test_crash); #if defined(HAVE_LIBXML) && !defined(PHP_WIN32) static ZEND_FUNCTION(zend_test_override_libxml_global_state); #endif +static ZEND_FUNCTION(zend_test_is_pcre_bundled); static ZEND_FUNCTION(namespaced_func); static ZEND_METHOD(_ZendTestClass, is_object); static ZEND_METHOD(_ZendTestClass, __toString); @@ -199,6 +202,7 @@ static const zend_function_entry ext_functions[] = { #if defined(HAVE_LIBXML) && !defined(PHP_WIN32) ZEND_FE(zend_test_override_libxml_global_state, arginfo_zend_test_override_libxml_global_state) #endif + ZEND_FE(zend_test_is_pcre_bundled, arginfo_zend_test_is_pcre_bundled) ZEND_NS_FE("ZendTestNS2\\ZendSubNS", namespaced_func, arginfo_ZendTestNS2_ZendSubNS_namespaced_func) ZEND_FE_END }; diff --git a/ext/zend_test/tests/opline_dangling.phpt b/ext/zend_test/tests/opline_dangling.phpt new file mode 100644 index 0000000000000..3b27b544ca34f --- /dev/null +++ b/ext/zend_test/tests/opline_dangling.phpt @@ -0,0 +1,35 @@ +--TEST-- +possible segfault in `ZEND_BIND_STATIC` +--DESCRIPTION-- +https://github.com/php/php-src/pull/12758 +--EXTENSIONS-- +zend_test +--ENV-- +USE_ZEND_ALLOC=1 +--INI-- +zend_test.observe_opline_in_zendmm=1 +--FILE-- + +--EXPECT-- +int(1) +string(1) "x" +int(1) +int(5) +Done. diff --git a/ext/zend_test/tests/opline_dangling_02.phpt b/ext/zend_test/tests/opline_dangling_02.phpt new file mode 100644 index 0000000000000..585a9c8395b59 --- /dev/null +++ b/ext/zend_test/tests/opline_dangling_02.phpt @@ -0,0 +1,37 @@ +--TEST-- +possible segfault in `ZEND_FUNC_GET_ARGS` +--EXTENSIONS-- +zend_test +--ENV-- +USE_ZEND_ALLOC=1 +--INI-- +zend_test.observe_opline_in_zendmm=1 +--FILE-- + +--EXPECT-- +int(1) +string(1) "x" +int(1) +array(2) { + [0]=> + string(6) "string" + [1]=> + int(0) +} +Done. diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 96c3757a3740d..4dccdffbcdc42 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -1747,7 +1747,7 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* basename = php_basename(Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), NULL, 0); file_stripped = ZSTR_VAL(basename); file_stripped_len = ZSTR_LEN(basename); - } else if (opts.remove_path && strstr(Z_STRVAL_P(zval_file), opts.remove_path) != NULL) { + } else if (opts.remove_path && !memcmp(Z_STRVAL_P(zval_file), opts.remove_path, opts.remove_path_len)) { if (IS_SLASH(Z_STRVAL_P(zval_file)[opts.remove_path_len])) { file_stripped = Z_STRVAL_P(zval_file) + opts.remove_path_len + 1; file_stripped_len = Z_STRLEN_P(zval_file) - opts.remove_path_len - 1; diff --git a/ext/zip/tests/bug_gh12661.phpt b/ext/zip/tests/bug_gh12661.phpt new file mode 100644 index 0000000000000..993299b8657f2 --- /dev/null +++ b/ext/zip/tests/bug_gh12661.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug GH-12661 (Inconsistency in ZipArchive::addGlob 'remove_path' Option Behavior) +--EXTENSIONS-- +zip +--FILE-- +open($file, ZipArchive::CREATE | ZipArchive::OVERWRITE); +$zip->addGlob(__FILE__, 0, ['remove_path' => 'bug_']); // unchanged (bug is not a prefix) +$zip->addGlob(__FILE__, 0, ['remove_path' => dirname(__DIR__)]); +verify_entries($zip, [__FILE__, basename(__DIR__) . DIRECTORY_SEPARATOR . basename(__FILE__)]); +$zip->close(); + +?> +Done +--CLEAN-- + +--EXPECT-- +Done diff --git a/ext/zlib/tests/gzcompress_basic1.phpt b/ext/zlib/tests/gzcompress_basic1.phpt index ec5f554153dab..b5ad22341ed74 100644 --- a/ext/zlib/tests/gzcompress_basic1.phpt +++ b/ext/zlib/tests/gzcompress_basic1.phpt @@ -2,6 +2,8 @@ Test gzcompress() function : basic functionality --EXTENSIONS-- zlib +--SKIPIF-- + --FILE-- --FILE-- --FILE-- fclose_stdiocast == PHP_STREAM_FCLOSE_FOPENCOOKIE) { - /* flush to commit data written to the fopencookie FILE* */ - fflush(stream->stdiocast); + /* flush can call seek internally so we need to prevent an infinite loop */ + if (!stream->fclose_stdiocast_flush_in_progress) { + stream->fclose_stdiocast_flush_in_progress = 1; + /* flush to commit data written to the fopencookie FILE* */ + fflush(stream->stdiocast); + stream->fclose_stdiocast_flush_in_progress = 0; + } } /* handle the case where we are in the buffer */ diff --git a/run-tests.php b/run-tests.php index 404fd3e457a6a..39621a39aa666 100755 --- a/run-tests.php +++ b/run-tests.php @@ -2873,10 +2873,44 @@ function run_test(string $php, $file, array $env): string return $restype[0] . 'ED'; } +function is_flaky(TestFile $test): bool +{ + if ($test->hasSection('FLAKY')) { + return true; + } + if (!$test->hasSection('FILE')) { + return false; + } + $file = $test->getSection('FILE'); + $flaky_functions = [ + 'disk_free_space', + 'hrtime', + 'microtime', + 'sleep', + 'usleep', + ]; + $regex = '(\b(' . implode('|', $flaky_functions) . ')\()i'; + return preg_match($regex, $file) === 1; +} + +function is_flaky_output(string $output): bool +{ + $messages = [ + '404: page not found', + 'address already in use', + 'connection refused', + 'deadlock', + 'mailbox already exists', + 'timed out', + ]; + $regex = '(\b(' . implode('|', $messages) . ')\b)i'; + return preg_match($regex, $output) === 1; +} + function error_may_be_retried(TestFile $test, string $output): bool { - return preg_match('((timed out)|(connection refused)|(404: page not found)|(address already in use)|(mailbox already exists))i', $output) === 1 - || $test->hasSection('FLAKY'); + return is_flaky_output($output) + || is_flaky($test); } /** diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c index f0d869444afce..f698753cf4c65 100644 --- a/sapi/fpm/fpm/fpm_status.c +++ b/sapi/fpm/fpm/fpm_status.c @@ -56,7 +56,7 @@ int fpm_status_export_to_zval(zval *status) scoreboard_p = fpm_scoreboard_acquire(NULL, 1); if (!scoreboard_p) { - zlog(ZLOG_NOTICE, "[pool %s] status: scoreboard already in use.", scoreboard_p->pool); + zlog(ZLOG_NOTICE, "[pool (unknown)] status: scoreboard already in use."); return -1; } diff --git a/sapi/fpm/tests/gh12621.phpt b/sapi/fpm/tests/gh12621.phpt new file mode 100644 index 0000000000000..0ee64fbe415bd --- /dev/null +++ b/sapi/fpm/tests/gh12621.phpt @@ -0,0 +1,46 @@ +--TEST-- +GH-12621 (browscap segmentation fault when configured with php_admin_value) +--SKIPIF-- + +--FILE-- +browser_name_pattern; +var_dump(\$cv); +EOT; + +$tester = new FPM\Tester($cfg, $code); +$tester->start(); +$tester->expectLogStartNotices(); +echo $tester + ->request() + ->getBody(); +$tester->terminate(); +$tester->close(); + +?> +--EXPECT-- +string(14) "*Konqueror/2.*" +--CLEAN-- + diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index 4c50653ce66f9..8ca6d83c78c2b 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -408,6 +408,7 @@ PHPDBG_COMMAND(exec) /* {{{ */ if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) { if (PHPDBG_G(in_execution)) { if (phpdbg_ask_user_permission("Do you really want to stop execution to set a new execution context?") == FAILURE) { + free(res); return FAILURE; } } @@ -441,6 +442,7 @@ PHPDBG_COMMAND(exec) /* {{{ */ phpdbg_compile(); } else { + free(res); phpdbg_notice("Execution context not changed"); } } else { diff --git a/sapi/phpdbg/tests/gh12675.phpt b/sapi/phpdbg/tests/gh12675.phpt new file mode 100644 index 0000000000000..167e68595df31 --- /dev/null +++ b/sapi/phpdbg/tests/gh12675.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-12675 (MEMORY_LEAK in phpdbg_prompt.c) +--INI-- +opcache.enable=0 +--PHPDBG-- +ev file_put_contents("gh12675_1.tmp", " 24 +prompt> 16 +prompt> [Cannot stat nonexistent.php, ensure the file exists] +prompt> [Set execution context: %sgh12675_1.tmp] +[Successful compilation of %sgh12675_1.tmp] +prompt> [Execution context not changed] +prompt> [Breakpoint #0 added at %sgh12675_1.tmp:2] +prompt> hi +[Breakpoint #0 at %sgh12675_1.tmp:2, hits: 1] +>00002: echo 2; +prompt> Do you really want to stop execution to set a new execution context? (type y or n): prompt> +--CLEAN-- + diff --git a/travis/test.sh b/travis/test.sh index 13f5c8bd7b53b..51e02dbab0c6c 100755 --- a/travis/test.sh +++ b/travis/test.sh @@ -4,6 +4,7 @@ set -ex # ARM64 CI reports nproc=32, which is excessive. if [ -z "$ARM64" ]; then export JOBS=$(nproc); else export JOBS=16; fi +export SKIP_SLOW_TESTS=1 export SKIP_IO_CAPTURE_TESTS=1 ./sapi/cli/php run-tests.php -P \ -g "FAIL,XFAIL,BORK,WARN,LEAK,SKIP" --offline --show-diff --show-slow 1000 \