From 7967875d70f9cb1ef6865c40b6c6364d9bcd0536 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Sun, 14 Nov 2021 23:35:26 -0800 Subject: [PATCH 01/32] [ci skip] Update NEWS --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 3e18b09047a51..cede31ab91000 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 7.3.33 +- XML: + . Fix #79971: special character is breaking the path in xml function. + (CVE-2021-21707) (cmb) 28 Oct 2021, PHP 7.3.32 From 6f38acfaf906482a70dfb528ba0e427efbd8e2db Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 16 Nov 2021 10:11:21 +0100 Subject: [PATCH 02/32] Safely reassign array in usort() Make sure to destroy the old value only after assigning the new one. Otherwise we may try to double free, e.g. if GC runs during this dtor. This caused an assertion failure in phpro/grumphp and is likely the cause for bug #81603 as well. --- ext/standard/array.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index 4556cfe8e621a..32ad1b0eaa6ed 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -988,8 +988,10 @@ static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compar zend_hash_sort(arr, compare_func, renumber); - zval_ptr_dtor(array); + zval garbage; + ZVAL_COPY_VALUE(&garbage, array); ZVAL_ARR(array, arr); + zval_ptr_dtor(&garbage); PHP_ARRAY_CMP_FUNC_RESTORE(); RETURN_TRUE; From 18a0d46a1b44cc67e97ccdf9f828c690d651c7f1 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 16 Nov 2021 10:11:21 +0100 Subject: [PATCH 03/32] Safely reassign array in usort() Make sure to destroy the old value only after assigning the new one. Otherwise we may try to double free, e.g. if GC runs during this dtor. This caused an assertion failure in phpro/grumphp and is likely the cause for bug #81603 as well. (cherry picked from commit 6f38acfaf906482a70dfb528ba0e427efbd8e2db) (I applied this to the wrong base branch at first...) --- ext/standard/array.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/standard/array.c b/ext/standard/array.c index b7131fd7df38d..cd2e5287daf04 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1037,8 +1037,10 @@ static void php_usort(INTERNAL_FUNCTION_PARAMETERS, compare_func_t compare_func, retval = zend_hash_sort(arr, compare_func, renumber) != FAILURE; - zval_ptr_dtor(array); + zval garbage; + ZVAL_COPY_VALUE(&garbage, array); ZVAL_ARR(array, arr); + zval_ptr_dtor(&garbage); PHP_ARRAY_CMP_FUNC_RESTORE(); RETURN_BOOL(retval); From d26965b2474229efd9b4a997570b3fb924405be9 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 16 Nov 2021 12:44:51 +0100 Subject: [PATCH 04/32] Fixed bug #81626 Backport of a8926474cb2c68cde2a7c0d19bcd95cdbda55ad2 to 7.4. --- NEWS | 4 ++++ Zend/tests/bug81626.phpt | 17 +++++++++++++++++ Zend/zend_closures.c | 1 + 3 files changed, 22 insertions(+) create mode 100644 Zend/tests/bug81626.phpt diff --git a/NEWS b/NEWS index df2e22c820df0..8057de4f5178e 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 7.4.27 +- Core: + . Fixed bug #81626 (Error on use static:: in __сallStatic() wrapped to + Closure::fromCallable()). (Nikita) + - FPM: . Fixed bug #81513 (Future possibility for heap overflow in FPM zlog). (Jakub Zelenka) diff --git a/Zend/tests/bug81626.phpt b/Zend/tests/bug81626.phpt new file mode 100644 index 0000000000000..b05cb23097bbb --- /dev/null +++ b/Zend/tests/bug81626.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #81626: Error on use static:: in __сallStatic() wrapped to Closure::fromCallable() +--FILE-- + +--EXPECT-- +string(2) "ok" diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index a003d9f721092..f6acd050820a1 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -270,6 +270,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ { } fcc.object = fci.object = Z_OBJ_P(ZEND_THIS); + fcc.called_scope = zend_get_called_scope(EG(current_execute_data)); zend_call_function(&fci, &fcc); From d14a9139d5c55521b21ccf1529c2289b57e64bda Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Tue, 16 Nov 2021 13:06:04 +0100 Subject: [PATCH 05/32] [ci skip] Fix news entry for bug #79971 --- NEWS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index cede31ab91000..254a7faba15b1 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 7.3.33 +18 Nov 2021, PHP 7.3.33 - XML: - . Fix #79971: special character is breaking the path in xml function. + . Fixed bug #79971 (special character is breaking the path in xml function). (CVE-2021-21707) (cmb) 28 Oct 2021, PHP 7.3.32 From 7d92153182f86722f83963d6fb054609c40bed8d Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Tue, 16 Nov 2021 13:24:00 +0100 Subject: [PATCH 06/32] 7.3.34 might be next --- NEWS | 3 +++ Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 254a7faba15b1..118295078406a 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +?? ??? ????, PHP 7.3.34 + + 18 Nov 2021, PHP 7.3.33 - XML: diff --git a/Zend/zend.h b/Zend/zend.h index 7329303d83bd2..250bd00d97c67 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "3.3.32-dev" +#define ZEND_VERSION "3.3.34-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 7bb1aa1e5e0fc..3815cb54bc927 100644 --- a/configure.ac +++ b/configure.ac @@ -107,7 +107,7 @@ int zend_sprintf(char *buffer, const char *format, ...); PHP_MAJOR_VERSION=7 PHP_MINOR_VERSION=3 -PHP_RELEASE_VERSION=32 +PHP_RELEASE_VERSION=34 PHP_EXTRA_VERSION="-dev" PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION" PHP_VERSION_ID=`expr [$]PHP_MAJOR_VERSION \* 10000 + [$]PHP_MINOR_VERSION \* 100 + [$]PHP_RELEASE_VERSION` diff --git a/main/php_version.h b/main/php_version.h index 5bcd2c245e49f..200cf447c4750 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 7 #define PHP_MINOR_VERSION 3 -#define PHP_RELEASE_VERSION 32 +#define PHP_RELEASE_VERSION 34 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "7.3.32-dev" -#define PHP_VERSION_ID 70332 +#define PHP_VERSION "7.3.34-dev" +#define PHP_VERSION_ID 70334 From d9ff09a333df24c2187d3d9d29d60923d6978711 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 16 Nov 2021 14:31:26 +0100 Subject: [PATCH 07/32] Extract code for reporting a zend_fetch_class() error --- Zend/zend_execute_API.c | 49 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 7d870530b1522..ac0249c034a20 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1479,6 +1479,28 @@ void zend_unset_timeout(void) /* {{{ */ } /* }}} */ +static ZEND_COLD void report_class_fetch_error(zend_string *class_name, int fetch_type) +{ + if (fetch_type & ZEND_FETCH_CLASS_SILENT) { + return; + } + + if (EG(exception)) { + if (!(fetch_type & ZEND_FETCH_CLASS_EXCEPTION)) { + zend_exception_uncaught_error("During class fetch"); + } + return; + } + + if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_INTERFACE) { + zend_throw_or_error(fetch_type, NULL, "Interface \"%s\" not found", ZSTR_VAL(class_name)); + } else if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_TRAIT) { + zend_throw_or_error(fetch_type, NULL, "Trait \"%s\" not found", ZSTR_VAL(class_name)); + } else { + zend_throw_or_error(fetch_type, NULL, "Class \"%s\" not found", ZSTR_VAL(class_name)); + } +} + zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type) /* {{{ */ { zend_class_entry *ce, *scope; @@ -1520,15 +1542,7 @@ zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type) /* { ce = zend_lookup_class_ex(class_name, NULL, fetch_type); if (!ce) { - if (!(fetch_type & ZEND_FETCH_CLASS_SILENT) && !EG(exception)) { - if (fetch_sub_type == ZEND_FETCH_CLASS_INTERFACE) { - zend_throw_or_error(fetch_type, NULL, "Interface \"%s\" not found", ZSTR_VAL(class_name)); - } else if (fetch_sub_type == ZEND_FETCH_CLASS_TRAIT) { - zend_throw_or_error(fetch_type, NULL, "Trait \"%s\" not found", ZSTR_VAL(class_name)); - } else { - zend_throw_or_error(fetch_type, NULL, "Class \"%s\" not found", ZSTR_VAL(class_name)); - } - } + report_class_fetch_error(class_name, fetch_type); return NULL; } return ce; @@ -1539,22 +1553,7 @@ zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string { zend_class_entry *ce = zend_lookup_class_ex(class_name, key, fetch_type); if (!ce) { - if (fetch_type & ZEND_FETCH_CLASS_SILENT) { - return NULL; - } - if (EG(exception)) { - if (!(fetch_type & ZEND_FETCH_CLASS_EXCEPTION)) { - zend_exception_uncaught_error("During class fetch"); - } - return NULL; - } - if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_INTERFACE) { - zend_throw_or_error(fetch_type, NULL, "Interface \"%s\" not found", ZSTR_VAL(class_name)); - } else if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_TRAIT) { - zend_throw_or_error(fetch_type, NULL, "Trait \"%s\" not found", ZSTR_VAL(class_name)); - } else { - zend_throw_or_error(fetch_type, NULL, "Class \"%s\" not found", ZSTR_VAL(class_name)); - } + report_class_fetch_error(class_name, fetch_type); return NULL; } return ce; From 812df2bd8a95c7e07879924b023e14c2dbe1c24f Mon Sep 17 00:00:00 2001 From: Cameron Porter Date: Fri, 12 Nov 2021 16:31:40 -0600 Subject: [PATCH 08/32] Fix bug #81611 Add zend_fetch_class_with_scope() which accepts a scope to use for self/parent, and use that during constant expression evaluation. Closes GH-7649. --- NEWS | 4 ++ Zend/zend_ast.c | 2 +- Zend/zend_execute.h | 1 + Zend/zend_execute_API.c | 33 +++++++++++++++ ext/reflection/tests/bug81611.phpt | 64 ++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 ext/reflection/tests/bug81611.phpt diff --git a/NEWS b/NEWS index 9e76302a3dded..65d7d24ca40f8 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,10 @@ PHP NEWS . Fixed bug #81513 (Future possibility for heap overflow in FPM zlog). (Jakub Zelenka) +- Reflection: + . Fixed bug #81611 (ArgumentCountError when getting default value from + ReflectionParameter with new). (Cameron Porter) + - XML: . Fixed bug #79971 (special character is breaking the path in xml function). (CVE-2021-21707) (cmb) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index a064927f93a87..0d69bfbaca369 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -482,7 +482,7 @@ static zend_result zend_ast_add_unpacked_element(zval *result, zval *expr) { zend_class_entry *zend_ast_fetch_class(zend_ast *ast, zend_class_entry *scope) { - return zend_fetch_class(zend_ast_get_str(ast), ast->attr | ZEND_FETCH_CLASS_EXCEPTION); + return zend_fetch_class_with_scope(zend_ast_get_str(ast), ast->attr | ZEND_FETCH_CLASS_EXCEPTION, scope); } ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope) diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 278afc9c10fa3..d13087a5b0264 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -349,6 +349,7 @@ ZEND_API void zend_set_timeout(zend_long seconds, bool reset_signals); ZEND_API void zend_unset_timeout(void); ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void); ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type); +ZEND_API zend_class_entry *zend_fetch_class_with_scope(zend_string *class_name, int fetch_type, zend_class_entry *scope); ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, int fetch_type); ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index ac0249c034a20..7d6144e8e4fe4 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1549,6 +1549,39 @@ zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type) /* { } /* }}} */ +zend_class_entry *zend_fetch_class_with_scope( + zend_string *class_name, int fetch_type, zend_class_entry *scope) +{ + zend_class_entry *ce; + switch (fetch_type & ZEND_FETCH_CLASS_MASK) { + case ZEND_FETCH_CLASS_SELF: + if (UNEXPECTED(!scope)) { + zend_throw_or_error(fetch_type, NULL, "Cannot access \"self\" when no class scope is active"); + } + return scope; + case ZEND_FETCH_CLASS_PARENT: + if (UNEXPECTED(!scope)) { + zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when no class scope is active"); + return NULL; + } + if (UNEXPECTED(!scope->parent)) { + zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when current class scope has no parent"); + } + return scope->parent; + case 0: + break; + /* Other fetch types are not supported by this function. */ + EMPTY_SWITCH_DEFAULT_CASE() + } + + ce = zend_lookup_class_ex(class_name, NULL, fetch_type); + if (!ce) { + report_class_fetch_error(class_name, fetch_type); + return NULL; + } + return ce; +} + zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *key, int fetch_type) /* {{{ */ { zend_class_entry *ce = zend_lookup_class_ex(class_name, key, fetch_type); diff --git a/ext/reflection/tests/bug81611.phpt b/ext/reflection/tests/bug81611.phpt new file mode 100644 index 0000000000000..94bebf359cbef --- /dev/null +++ b/ext/reflection/tests/bug81611.phpt @@ -0,0 +1,64 @@ +--TEST-- +Reflection Bug #81611 (ArgumentCountError when getting default value from ReflectionParameter with new) +--FILE-- +getMethod($method)->getParameters(); + + foreach ($params as $param) { + echo "isDefaultValueAvailable:\n"; + var_dump($param->isDefaultValueAvailable()); + + echo "isDefaultValueConstant:\n"; + var_dump($param->isDefaultValueConstant()); + + echo "getDefaultValueConstantName:\n"; + var_dump($param->getDefaultValueConstantName()); + + echo "getDefaultValue:\n"; + var_dump($param->getDefaultValue()); + + echo "\n"; + } +} +?> +--EXPECT-- +isDefaultValueAvailable: +bool(true) +isDefaultValueConstant: +bool(false) +getDefaultValueConstantName: +NULL +getDefaultValue: +object(Foo)#2 (0) { +} + +isDefaultValueAvailable: +bool(true) +isDefaultValueConstant: +bool(false) +getDefaultValueConstantName: +NULL +getDefaultValue: +object(Bar)#3 (0) { +} From c02aa461268843270a6897c20ef3fb4b00d9de8a Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 16 Nov 2021 16:14:29 +0100 Subject: [PATCH 09/32] Fix leak when persistent PDO connection fails As we don't register the resource, the resource dtor is not called and will not decrement the refcount. --- ext/pdo/pdo_dbh.c | 4 ++++ .../tests/construct_persistent_failure.phpt | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 ext/pdo_mysql/tests/construct_persistent_failure.phpt diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index d53d2ca63c0d1..5d3efe12e55f7 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -419,6 +419,10 @@ PHP_METHOD(PDO, __construct) } /* the connection failed; things will tidy up in free_storage */ + if (is_persistent) { + dbh->refcount--; + } + /* XXX raise exception */ zend_restore_error_handling(&zeh); if (!EG(exception)) { diff --git a/ext/pdo_mysql/tests/construct_persistent_failure.phpt b/ext/pdo_mysql/tests/construct_persistent_failure.phpt new file mode 100644 index 0000000000000..65c3b65329eed --- /dev/null +++ b/ext/pdo_mysql/tests/construct_persistent_failure.phpt @@ -0,0 +1,16 @@ +--TEST-- +Failure when creating persistent connection +--EXTENSIONS-- +pdo_mysql +--FILE-- + true, + ]); +} catch (PDOException $e) { + echo "Caught\n"; +} +?> +--EXPECT-- +Caught From fb3e646f6b1620ead9630ad6b5b9945bac783df9 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 16 Nov 2021 15:35:15 +0000 Subject: [PATCH 10/32] OpenBSD ZTS build fix Closes GH-7661. --- TSRM/TSRM.c | 4 ++-- ext/opcache/jit/zend_jit_x86.dasc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 2d60b6a9d6eee..fc6c932065b0e 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -729,13 +729,13 @@ TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void) #if defined(__APPLE__) && defined(__x86_64__) // TODO: Implement support for fast JIT ZTS code ??? return 0; -#elif defined(__x86_64__) && defined(__GNUC__) && !defined(__FreeBSD__) +#elif defined(__x86_64__) && defined(__GNUC__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) size_t ret; asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0" : "=r" (ret)); return ret; -#elif defined(__i386__) && defined(__GNUC__) && !defined(__FreeBSD__) +#elif defined(__i386__) && defined(__GNUC__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) size_t ret; asm ("leal _tsrm_ls_cache@ntpoff,%0" diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 52e2388ca37e9..fa7edc9279a7b 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -2968,7 +2968,7 @@ static int zend_jit_setup(void) # elif defined(__GNUC__) && defined(__x86_64__) tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset(); if (tsrm_ls_cache_tcb_offset == 0) { -#if defined(__has_attribute) && __has_attribute(tls_model) && !defined(__FreeBSD__) +#if defined(__has_attribute) && __has_attribute(tls_model) && !defined(__FreeBSD__) && !defined(__OpenBSD__) size_t ret; asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0" @@ -2987,7 +2987,7 @@ static int zend_jit_setup(void) # elif defined(__GNUC__) && defined(__i386__) tsrm_ls_cache_tcb_offset = tsrm_get_ls_cache_tcb_offset(); if (tsrm_ls_cache_tcb_offset == 0) { -#if !defined(__FreeBSD__) +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) size_t ret; asm ("leal _tsrm_ls_cache@ntpoff,%0\n" From 48a65fef6fbbce5e941ca6becb042bd1d9e2a8af Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 17 Nov 2021 12:48:56 +0300 Subject: [PATCH 11/32] JIT: Fixed memory lieak --- ext/opcache/jit/zend_jit_x86.dasc | 78 ++++++++++++++++++------------ ext/opcache/tests/jit/mod_004.phpt | 21 ++++++++ 2 files changed, 67 insertions(+), 32 deletions(-) create mode 100644 ext/opcache/tests/jit/mod_004.phpt diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index fa7edc9279a7b..c11937b566469 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -559,10 +559,9 @@ static void* dasm_labels[zend_lb_MAX]; |.endmacro |.macro UNDEF_OPLINE_RESULT_IF_USED -| mov r0, EX->opline -| test byte OP:r0->result_type, (IS_TMP_VAR|IS_VAR) +| test byte OP:RX->result_type, (IS_TMP_VAR|IS_VAR) | jz >1 -| mov eax, dword OP:r0->result.var +| mov eax, dword OP:RX->result.var | SET_Z_TYPE_INFO FP + r0, IS_UNDEF |1: |.endmacro @@ -1841,6 +1840,42 @@ static int zend_jit_exception_handler_undef_stub(dasm_State **Dst) return 1; } + +static int zend_jit_exception_handler_free_op1_op2_stub(dasm_State **Dst) +{ + |->exception_handler_free_op1_op2: + | UNDEF_OPLINE_RESULT_IF_USED + | test byte OP:RX->op1_type, (IS_TMP_VAR|IS_VAR) + | je >9 + | mov eax, dword OP:RX->op1.var + | add r0, FP + | ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL + |9: + | test byte OP:RX->op2_type, (IS_TMP_VAR|IS_VAR) + | je >9 + | mov eax, dword OP:RX->op2.var + | add r0, FP + | ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL + |9: + | jmp ->exception_handler + return 1; +} + +static int zend_jit_exception_handler_free_op2_stub(dasm_State **Dst) +{ + |->exception_handler_free_op2: + | MEM_OP2_2_ZTS mov, RX, aword, executor_globals, opline_before_exception, r0 + | UNDEF_OPLINE_RESULT_IF_USED + | test byte OP:RX->op2_type, (IS_TMP_VAR|IS_VAR) + | je >9 + | mov eax, dword OP:RX->op2.var + | add r0, FP + | ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL + |9: + | jmp ->exception_handler + return 1; +} + static int zend_jit_leave_function_stub(dasm_State **Dst) { |->leave_function_handler: @@ -2154,7 +2189,6 @@ static int zend_jit_negative_shift_stub(dasm_State **Dst) { |->negative_shift: | mov RX, EX->opline - | UNDEF_OPLINE_RESULT_IF_USED |.if X64 |.if WIN | LOAD_ADDR CARG1, &zend_ce_arithmetic_error @@ -2176,19 +2210,7 @@ static int zend_jit_negative_shift_stub(dasm_State **Dst) | EXT_CALL zend_throw_error, r0 | add r4, 16 |.endif - | test byte OP:RX->op1_type, (IS_TMP_VAR|IS_VAR) - | je >9 - | mov eax, dword OP:RX->op1.var - | add r0, FP - | ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL - |9: - | test byte OP:RX->op2_type, (IS_TMP_VAR|IS_VAR) - | je >9 - | mov eax, dword OP:RX->op2.var - | add r0, FP - | ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL - |9: - | jmp ->exception_handler + | jmp ->exception_handler_free_op1_op2 return 1; } @@ -2196,7 +2218,6 @@ static int zend_jit_mod_by_zero_stub(dasm_State **Dst) { |->mod_by_zero: | mov RX, EX->opline - | UNDEF_OPLINE_RESULT_IF_USED |.if X64 |.if WIN | LOAD_ADDR CARG1, &zend_ce_division_by_zero_error @@ -2218,19 +2239,7 @@ static int zend_jit_mod_by_zero_stub(dasm_State **Dst) | EXT_CALL zend_throw_error, r0 | add r4, 16 |.endif - | test byte OP:RX->op1_type, (IS_TMP_VAR|IS_VAR) - | je >9 - | mov eax, dword OP:RX->op1.var - | add r0, FP - | ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL - |9: - | test byte OP:RX->op2_type, (IS_TMP_VAR|IS_VAR) - | je >9 - | mov eax, dword OP:RX->op2.var - | add r0, FP - | ZVAL_PTR_DTOR ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0), MAY_BE_ANY|MAY_BE_RC1|MAY_BE_RCN|MAY_BE_REF, 0, 0, NULL - |9: - | jmp ->exception_handler + | jmp ->exception_handler_free_op1_op2 return 1; } @@ -2852,6 +2861,8 @@ static const zend_jit_stub zend_jit_stubs[] = { JIT_STUB(interrupt_handler), JIT_STUB(exception_handler), JIT_STUB(exception_handler_undef), + JIT_STUB(exception_handler_free_op1_op2), + JIT_STUB(exception_handler_free_op2), JIT_STUB(leave_function), JIT_STUB(leave_throw), JIT_STUB(icall_throw), @@ -5242,7 +5253,10 @@ static int zend_jit_long_math_helper(dasm_State **Dst, | FREE_OP op1_type, op1, op1_info, 0, opline | FREE_OP op2_type, op2, op2_info, 0, opline if (may_throw) { - if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) { + if (opline->opcode == ZEND_ASSIGN_DIM_OP && (opline->op2_type & (IS_VAR|IS_TMP_VAR))) { + | MEM_OP2_1_ZTS cmp, aword, executor_globals, exception, 0, r0 + | jne ->exception_handler_free_op2 + } else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_RX) { zend_jit_check_exception_undef_result(Dst, opline); } else { zend_jit_check_exception(Dst); diff --git a/ext/opcache/tests/jit/mod_004.phpt b/ext/opcache/tests/jit/mod_004.phpt new file mode 100644 index 0000000000000..a80a487a9799c --- /dev/null +++ b/ext/opcache/tests/jit/mod_004.phpt @@ -0,0 +1,21 @@ +--TEST-- +JIT MOD: 004 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +opcache.protect_memory=1 +--FILE-- + +--EXPECTF-- +Warning: Undefined array key "xy" in %smod_004.php on line 4 + +Fatal error: Uncaught DivisionByZeroError: Modulo by zero in %smod_004.php:4 +Stack trace: +#0 {main} + thrown in %smod_004.php on line 4 \ No newline at end of file From 20fb26e55ca4eb0ecfbb88bd25f01081bbf68819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 30 Aug 2021 23:47:58 +0200 Subject: [PATCH 12/32] Add more specific array return type hints for various extensions - part 2 ext/ftp, ext/gmp, ext/intl Closes GH-7433 --- Zend/Optimizer/zend_func_infos.h | 15 ++++++++++ ext/ftp/ftp.stub.php | 21 +++++++++++++ ext/ftp/ftp_arginfo.h | 2 +- ext/gmp/gmp.stub.php | 16 ++++++++++ ext/gmp/gmp_arginfo.h | 2 +- ext/intl/calendar/calendar.stub.php | 1 + ext/intl/calendar/calendar_arginfo.h | 2 +- ext/intl/converter/converter.stub.php | 10 +++++-- ext/intl/converter/converter_arginfo.h | 2 +- ext/intl/dateformat/dateformat.stub.php | 1 + ext/intl/dateformat/dateformat_arginfo.h | 2 +- ext/intl/locale/locale.stub.php | 1 + ext/intl/locale/locale_arginfo.h | 2 +- ext/intl/msgformat/msgformat.stub.php | 2 ++ ext/intl/msgformat/msgformat_arginfo.h | 2 +- ext/intl/php_intl.stub.php | 30 ++++++++++++++++++- ext/intl/php_intl_arginfo.h | 2 +- .../resourcebundle/resourcebundle.stub.php | 1 + .../resourcebundle/resourcebundle_arginfo.h | 2 +- .../transliterator/transliterator.stub.php | 1 + .../transliterator/transliterator_arginfo.h | 2 +- 21 files changed, 106 insertions(+), 13 deletions(-) diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index 2f2020fb3ed2a..0e18481f40738 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -95,6 +95,10 @@ static const func_info_t func_infos[] = { F1("filter_input_array", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_FALSE|MAY_BE_NULL), F1("filter_var_array", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF|MAY_BE_FALSE|MAY_BE_NULL), F1("filter_list", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), + F1("ftp_raw", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_NULL), + F1("ftp_nlist", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE), + F1("ftp_rawlist", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE), + F1("ftp_mlsd", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_FALSE), F1("gd_info", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_FALSE|MAY_BE_ARRAY_OF_TRUE), F1("imagecreatetruecolor", MAY_BE_OBJECT|MAY_BE_FALSE), #if defined(PHP_WIN32) @@ -163,6 +167,10 @@ static const func_info_t func_infos[] = { #if defined(HAVE_BIND_TEXTDOMAIN_CODESET) F1("bind_textdomain_codeset", MAY_BE_STRING|MAY_BE_FALSE), #endif + F1("gmp_div_qr", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_OBJECT), + F1("gmp_sqrtrem", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_OBJECT), + F1("gmp_rootrem", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_OBJECT), + F1("gmp_gcdext", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_OBJECT), F1("hash", MAY_BE_STRING|MAY_BE_FALSE), F1("hash_file", MAY_BE_STRING|MAY_BE_FALSE), F1("hash_hmac", MAY_BE_STRING|MAY_BE_FALSE), @@ -189,6 +197,13 @@ static const func_info_t func_infos[] = { F1("iconv_mime_decode_headers", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_FALSE), F1("iconv", MAY_BE_STRING|MAY_BE_FALSE), F1("iconv_get_encoding", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_STRING|MAY_BE_STRING|MAY_BE_FALSE), + F1("intlcal_get_available_locales", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), + F1("datefmt_localtime", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_FALSE), + F1("locale_get_keywords", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE|MAY_BE_NULL), + F1("msgfmt_parse", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE), + F1("msgfmt_parse_message", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_DOUBLE|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE), + F1("resourcebundle_locales", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE), + F1("transliterator_list_ids", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_FALSE), F1("json_encode", MAY_BE_STRING|MAY_BE_FALSE), F1("json_last_error_msg", MAY_BE_STRING), F1("ldap_get_entries", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_FALSE), diff --git a/ext/ftp/ftp.stub.php b/ext/ftp/ftp.stub.php index 6693641e2b497..13708c6be6d96 100644 --- a/ext/ftp/ftp.stub.php +++ b/ext/ftp/ftp.stub.php @@ -27,6 +27,11 @@ function ftp_pwd(FTP\Connection $ftp): string|false {} function ftp_cdup(FTP\Connection $ftp): bool {} function ftp_chdir(FTP\Connection $ftp, string $directory): bool {} function ftp_exec(FTP\Connection $ftp, string $command): bool {} + + /** + * @return array|null + * @refcount 1 + */ function ftp_raw(FTP\Connection $ftp, string $command): ?array {} function ftp_mkdir(FTP\Connection $ftp, string $directory): string|false {} function ftp_rmdir(FTP\Connection $ftp, string $directory): bool {} @@ -34,9 +39,25 @@ function ftp_chmod(FTP\Connection $ftp, int $permissions, string $filename): int /** @param string $response */ function ftp_alloc(FTP\Connection $ftp, int $size, &$response = null): bool {} + + /** + * @return array|false + * @refcount 1 + */ function ftp_nlist(FTP\Connection $ftp, string $directory): array|false {} + + /** + * @return array|false + * @refcount 1 + */ function ftp_rawlist(FTP\Connection $ftp, string $directory, bool $recursive = false): array|false {} + + /** + * @return array|false + * @refcount 1 + */ function ftp_mlsd(FTP\Connection $ftp, string $directory): array|false {} + function ftp_systype(FTP\Connection $ftp): string|false {} /** @param resource $stream */ diff --git a/ext/ftp/ftp_arginfo.h b/ext/ftp/ftp_arginfo.h index 92c580ca28bb7..654f2dae450a7 100644 --- a/ext/ftp/ftp_arginfo.h +++ b/ext/ftp/ftp_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6de0997e9f07a8e1079764ab1841d3ac4651cc53 */ + * Stub hash: 2b1726dd5652839a37e533e20dfcf6782b3c766d */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ftp_connect, 0, 1, FTP\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, hostname, IS_STRING, 0) diff --git a/ext/gmp/gmp.stub.php b/ext/gmp/gmp.stub.php index 06e96c8392691..89ad2a458375a 100644 --- a/ext/gmp/gmp.stub.php +++ b/ext/gmp/gmp.stub.php @@ -22,6 +22,10 @@ function gmp_sub(GMP|int|string $num1, GMP|int|string $num2): GMP {} function gmp_mul(GMP|int|string $num1, GMP|int|string $num2): GMP {} +/** + * @return array + * @refcount 1 + */ function gmp_div_qr(GMP|int|string $num1, GMP|int|string $num2, int $rounding_mode = GMP_ROUND_ZERO): array {} function gmp_div_q(GMP|int|string $num1, GMP|int|string $num2, int $rounding_mode = GMP_ROUND_ZERO): GMP {} @@ -43,10 +47,18 @@ function gmp_fact(GMP|int|string $num): GMP {} function gmp_sqrt(GMP|int|string $num): GMP {} +/** + * @return array + * @refcount 1 + */ function gmp_sqrtrem(GMP|int|string $num): array {} function gmp_root(GMP|int|string $num, int $nth): GMP {} +/** + * @return array + * @refcount 1 + */ function gmp_rootrem(GMP|int|string $num, int $nth): array {} function gmp_pow(GMP|int|string $num, int $exponent): GMP {} @@ -61,6 +73,10 @@ function gmp_prob_prime(GMP|int|string $num, int $repetitions = 10): int {} function gmp_gcd(GMP|int|string $num1, GMP|int|string $num2): GMP {} +/** + * @return array + * @refcount 1 + */ function gmp_gcdext(GMP|int|string $num1, GMP|int|string $num2): array {} function gmp_lcm(GMP|int|string $num1, GMP|int|string $num2): GMP {} diff --git a/ext/gmp/gmp_arginfo.h b/ext/gmp/gmp_arginfo.h index 2bb36745457ed..fb0a02175925b 100644 --- a/ext/gmp/gmp_arginfo.h +++ b/ext/gmp/gmp_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 69215fb94adb4156c974360e5698e842471cb27d */ + * Stub hash: 05e618ec428bd8769410153469c42cbc923ea77f */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_init, 0, 1, GMP, 0) ZEND_ARG_TYPE_MASK(0, num, MAY_BE_LONG|MAY_BE_STRING, NULL) diff --git a/ext/intl/calendar/calendar.stub.php b/ext/intl/calendar/calendar.stub.php index ed491cb2c4e20..709d0e3667c7f 100644 --- a/ext/intl/calendar/calendar.stub.php +++ b/ext/intl/calendar/calendar.stub.php @@ -74,6 +74,7 @@ public function getActualMaximum(int $field): int|false {} public function getActualMinimum(int $field): int|false {} /** + * @return array * @tentative-return-type * @alias intlcal_get_available_locales */ diff --git a/ext/intl/calendar/calendar_arginfo.h b/ext/intl/calendar/calendar_arginfo.h index 6f3733372f90e..7be45231f9c90 100644 --- a/ext/intl/calendar/calendar_arginfo.h +++ b/ext/intl/calendar/calendar_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2265c2a4f478d6ccd576ce09a19a158df38a2bdb */ + * Stub hash: 7be0e49d2b898587c4bbefaaf613932ae4786c52 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IntlCalendar___construct, 0, 0, 0) ZEND_END_ARG_INFO() diff --git a/ext/intl/converter/converter.stub.php b/ext/intl/converter/converter.stub.php index 7e2b207e44e79..9e07341778b20 100644 --- a/ext/intl/converter/converter.stub.php +++ b/ext/intl/converter/converter.stub.php @@ -15,10 +15,16 @@ public function convert(string $str, bool $reverse = false): string|false {} */ public function fromUCallback(int $reason, array $source, int $codePoint, &$error): string|int|array|null {} - /** @tentative-return-type */ + /** + * @return array|false|null + * @tentative-return-type + */ public static function getAliases(string $name): array|false|null {} - /** @tentative-return-type */ + /** + * @return array + * @tentative-return-type + */ public static function getAvailable(): array {} /** @tentative-return-type */ diff --git a/ext/intl/converter/converter_arginfo.h b/ext/intl/converter/converter_arginfo.h index 663c68271a5ae..fa88dd2ab630e 100644 --- a/ext/intl/converter/converter_arginfo.h +++ b/ext/intl/converter/converter_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a6b352ba1b6ad2367bd4705ac941e763f74c8fac */ + * Stub hash: 2a6d8499e1a2d414130e366783a1c084f47a3293 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_UConverter___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, destination_encoding, IS_STRING, 1, "null") diff --git a/ext/intl/dateformat/dateformat.stub.php b/ext/intl/dateformat/dateformat.stub.php index e00e654b131c2..11c0d05dc41e6 100644 --- a/ext/intl/dateformat/dateformat.stub.php +++ b/ext/intl/dateformat/dateformat.stub.php @@ -134,6 +134,7 @@ public function parse(string $string, &$offset = null): int|float|false {} /** * @param int $offset + * @return array|false * @tentative-return-type * @alias datefmt_localtime */ diff --git a/ext/intl/dateformat/dateformat_arginfo.h b/ext/intl/dateformat/dateformat_arginfo.h index 2ec4da71d29e0..ccad07cd60a9d 100644 --- a/ext/intl/dateformat/dateformat_arginfo.h +++ b/ext/intl/dateformat/dateformat_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 767e5d064aef6d68f860a79c721eb728436c4eb9 */ + * Stub hash: 82f90e7b0528b2b3515c086763dba4de0f92dfa7 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IntlDateFormatter___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, locale, IS_STRING, 1) diff --git a/ext/intl/locale/locale.stub.php b/ext/intl/locale/locale.stub.php index 144cdbc12e044..e5a4194a261ac 100644 --- a/ext/intl/locale/locale.stub.php +++ b/ext/intl/locale/locale.stub.php @@ -35,6 +35,7 @@ public static function getScript(string $locale): ?string {} public static function getRegion(string $locale): ?string {} /** + * @return array|false|null * @tentative-return-type * @alias locale_get_keywords */ diff --git a/ext/intl/locale/locale_arginfo.h b/ext/intl/locale/locale_arginfo.h index 57544eba306be..679a4ca9d9034 100644 --- a/ext/intl/locale/locale_arginfo.h +++ b/ext/intl/locale/locale_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a0c2cf74b0d2e8309f0cd39fd9969d14586d0988 */ + * Stub hash: 539e559bc038e18358540b3b3f4db7b09e532dae */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Locale_getDefault, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() diff --git a/ext/intl/msgformat/msgformat.stub.php b/ext/intl/msgformat/msgformat.stub.php index efa1164fd8f71..c85a9b92b3a32 100644 --- a/ext/intl/msgformat/msgformat.stub.php +++ b/ext/intl/msgformat/msgformat.stub.php @@ -25,12 +25,14 @@ public function format(array $values): string|false {} public static function formatMessage(string $locale, string $pattern, array $values): string|false {} /** + * @return array|false * @tentative-return-type * @alias msgfmt_parse */ public function parse(string $string): array|false {} /** + * @return array|false * @tentative-return-type * @alias msgfmt_parse_message */ diff --git a/ext/intl/msgformat/msgformat_arginfo.h b/ext/intl/msgformat/msgformat_arginfo.h index 90eeab215300c..8f0456f6cbb05 100644 --- a/ext/intl/msgformat/msgformat_arginfo.h +++ b/ext/intl/msgformat/msgformat_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3fc29ffe87caf85a84e97470d0b59a26c801457c */ + * Stub hash: 44bc7b87c0b6c674bf94764b3f036006e3933713 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_MessageFormatter___construct, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, locale, IS_STRING, 0) diff --git a/ext/intl/php_intl.stub.php b/ext/intl/php_intl.stub.php index ae8657eb61a5c..7ed23ee3946f2 100644 --- a/ext/intl/php_intl.stub.php +++ b/ext/intl/php_intl.stub.php @@ -15,6 +15,10 @@ function intlcal_get_keyword_values_for_locale(string $keyword, string $locale, function intlcal_get_now(): float {} +/** + * @return array + * @refcount 1 + */ function intlcal_get_available_locales(): array {} function intlcal_get(IntlCalendar $calendar, int $field): int|false {} @@ -206,7 +210,11 @@ function datefmt_format_object($datetime, $format = null, ?string $locale = null /** @param int $offset */ function datefmt_parse(IntlDateFormatter $formatter, string $string, &$offset = null): int|float|false {} -/** @param int $offset */ +/** + * @param int $offset + * @return array|false + * @refcount 1 + */ function datefmt_localtime(IntlDateFormatter $formatter, string $string, &$offset = null): array|false {} function datefmt_get_error_code(IntlDateFormatter $formatter): int {} @@ -294,6 +302,10 @@ function locale_get_script(string $locale): ?string {} function locale_get_region(string $locale): ?string {} +/** + * @return array|false|null + * @refcount 1 + */ function locale_get_keywords(string $locale): array|false|null {} function locale_get_display_script(string $locale, ?string $displayLocale = null): string|false {} @@ -328,8 +340,16 @@ function msgfmt_format(MessageFormatter $formatter, array $values): string|false function msgfmt_format_message(string $locale, string $pattern, array $values): string|false {} +/** + * @return array|false + * @refcount 1 + */ function msgfmt_parse(MessageFormatter $formatter, string $string): array|false {} +/** + * @return array|false + * @refcount 1 + */ function msgfmt_parse_message(string $locale, string $pattern, string $message): array|false {} function msgfmt_set_pattern(MessageFormatter $formatter, string $pattern): bool {} @@ -361,6 +381,10 @@ function resourcebundle_get(ResourceBundle $bundle, $index, bool $fallback = tru function resourcebundle_count(ResourceBundle $bundle): int {} +/** + * @return array|false + * @refcount 1 + */ function resourcebundle_locales(string $bundle): array|false {} function resourcebundle_get_error_code(ResourceBundle $bundle): int {} @@ -431,6 +455,10 @@ function transliterator_create(string $id, int $direction = Transliterator::FORW function transliterator_create_from_rules(string $rules, int $direction = Transliterator::FORWARD): ?Transliterator {} +/** + * @return array|false + * @refcount 1 + */ function transliterator_list_ids(): array|false {} function transliterator_create_inverse(Transliterator $transliterator): ?Transliterator {} diff --git a/ext/intl/php_intl_arginfo.h b/ext/intl/php_intl_arginfo.h index d3df7f8b31b78..443341941ac75 100644 --- a/ext/intl/php_intl_arginfo.h +++ b/ext/intl/php_intl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: dc9632b2417200deb39cc5cce25aa26a44128707 */ + * Stub hash: 09aa0aa66c78b86c0e6e0e554c3ebe205a0e5f59 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_intlcal_create_instance, 0, 0, IntlCalendar, 1) ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, timezone, "null") diff --git a/ext/intl/resourcebundle/resourcebundle.stub.php b/ext/intl/resourcebundle/resourcebundle.stub.php index 79374e2951241..4247fca318a4e 100644 --- a/ext/intl/resourcebundle/resourcebundle.stub.php +++ b/ext/intl/resourcebundle/resourcebundle.stub.php @@ -26,6 +26,7 @@ public function get($index, bool $fallback = true): mixed {} public function count(): int {} /** + * @return array|false * @tentative-return-type * @alias resourcebundle_locales */ diff --git a/ext/intl/resourcebundle/resourcebundle_arginfo.h b/ext/intl/resourcebundle/resourcebundle_arginfo.h index 6d138768f3f72..0564b026dc39b 100644 --- a/ext/intl/resourcebundle/resourcebundle_arginfo.h +++ b/ext/intl/resourcebundle/resourcebundle_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a299d2a45a3575e6da71560979e2f9301b3c952f */ + * Stub hash: d27fa5a4dc092b94e48fc876070f440c247fa6c2 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ResourceBundle___construct, 0, 0, 2) ZEND_ARG_TYPE_INFO(0, locale, IS_STRING, 1) diff --git a/ext/intl/transliterator/transliterator.stub.php b/ext/intl/transliterator/transliterator.stub.php index 4ae7c3a645dd7..6d52263e0646f 100644 --- a/ext/intl/transliterator/transliterator.stub.php +++ b/ext/intl/transliterator/transliterator.stub.php @@ -27,6 +27,7 @@ public static function createFromRules(string $rules, int $direction = Translite public function createInverse(): ?Transliterator {} /** + * @return array|false * @tentative-return-type * @alias transliterator_list_ids */ diff --git a/ext/intl/transliterator/transliterator_arginfo.h b/ext/intl/transliterator/transliterator_arginfo.h index 76d10000b106f..c80be4f5b3460 100644 --- a/ext/intl/transliterator/transliterator_arginfo.h +++ b/ext/intl/transliterator/transliterator_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fbc9a196271c276e5af658115571cacde2ad3a3a */ + * Stub hash: 8a6aaab7dd89a014726bd1fdf1f40f7b6fa98ea5 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Transliterator___construct, 0, 0, 0) ZEND_END_ARG_INFO() From baac97081763cba0fa47d3d2e9ef66f55c89a073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Wed, 17 Nov 2021 10:55:56 +0100 Subject: [PATCH 13/32] Add the --generate-optimizer-info option to the help of gen_stub.php --- build/gen_stub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index ecbad1eb19440..5f74d26dbc37d 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3310,7 +3310,7 @@ function initPhpParser() { } if (isset($options["h"]) || isset($options["help"])) { - die("\nusage: gen_stub.php [ -f | --force-regeneration ] [ --generate-classsynopses ] [ --replace-classsynopses ] [ --generate-methodsynopses ] [ --replace-methodsynopses ] [ --parameter-stats ] [ --verify ] [ -h | --help ] [ name.stub.php | directory ] [ directory ]\n\n"); + die("\nusage: gen_stub.php [ -f | --force-regeneration ] [ --generate-classsynopses ] [ --replace-classsynopses ] [ --generate-methodsynopses ] [ --replace-methodsynopses ] [ --parameter-stats ] [ --verify ] [ --generate-optimizer-info ] [ -h | --help ] [ name.stub.php | directory ] [ directory ]\n\n"); } $fileInfos = []; From fac78ee760d04414abae571a28751de61a1c5b8c Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 17 Nov 2021 13:59:35 +0300 Subject: [PATCH 14/32] JIT: Fixed memory leak --- ext/opcache/jit/zend_jit_helpers.c | 2 +- ext/opcache/jit/zend_jit_x86.dasc | 2 ++ ext/opcache/tests/jit/assign_op_006.phpt | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 ext/opcache/tests/jit/assign_op_006.phpt diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index e2f6fff0ced71..dc742a31e9a1c 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1369,7 +1369,7 @@ static void ZEND_FASTCALL zend_jit_fast_assign_concat_helper(zval *op1, zval *op do { if (Z_REFCOUNTED_P(op1)) { - if (GC_REFCOUNT(Z_STR_P(op1)) == 1) { + if (GC_REFCOUNT(Z_STR_P(op1)) == 1 && EXPECTED(Z_STR_P(op1) != Z_STR_P(op2))) { result_str = perealloc(Z_STR_P(op1), ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(result_len)), 0); ZSTR_LEN(result_str) = result_len; zend_string_forget_hash_val(result_str); diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index c11937b566469..122eb298e7e13 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -5323,6 +5323,8 @@ static int zend_jit_concat_helper(dasm_State **Dst, } | LOAD_ZVAL_ADDR FCARG2a, op2_addr | EXT_CALL zend_jit_fast_assign_concat_helper, r0 + /* concatination with itself may reduce refcount */ + op2_info |= MAY_BE_RC1; } else { if (Z_REG(res_addr) != ZREG_FCARG1a || Z_OFFSET(res_addr) != 0) { | LOAD_ZVAL_ADDR FCARG1a, res_addr diff --git a/ext/opcache/tests/jit/assign_op_006.phpt b/ext/opcache/tests/jit/assign_op_006.phpt new file mode 100644 index 0000000000000..8d4caa97094e5 --- /dev/null +++ b/ext/opcache/tests/jit/assign_op_006.phpt @@ -0,0 +1,19 @@ +--TEST-- +JIT ASSIGN_OP: 006 concationation with itself +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--FILE-- + +DONE +--EXPECT-- +DONE From 44e5d253000fcefa8b6e771683e2e5266326f8c7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 16 Nov 2021 14:15:01 +0100 Subject: [PATCH 15/32] Fix inheritance of class constants if mutable data used Class constants from parents should always be directly reused, rather than re-evaluated as a separate copy. Previously this used to happen automatically, as we'd just inherit the class constant entry from the parent class. With mutable data there may now be a separate copy of the constant, so we need to use that copy when updating constants. Otherwise we may evaluate the same constant multiple times. Closes GH-7658. --- ...ass_constant_inheritance_mutable_data.phpt | 35 +++++++++++++++++++ Zend/zend_API.c | 29 +++++++++++---- Zend/zend_opcode.c | 4 ++- 3 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 Zend/tests/class_constant_inheritance_mutable_data.phpt diff --git a/Zend/tests/class_constant_inheritance_mutable_data.phpt b/Zend/tests/class_constant_inheritance_mutable_data.phpt new file mode 100644 index 0000000000000..02bef7f02392e --- /dev/null +++ b/Zend/tests/class_constant_inheritance_mutable_data.phpt @@ -0,0 +1,35 @@ +--TEST-- +Class constant inheritance with mutable data +--FILE-- + +--EXPECTF-- +object(B)#1 (0) { +} +string(2) "XY" +string(4) "X2Y2" + +Deprecated: Implicit conversion from float 1.5 to int loses precision in %s on line %d +int(0) +int(0) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 69da32d7e18f5..0fdf1a02ce991 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1322,12 +1322,19 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_ zend_hash_extend(constants_table, zend_hash_num_elements(&class_type->constants_table), 0); ZEND_HASH_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) { - if (Z_TYPE(c->value) == IS_CONSTANT_AST) { - new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); - memcpy(new_c, c, sizeof(zend_class_constant)); - c = new_c; + if (c->ce == class_type) { + if (Z_TYPE(c->value) == IS_CONSTANT_AST) { + new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); + memcpy(new_c, c, sizeof(zend_class_constant)); + c = new_c; + } + Z_TRY_ADDREF(c->value); + } else { + if (Z_TYPE(c->value) == IS_CONSTANT_AST) { + c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(c->ce), key); + ZEND_ASSERT(c); + } } - Z_TRY_ADDREF(c->value); _zend_hash_append_ptr(constants_table, key, c); } ZEND_HASH_FOREACH_END(); @@ -1409,8 +1416,18 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / } else { constants_table = &class_type->constants_table; } - ZEND_HASH_FOREACH_PTR(constants_table, c) { + + zend_string *name; + ZEND_HASH_FOREACH_STR_KEY_VAL(constants_table, name, val) { + c = Z_PTR_P(val); if (Z_TYPE(c->value) == IS_CONSTANT_AST) { + if (c->ce != class_type) { + Z_PTR_P(val) = c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(c->ce), name); + if (Z_TYPE(c->value) != IS_CONSTANT_AST) { + continue; + } + } + val = &c->value; if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) { return FAILURE; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index dd515c9b30f85..9f97570fe2d8c 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -250,7 +250,9 @@ ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce) zend_class_constant *c; ZEND_HASH_FOREACH_PTR(constants_table, c) { - zval_ptr_dtor_nogc(&c->value); + if (c->ce == ce) { + zval_ptr_dtor_nogc(&c->value); + } } ZEND_HASH_FOREACH_END(); zend_hash_destroy(constants_table); mutable_data->constants_table = NULL; From d0ecc83ab5e250c4ddaf4b415c55e5b21957967e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 17 Nov 2021 15:47:29 +0100 Subject: [PATCH 16/32] Assert hash is known when we claim it is --- Zend/zend_hash.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 8ee292106a6b3..5f878b2154bad 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -646,6 +646,7 @@ static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zen if (known_hash) { h = ZSTR_H(key); + ZEND_ASSERT(h != 0 && "Hash must be known"); } else { h = zend_string_hash_val(key); } From 6641e3b8f42795bd5955f59112c459435131d5ba Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 17 Nov 2021 15:53:25 +0100 Subject: [PATCH 17/32] Fix bug #81630: Don't claim known hash in getTraitAliases() We don't intern this string, and this code is not particularly performance critical in the first place, so just drop the the assumption. --- NEWS | 2 ++ ext/reflection/php_reflection.c | 15 ++++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 65d7d24ca40f8..7b98ac4c13ca4 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,8 @@ PHP NEWS - Reflection: . Fixed bug #81611 (ArgumentCountError when getting default value from ReflectionParameter with new). (Cameron Porter) + . Fixed bug #81630 (PHP 8.1: ReflectionClass->getTraitAliases() crashes with + Internal error). (Nikita) - XML: . Fixed bug #79971 (special character is breaking the path in xml function). diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 11024b9aee8cd..2134d9aa72f2f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -5116,18 +5116,15 @@ ZEND_METHOD(ReflectionClass, getTraitAliases) if (!class_name) { uint32_t j = 0; - zval *zv; - zend_class_entry *trait; zend_string *lcname = zend_string_tolower(cur_ref->method_name); for (j = 0; j < ce->num_traits; j++) { - zv = zend_hash_find_known_hash(CG(class_table), ce->trait_names[j].lc_name); - if (zv) { - trait = Z_CE_P(zv); - if (zend_hash_exists(&trait->function_table, lcname)) { - class_name = trait->name; - break; - } + zend_class_entry *trait = + zend_hash_find_ptr(CG(class_table), ce->trait_names[j].lc_name); + ZEND_ASSERT(trait && "Trait must exist"); + if (zend_hash_exists(&trait->function_table, lcname)) { + class_name = trait->name; + break; } } zend_string_release_ex(lcname, 0); From 4d4fe7639f3c217d169155b3f4217f5fe12ae114 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 17 Nov 2021 16:06:25 +0100 Subject: [PATCH 18/32] Fixed bug #81631 We need to save the opline before fetching the operand, as it may throw an undef var warning. --- NEWS | 2 ++ Zend/tests/bug81631.phpt | 14 ++++++++++++++ Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 6 +++--- 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 Zend/tests/bug81631.phpt diff --git a/NEWS b/NEWS index bd411955dcda7..fc7389a214568 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,8 @@ PHP NEWS (Nikita) . Fixed bug #81626 (Error on use static:: in __сallStatic() wrapped to Closure::fromCallable()). (Nikita) + . Fixed bug #81631 (::class with dynamic class name may yield wrong line + number). (Nikita) - FPM: . Fixed bug #81513 (Future possibility for heap overflow in FPM zlog). diff --git a/Zend/tests/bug81631.phpt b/Zend/tests/bug81631.phpt new file mode 100644 index 0000000000000..191ea951274d2 --- /dev/null +++ b/Zend/tests/bug81631.phpt @@ -0,0 +1,14 @@ +--TEST-- +Bug #81631: ::class with dynamic class name may yield wrong line number +--FILE-- + +--EXPECTF-- +Warning: Undefined variable $b in %s on line 3 + +Fatal error: Uncaught TypeError: Cannot use "::class" on value of type null in %s:3 +Stack trace: +#0 {main} + thrown in %s on line 3 diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index cceea0ebb62c6..9b517a1a876ff 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8415,8 +8415,8 @@ ZEND_VM_HANDLER(157, ZEND_FETCH_CLASS_NAME, CV|TMPVAR|UNUSED|CLASS_FETCH, ANY) USE_OPLINE if (OP1_TYPE != IS_UNUSED) { - zval *op = GET_OP1_ZVAL_PTR(BP_VAR_R); SAVE_OPLINE(); + zval *op = GET_OP1_ZVAL_PTR(BP_VAR_R); if (UNEXPECTED(Z_TYPE_P(op) != IS_OBJECT)) { ZVAL_DEREF(op); if (Z_TYPE_P(op) != IS_OBJECT) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 7efe9b7a0cefe..1c2cbd008a1a1 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -14538,8 +14538,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_TMPVAR_H USE_OPLINE if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED) { - zval *op = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); SAVE_OPLINE(); + zval *op = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); if (UNEXPECTED(Z_TYPE_P(op) != IS_OBJECT)) { ZVAL_DEREF(op); if (Z_TYPE_P(op) != IS_OBJECT) { @@ -30855,8 +30855,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_H USE_OPLINE if (IS_UNUSED != IS_UNUSED) { - zval *op = NULL; SAVE_OPLINE(); + zval *op = NULL; if (UNEXPECTED(Z_TYPE_P(op) != IS_OBJECT)) { ZVAL_DEREF(op); if (Z_TYPE_P(op) != IS_OBJECT) { @@ -38400,8 +38400,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_CV_HANDL USE_OPLINE if (IS_CV != IS_UNUSED) { - zval *op = _get_zval_ptr_cv_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); SAVE_OPLINE(); + zval *op = _get_zval_ptr_cv_BP_VAR_R(opline->op1.var EXECUTE_DATA_CC); if (UNEXPECTED(Z_TYPE_P(op) != IS_OBJECT)) { ZVAL_DEREF(op); if (Z_TYPE_P(op) != IS_OBJECT) { From 76548e509346383468439c3bdce0c290eb1aa3af Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 17 Nov 2021 18:23:36 +0300 Subject: [PATCH 19/32] Fixed bug #81607 (CE_CACHE allocation with concurrent access) --- NEWS | 4 ++++ Zend/zend_execute_API.c | 16 ++++++++------ Zend/zend_inheritance.c | 6 ----- Zend/zend_types.h | 23 +++++++++++++++---- ext/opcache/ZendAccelerator.c | 8 +++++-- ext/opcache/tests/bug81607.inc | 2 ++ ext/opcache/tests/bug81607.phpt | 27 +++++++++++++++++++++++ ext/opcache/zend_accelerator_util_funcs.c | 2 +- ext/opcache/zend_persist.c | 2 +- 9 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 ext/opcache/tests/bug81607.inc create mode 100644 ext/opcache/tests/bug81607.phpt diff --git a/NEWS b/NEWS index 7b98ac4c13ca4..2eee1ea26d780 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.0 +- Core: + . Fixed bug #81607 (CE_CACHE allocation with concurrent access). (Nikita, + Dmitry) + - FPM: . Fixed bug #81513 (Future possibility for heap overflow in FPM zlog). (Jakub Zelenka) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 7d6144e8e4fe4..f2ace090c6724 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1033,10 +1033,12 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * zval *zv; zend_string *lc_name; zend_string *autoload_name; + uint32_t ce_cache = 0; - if (ZSTR_HAS_CE_CACHE(name)) { - ce = ZSTR_GET_CE_CACHE(name); - if (ce) { + if (ZSTR_HAS_CE_CACHE(name) && ZSTR_VALID_CE_CACHE(name)) { + ce_cache = GC_REFCOUNT(name); + ce = GET_CE_CACHE(ce_cache); + if (EXPECTED(ce)) { return ce; } } @@ -1077,9 +1079,9 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * } /* Don't populate CE_CACHE for mutable classes during compilation. * The class may be freed while persisting. */ - if (ZSTR_HAS_CE_CACHE(name) && + if (ce_cache && (!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) { - ZSTR_SET_CE_CACHE(name, ce); + SET_CE_CACHE(ce_cache, ce); } return ce; } @@ -1135,8 +1137,8 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * } if (ce) { ZEND_ASSERT(!CG(in_compilation)); - if (ZSTR_HAS_CE_CACHE(name)) { - ZSTR_SET_CE_CACHE(name, ce); + if (ce_cache) { + SET_CE_CACHE(ce_cache, ce); } } return ce; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index c3595a6cd5802..4903ef4b3828b 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2785,9 +2785,6 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string } zv = zend_hash_find_known_hash(CG(class_table), key); Z_CE_P(zv) = ret; - if (ZSTR_HAS_CE_CACHE(ret->name)) { - ZSTR_SET_CE_CACHE(ret->name, ret); - } return ret; } @@ -3012,9 +3009,6 @@ zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *pa if (UNEXPECTED(!register_early_bound_ce(delayed_early_binding, lcname, ret))) { return NULL; } - if (ZSTR_HAS_CE_CACHE(ret->name)) { - ZSTR_SET_CE_CACHE(ret->name, ret); - } return ret; } } else { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 99f6b997a4408..8f666ff773bb6 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -714,10 +714,25 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { /* Fast class cache */ #define ZSTR_HAS_CE_CACHE(s) (GC_FLAGS(s) & IS_STR_CLASS_NAME_MAP_PTR) -#define ZSTR_GET_CE_CACHE(s) \ - (*(zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(GC_REFCOUNT(s))) -#define ZSTR_SET_CE_CACHE(s, ce) do { \ - *((zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(GC_REFCOUNT(s))) = ce; \ +#define ZSTR_GET_CE_CACHE(s) ZSTR_GET_CE_CACHE_EX(s, 1) +#define ZSTR_SET_CE_CACHE(s, ce) ZSTR_SET_CE_CACHE_EX(s, ce, 1) + +#define ZSTR_VALID_CE_CACHE(s) EXPECTED((GC_REFCOUNT(s)-1)/sizeof(void *) < CG(map_ptr_last)) + +#define ZSTR_GET_CE_CACHE_EX(s, validate) \ + ((!(validate) || ZSTR_VALID_CE_CACHE(s)) ? GET_CE_CACHE(GC_REFCOUNT(s)) : NULL) + +#define ZSTR_SET_CE_CACHE_EX(s, ce, validate) do { \ + if (!(validate) || ZSTR_VALID_CE_CACHE(s)) { \ + SET_CE_CACHE(GC_REFCOUNT(s), ce); \ + } \ + } while (0) + +#define GET_CE_CACHE(ce_cache) \ + (*(zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) + +#define SET_CE_CACHE(ce_cache, ce) do { \ + *((zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) = ce; \ } while (0) /* Recursion protection macros must be used only for arrays and objects */ diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 2e5eabd6a0bf3..9cd389a2ba451 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2293,11 +2293,15 @@ static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce, entry = zend_accel_inheritance_cache_find(entry, ce, parent, traits_and_interfaces, &needs_autoload); if (entry) { if (!needs_autoload) { + replay_warnings(entry->num_warnings, entry->warnings); if (ZCSG(map_ptr_last) > CG(map_ptr_last)) { zend_map_ptr_extend(ZCSG(map_ptr_last)); } - replay_warnings(entry->num_warnings, entry->warnings); - return entry->ce; + ce = entry->ce; + if (ZSTR_HAS_CE_CACHE(ce->name)) { + ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0); + } + return ce; } for (i = 0; i < entry->dependencies_count; i++) { diff --git a/ext/opcache/tests/bug81607.inc b/ext/opcache/tests/bug81607.inc new file mode 100644 index 0000000000000..a50dceebf6ffa --- /dev/null +++ b/ext/opcache/tests/bug81607.inc @@ -0,0 +1,2 @@ + 0) { + pcntl_wait($status); + var_dump(new FooBar); +} else { + echo "pcntl_fork() failed\n"; +} + +?> +--EXPECTF-- +Fatal error: Uncaught Error: Class "FooBar" not found in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 35f13c018473a..4f1ea70ea1202 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -207,7 +207,7 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source) if ((ce->ce_flags & ZEND_ACC_LINKED) && ZSTR_HAS_CE_CACHE(ce->name) && ZSTR_VAL(p->key)[0]) { - ZSTR_SET_CE_CACHE(ce->name, ce); + ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0); } } } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index d2b62c30eb9b4..6ca8c46892ae0 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -869,7 +869,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) if (!(ce->ce_flags & ZEND_ACC_CACHED)) { if (ZSTR_HAS_CE_CACHE(ce->name)) { - ZSTR_SET_CE_CACHE(ce->name, NULL); + ZSTR_SET_CE_CACHE_EX(ce->name, NULL, 0); } zend_accel_store_interned_string(ce->name); if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS) From 9dd3e8be8affbfadede2e13fab64e15551f4f187 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 17 Nov 2021 18:43:39 +0300 Subject: [PATCH 20/32] JIT: Fixed memory leak in Zend/tests/concat_002.phpt introduced by fac78ee760d04414abae571a28751de61a1c5b8c --- ext/opcache/jit/zend_jit_helpers.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index dc742a31e9a1c..dafb648ea60e3 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1369,10 +1369,13 @@ static void ZEND_FASTCALL zend_jit_fast_assign_concat_helper(zval *op1, zval *op do { if (Z_REFCOUNTED_P(op1)) { - if (GC_REFCOUNT(Z_STR_P(op1)) == 1 && EXPECTED(Z_STR_P(op1) != Z_STR_P(op2))) { + if (GC_REFCOUNT(Z_STR_P(op1)) == 1) { result_str = perealloc(Z_STR_P(op1), ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(result_len)), 0); ZSTR_LEN(result_str) = result_len; zend_string_forget_hash_val(result_str); + if (UNEXPECTED(Z_STR_P(op1) == Z_STR_P(op2))) { + ZVAL_NEW_STR(op2, result_str); + } break; } GC_DELREF(Z_STR_P(op1)); From a8b9dbc632685bfb000615c2599f252b417a61b6 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 18 Nov 2021 00:10:28 +0100 Subject: [PATCH 21/32] Fix ZEND_HASH_REVERSE_FOREACH_PTR definition --- Zend/zend_hash.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index b7559257d27e5..e004439029f6e 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -1093,7 +1093,7 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, _ptr = Z_PTR_P(_z); #define ZEND_HASH_REVERSE_FOREACH_PTR(ht, _ptr) \ - _ZEND_HASH_REVERSE_FOREACH_VAL(ht, 0); \ + _ZEND_HASH_REVERSE_FOREACH_VAL(ht); \ _ptr = Z_PTR_P(_z); #define ZEND_HASH_FOREACH_NUM_KEY(ht, _h) \ From 279d6a4f28a8a73b1d1ab5c97241eb05bfd07f88 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 18 Nov 2021 09:33:24 +0100 Subject: [PATCH 22/32] Skip test under preloading In this case we produce additional warnings when preloading evaluates constants. Possibly we shouldn't be evaluating expressions with side-effects during preloading, but for now just skip this test. --- Zend/tests/class_constant_inheritance_mutable_data.phpt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Zend/tests/class_constant_inheritance_mutable_data.phpt b/Zend/tests/class_constant_inheritance_mutable_data.phpt index 02bef7f02392e..0accf79c1dcfb 100644 --- a/Zend/tests/class_constant_inheritance_mutable_data.phpt +++ b/Zend/tests/class_constant_inheritance_mutable_data.phpt @@ -1,5 +1,9 @@ --TEST-- Class constant inheritance with mutable data +--SKIPIF-- + --FILE-- Date: Thu, 18 Nov 2021 10:36:35 +0100 Subject: [PATCH 23/32] Clarify that preg_match_all() cannot return null --- ext/pcre/php_pcre.c | 2 +- ext/pcre/php_pcre.stub.php | 2 +- ext/pcre/php_pcre_arginfo.h | 10 ++-------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index 19567889d88bd..340e103857fed 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -1206,7 +1206,7 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str, if (subpats != NULL) { subpats = zend_try_array_init(subpats); if (!subpats) { - return; + RETURN_THROWS(); } } diff --git a/ext/pcre/php_pcre.stub.php b/ext/pcre/php_pcre.stub.php index b2862a2f62ee1..cf091f1dd600d 100644 --- a/ext/pcre/php_pcre.stub.php +++ b/ext/pcre/php_pcre.stub.php @@ -6,7 +6,7 @@ function preg_match(string $pattern, string $subject, &$matches = null, int $flags = 0, int $offset = 0): int|false {} /** @param array $matches */ -function preg_match_all(string $pattern, string $subject, &$matches = null, int $flags = 0, int $offset = 0): int|false|null {} +function preg_match_all(string $pattern, string $subject, &$matches = null, int $flags = 0, int $offset = 0): int|false {} /** @param int $count */ function preg_replace(string|array $pattern, string|array $replacement, string|array $subject, int $limit = -1, &$count = null): string|array|null {} diff --git a/ext/pcre/php_pcre_arginfo.h b/ext/pcre/php_pcre_arginfo.h index c53f35559e36b..1e58849c84b6c 100644 --- a/ext/pcre/php_pcre_arginfo.h +++ b/ext/pcre/php_pcre_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2e5a9edc9635edd4f5a00e9d888fb34c1746a5b8 */ + * Stub hash: 8b3d31a0fc1542bcb432f1200a7b9f49afbf0f1f */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_match, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0) @@ -9,13 +9,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_match, 0, 2, MAY_BE_LONG|MA ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_match_all, 0, 2, MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_NULL) - ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, subject, IS_STRING, 0) - ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, matches, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "0") -ZEND_END_ARG_INFO() +#define arginfo_preg_match_all arginfo_preg_match ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_preg_replace, 0, 3, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_NULL) ZEND_ARG_TYPE_MASK(0, pattern, MAY_BE_STRING|MAY_BE_ARRAY, NULL) From 17ceed9ae785a560631865af86038150d9785121 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 18 Nov 2021 11:11:52 +0100 Subject: [PATCH 24/32] Implement __serialize() and __unserialize() on GMP GMP directly implements internal serialize/unserialize handlers rather than going through the Serializable interface, so it ended up being missed when adding the new __serialize()/__unserialize() methods to other classes. The serialization format is similar to before, but uses hex instead of decimal encoding and omits the members if not used (which should be almost always). --- ext/gmp/gmp.c | 45 ++++++++++++++++++++++++++++++++++++ ext/gmp/gmp.stub.php | 3 +++ ext/gmp/gmp_arginfo.h | 13 ++++++++++- ext/gmp/tests/serialize.phpt | 32 +++++++++++++++++++++++-- 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index 315da803de661..c75e2788b869a 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -2017,3 +2017,48 @@ ZEND_FUNCTION(gmp_scan1) FREE_GMP_TEMP(temp_a); } /* }}} */ + +ZEND_METHOD(GMP, __serialize) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zval zv; + array_init(return_value); + + mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(ZEND_THIS); + gmp_strval(&zv, gmpnum, 16); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv); + + HashTable *props = Z_OBJ_P(ZEND_THIS)->properties; + if (props && zend_hash_num_elements(props) != 0) { + ZVAL_ARR(&zv, zend_proptable_to_symtable( + zend_std_get_properties(Z_OBJ_P(ZEND_THIS)), /* always duplicate */ 1)); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &zv); + } +} + +ZEND_METHOD(GMP, __unserialize) +{ + HashTable *data; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(data) + ZEND_PARSE_PARAMETERS_END(); + + zval *num = zend_hash_index_find(data, 0); + if (!num || Z_TYPE_P(num) != IS_STRING || + convert_to_gmp(GET_GMP_FROM_ZVAL(ZEND_THIS), num, 16, 0) == FAILURE) { + zend_throw_exception(NULL, "Could not unserialize number", 0); + RETURN_THROWS(); + } + + zval *props = zend_hash_index_find(data, 1); + if (props) { + if (Z_TYPE_P(props) != IS_ARRAY) { + zend_throw_exception(NULL, "Could not unserialize properties", 0); + RETURN_THROWS(); + } + + object_properties_load(Z_OBJ_P(ZEND_THIS), Z_ARRVAL_P(props)); + } +} diff --git a/ext/gmp/gmp.stub.php b/ext/gmp/gmp.stub.php index 89ad2a458375a..280fa6abd7db6 100644 --- a/ext/gmp/gmp.stub.php +++ b/ext/gmp/gmp.stub.php @@ -4,6 +4,9 @@ class GMP { + public function __serialize(): array {} + + public function __unserialize(array $data): void {} } function gmp_init(int|string $num, int $base = 0): GMP {} diff --git a/ext/gmp/gmp_arginfo.h b/ext/gmp/gmp_arginfo.h index fb0a02175925b..8091359df9df5 100644 --- a/ext/gmp/gmp_arginfo.h +++ b/ext/gmp/gmp_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 05e618ec428bd8769410153469c42cbc923ea77f */ + * Stub hash: be077a57bc9ddbb9100fadfb212857c0b8f21ebf */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_init, 0, 1, GMP, 0) ZEND_ARG_TYPE_MASK(0, num, MAY_BE_LONG|MAY_BE_STRING, NULL) @@ -184,6 +184,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_gmp_binomial, 0, 2, GMP, 0) ZEND_ARG_TYPE_INFO(0, k, IS_LONG, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_GMP___serialize, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_GMP___unserialize, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + ZEND_FUNCTION(gmp_init); ZEND_FUNCTION(gmp_import); @@ -235,6 +242,8 @@ ZEND_FUNCTION(gmp_popcount); ZEND_FUNCTION(gmp_hamdist); ZEND_FUNCTION(gmp_nextprime); ZEND_FUNCTION(gmp_binomial); +ZEND_METHOD(GMP, __serialize); +ZEND_METHOD(GMP, __unserialize); static const zend_function_entry ext_functions[] = { @@ -294,6 +303,8 @@ static const zend_function_entry ext_functions[] = { static const zend_function_entry class_GMP_methods[] = { + ZEND_ME(GMP, __serialize, arginfo_class_GMP___serialize, ZEND_ACC_PUBLIC) + ZEND_ME(GMP, __unserialize, arginfo_class_GMP___unserialize, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/gmp/tests/serialize.phpt b/ext/gmp/tests/serialize.phpt index e54530107ea20..4b42db7606d6a 100644 --- a/ext/gmp/tests/serialize.phpt +++ b/ext/gmp/tests/serialize.phpt @@ -11,7 +11,10 @@ var_dump(unserialize($s)); $n = gmp_init(13); $n->foo = "bar"; -var_dump(unserialize(serialize($n))); +var_dump($s = serialize($n)); +var_dump(unserialize($s)); + +var_dump(unserialize('C:3:"GMP":15:{s:2:"42";a:0:{}}')); try { unserialize('C:3:"GMP":0:{}'); @@ -21,22 +24,47 @@ try { unserialize('C:3:"GMP":9:{s:2:"42";}'); } catch (Exception $e) { var_dump($e->getMessage()); } +try { + unserialize('O:3:"GMP":0:{}'); +} catch (Exception $e) { var_dump($e->getMessage()); } + +try { + unserialize('O:3:"GMP":1:{i:0;i:0;}'); +} catch (Exception $e) { var_dump($e->getMessage()); } + +try { + unserialize('O:3:"GMP":1:{i:0;s:0:"";}'); +} catch (Exception $e) { var_dump($e->getMessage()); } + +try { + unserialize('O:3:"GMP":2:{i:0;s:1:"0";i:1;i:0;}'); +} catch (Exception $e) { var_dump($e->getMessage()); } + ?> --EXPECTF-- object(GMP)#%d (1) { ["num"]=> string(2) "42" } -string(30) "C:3:"GMP":15:{s:2:"42";a:0:{}}" +string(27) "O:3:"GMP":1:{i:0;s:2:"2a";}" object(GMP)#%d (1) { ["num"]=> string(2) "42" } +string(56) "O:3:"GMP":2:{i:0;s:1:"d";i:1;a:1:{s:3:"foo";s:3:"bar";}}" object(GMP)#%d (2) { ["foo"]=> string(3) "bar" ["num"]=> string(2) "13" } +object(GMP)#1 (1) { + ["num"]=> + string(2) "42" +} +string(28) "Could not unserialize number" +string(32) "Could not unserialize properties" +string(28) "Could not unserialize number" +string(28) "Could not unserialize number" string(28) "Could not unserialize number" string(32) "Could not unserialize properties" From e81ad637f4c42c1d30ef136f7d880cebf22c65fb Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 18 Nov 2021 11:16:13 +0100 Subject: [PATCH 25/32] Update reflection test after stub change --- sapi/cli/tests/006.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sapi/cli/tests/006.phpt b/sapi/cli/tests/006.phpt index 3a55f998443c0..6d52e2078dd8c 100644 --- a/sapi/cli/tests/006.phpt +++ b/sapi/cli/tests/006.phpt @@ -85,7 +85,7 @@ string(%d) "Extension [ extension #%d pcre version %s ] { Parameter #3 [ int $flags = 0 ] Parameter #4 [ int $offset = 0 ] } - - Return [ int|false|null ] + - Return [ int|false ] } Function [ function preg_replace ] { From 5ab2749263b7d1b456ef0d4e956f1b5dcd9517d3 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 18 Nov 2021 14:17:36 +0300 Subject: [PATCH 26/32] Fixed a crash becaeuse of race condition in inheritance cache zend_inheritance_cache_entry.[num_]warnings must be updated before this entry is made visible to other processes --- ext/opcache/ZendAccelerator.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 9cd389a2ba451..3c892f82a3645 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2428,11 +2428,12 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, } entry->ce = new_ce = zend_persist_class_entry(ce); zend_update_parent_ce(new_ce); - entry->next = proto->inheritance_cache; - proto->inheritance_cache = entry; entry->num_warnings = EG(num_errors); entry->warnings = zend_persist_warnings(EG(num_errors), EG(errors)); + entry->next = proto->inheritance_cache; + proto->inheritance_cache = entry; + EG(num_errors) = 0; EG(errors) = NULL; From 1845b712e71a60484fb48f6f04a08ecaca33dc57 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 18 Nov 2021 14:50:49 +0100 Subject: [PATCH 27/32] Remove commented code in pass1 This has been disabled for a long time, and the whole optimization is no longer relevant given changes in PHP 8.1 --- Zend/Optimizer/pass1.c | 47 ------------------------------------------ 1 file changed, 47 deletions(-) diff --git a/Zend/Optimizer/pass1.c b/Zend/Optimizer/pass1.c index 8f3c263833dd6..3579ea1c6bfc8 100644 --- a/Zend/Optimizer/pass1.c +++ b/Zend/Optimizer/pass1.c @@ -25,7 +25,6 @@ * - constant expression evaluation * - optimize constant conditional JMPs * - pre-evaluate constant function calls - * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM */ #include "php.h" @@ -531,52 +530,6 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)); } break; -#if 0 - /* see ext/opcache/tests/bug78961.phpt */ -// case ZEND_FETCH_R: - case ZEND_FETCH_W: -// case ZEND_FETCH_RW: - case ZEND_FETCH_IS: -// case ZEND_FETCH_FUNC_ARG: - case ZEND_FETCH_UNSET: - /* convert FETCH $GLOBALS (global), FETCH_DIM $x into FETCH $x (global) */ - if ((opline->extended_value & ZEND_FETCH_GLOBAL) != 0 && - opline->op1_type == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING && - zend_string_equals_literal(Z_STR(ZEND_OP1_LITERAL(opline)), "GLOBALS") && - ((opline + 1)->opcode == opline->opcode + 1 || - ((opline + 1)->opcode == ZEND_UNSET_DIM && - opline->opcode == ZEND_FETCH_UNSET) || - ((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ && - opline->opcode == ZEND_FETCH_IS)) && - (opline + 1)->op1_type == opline->result_type && - (opline + 1)->op1.var == opline->result.var && - ((opline + 1)->op2_type != IS_CONST || - Z_TYPE(ZEND_OP2_LITERAL(opline + 1)) < IS_ARRAY)) { - - if ((opline + 1)->opcode == ZEND_UNSET_DIM) { - (opline + 1)->opcode = ZEND_UNSET_VAR; - (opline + 1)->extended_value = ZEND_FETCH_GLOBAL; - } else if ((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) { - (opline + 1)->opcode = ZEND_ISSET_ISEMPTY_VAR; - (opline + 1)->extended_value |= ZEND_FETCH_GLOBAL; - } else { - (opline + 1)->opcode = opline->opcode; - (opline + 1)->extended_value = ZEND_FETCH_GLOBAL; - } - (opline + 1)->op1_type = (opline + 1)->op2_type; - (opline + 1)->op1 = (opline + 1)->op2; - if ((opline + 1)->op1_type == IS_CONST && - Z_TYPE(ZEND_OP1_LITERAL(opline + 1)) != IS_STRING) { - - convert_to_string(&ZEND_OP1_LITERAL(opline + 1)); - zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline + 1))); - } - SET_UNUSED((opline + 1)->op2); - MAKE_NOP(opline); - } - break; -#endif case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: From e4679ef0c2d5a0a81cb6e9948dbec3fdb2b7c83f Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Thu, 18 Nov 2021 13:55:51 +0000 Subject: [PATCH 28/32] Fixed date/diff where the difference in hour is less than 1 --- ext/date/lib/timelib.c | 4 ++-- ext/date/lib/timelib.h | 6 +++--- ext/date/tests/bug81458.phpt | 7 +++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ext/date/lib/timelib.c b/ext/date/lib/timelib.c index f363d8a616e65..060c2d79db46e 100644 --- a/ext/date/lib/timelib.c +++ b/ext/date/lib/timelib.c @@ -200,7 +200,7 @@ void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec) void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h) { - if (hour > 0) { + if (hour >= 0) { *h = ((double)hour + (double)min / 60 + (double)sec / 3600); } else { *h = ((double)hour - (double)min / 60 - (double)sec / 3600); @@ -209,7 +209,7 @@ void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h) void timelib_hmsf_to_decimal_hour(int hour, int min, int sec, int us, double *h) { - if (hour > 0) { + if (hour >= 0) { *h = ((double)hour + (double)min / MINS_PER_HOUR + (double)sec / SECS_PER_HOUR) + (double)us / USECS_PER_HOUR; } else { *h = ((double)hour - (double)min / MINS_PER_HOUR - (double)sec / SECS_PER_HOUR) - (double)us / USECS_PER_HOUR; diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index bb04cad07063d..6b5b259a57a44 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -30,9 +30,9 @@ # include "timelib_config.h" #endif -#define TIMELIB_VERSION 202110 -#define TIMELIB_EXTENDED_VERSION 20211001 -#define TIMELIB_ASCII_VERSION "2021.10" +#define TIMELIB_VERSION 202111 +#define TIMELIB_EXTENDED_VERSION 20211101 +#define TIMELIB_ASCII_VERSION "2021.11" #include #include diff --git a/ext/date/tests/bug81458.phpt b/ext/date/tests/bug81458.phpt index ab0c46faffaca..709bedfde60eb 100644 --- a/ext/date/tests/bug81458.phpt +++ b/ext/date/tests/bug81458.phpt @@ -7,7 +7,14 @@ $second = new DateTime('2018-07-02 00:00:00.000000 America/Toronto'); var_dump($first->diff($second)->days); var_dump($first->diff($second)->d); + +date_default_timezone_set('UTC'); +$a = new DateTime('2018-12-01 00:00'); +$b = new DateTime('2018-12-02 00:01'); + +var_dump($a->diff($b)->days); ?> --EXPECT-- int(1) int(1) +int(1) From fc679671901635553df86751e548b914a69e2abd Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 5 Oct 2021 21:02:01 +0100 Subject: [PATCH 29/32] Increase script sleep in FPM process idle test --- sapi/fpm/tests/proc-idle-timeout.phpt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sapi/fpm/tests/proc-idle-timeout.phpt b/sapi/fpm/tests/proc-idle-timeout.phpt index a6d3395c14a84..3c638ea869f1a 100644 --- a/sapi/fpm/tests/proc-idle-timeout.phpt +++ b/sapi/fpm/tests/proc-idle-timeout.phpt @@ -4,7 +4,6 @@ FPM: Process manager config pm.process_idle_timeout --FILE-- status([ 'total processes' => 2, ]); // wait for process idle timeout -sleep(6); +sleep(5); $tester->status([ 'total processes' => 1, ]); From 2f8407f185be38dd05305ddd4dd422ebb9c9e634 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 14 Nov 2021 21:10:47 +0000 Subject: [PATCH 30/32] Increase read timeout in FPM process idle test --- sapi/fpm/tests/proc-idle-timeout.phpt | 2 +- sapi/fpm/tests/tester.inc | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sapi/fpm/tests/proc-idle-timeout.phpt b/sapi/fpm/tests/proc-idle-timeout.phpt index 3c638ea869f1a..9f6d4377b8ce4 100644 --- a/sapi/fpm/tests/proc-idle-timeout.phpt +++ b/sapi/fpm/tests/proc-idle-timeout.phpt @@ -29,7 +29,7 @@ EOT; $tester = new FPM\Tester($cfg, $code); $tester->start(); $tester->expectLogStartNotices(); -$tester->multiRequest(2); +$tester->multiRequest(2, null, null, null, false, 7000); $tester->status([ 'total processes' => 2, ]); diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc index 7868afc4ac1ba..5a190f0d39cbc 100644 --- a/sapi/fpm/tests/tester.inc +++ b/sapi/fpm/tests/tester.inc @@ -624,6 +624,7 @@ class Tester * @param string|null $successMessage * @param string|null $errorMessage * @param bool $connKeepAlive + * @param int $readTimeout * @return Response[] * @throws \Exception */ @@ -632,7 +633,8 @@ class Tester string $address = null, string $successMessage = null, string $errorMessage = null, - bool $connKeepAlive = false + bool $connKeepAlive = false, + int $readTimeout = 0 ) { if ($this->hasError()) { return new Response(null, true); @@ -658,8 +660,8 @@ class Tester ]; }, $requests); - $responses = array_map(function ($conn) { - $response = new Response($conn['client']->wait_for_response_data($conn['requestId'])); + $responses = array_map(function ($conn) use ($readTimeout) { + $response = new Response($conn['client']->wait_for_response_data($conn['requestId'], $readTimeout)); if ($this->debug) { $response->debugOutput(); } From b6419f919a556c12de18bd19b6ed53eed589563c Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Sat, 20 Nov 2021 11:55:04 -0500 Subject: [PATCH 31/32] Micro-optimizations for WeakMap Skip WeakMap lookup check used only for debug assertion in non-debug builds Use the `zend_hash_lookup` helper to optimize adding a WeakMap entry if the entry doesn't already exist. Closes GH-7670 --- Zend/zend_weakrefs.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index a4b16fbe97002..cd8c4ed9dadf4 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -87,9 +87,9 @@ static void zend_weakref_register(zend_object *object, void *payload) { GC_ADD_FLAGS(object, IS_OBJ_WEAKLY_REFERENCED); zend_ulong obj_addr = (zend_ulong) object; - zval *zv = zend_hash_index_find(&EG(weakrefs), obj_addr); - if (!zv) { - zend_hash_index_add_new_ptr(&EG(weakrefs), obj_addr, payload); + zval *zv = zend_hash_index_lookup(&EG(weakrefs), obj_addr); + if (Z_TYPE_P(zv) == IS_NULL) { + ZVAL_PTR(zv, payload); return; } @@ -127,9 +127,11 @@ static void zend_weakref_unregister(zend_object *object, void *payload) { } HashTable *ht = ptr; - tagged_ptr = zend_hash_index_find_ptr(ht, (zend_ulong) payload); - ZEND_ASSERT(tagged_ptr && "Weakref not registered?"); - ZEND_ASSERT(tagged_ptr == payload); +#if ZEND_DEBUG + void *old_payload = zend_hash_index_find_ptr(ht, (zend_ulong) payload); + ZEND_ASSERT(old_payload && "Weakref not registered?"); + ZEND_ASSERT(old_payload == payload); +#endif zend_hash_index_del(ht, (zend_ulong) payload); if (zend_hash_num_elements(ht) == 0) { GC_DEL_FLAGS(object, IS_OBJ_WEAKLY_REFERENCED); From 241bd3f4540ba29f1da57be604dc833cc1e21950 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Sat, 20 Nov 2021 15:20:52 -0500 Subject: [PATCH 32/32] Fix use after free when WeakMap is modified during field write (When a value's destructor triggers a resizing or rehashing of the WeakMap) Closes GH-7671 --- Zend/tests/weakrefs/weakrefs_006.phpt | 116 ++++++++++++++++++++++++++ Zend/zend_weakrefs.c | 6 +- 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/weakrefs/weakrefs_006.phpt diff --git a/Zend/tests/weakrefs/weakrefs_006.phpt b/Zend/tests/weakrefs/weakrefs_006.phpt new file mode 100644 index 0000000000000..a15ab6b542d57 --- /dev/null +++ b/Zend/tests/weakrefs/weakrefs_006.phpt @@ -0,0 +1,116 @@ +--TEST-- +WeakReference overwriting existing value +--FILE-- + +--EXPECT-- +In destruct +object(WeakMap)#1 (11) { + [0]=> + array(2) { + ["key"]=> + object(stdClass)#2 (0) { + } + ["value"]=> + int(123) + } + [1]=> + array(2) { + ["key"]=> + object(stdClass)#4 (0) { + } + ["value"]=> + int(0) + } + [2]=> + array(2) { + ["key"]=> + object(stdClass)#5 (0) { + } + ["value"]=> + int(1) + } + [3]=> + array(2) { + ["key"]=> + object(stdClass)#6 (0) { + } + ["value"]=> + int(2) + } + [4]=> + array(2) { + ["key"]=> + object(stdClass)#7 (0) { + } + ["value"]=> + int(3) + } + [5]=> + array(2) { + ["key"]=> + object(stdClass)#8 (0) { + } + ["value"]=> + int(4) + } + [6]=> + array(2) { + ["key"]=> + object(stdClass)#9 (0) { + } + ["value"]=> + int(5) + } + [7]=> + array(2) { + ["key"]=> + object(stdClass)#10 (0) { + } + ["value"]=> + int(6) + } + [8]=> + array(2) { + ["key"]=> + object(stdClass)#11 (0) { + } + ["value"]=> + int(7) + } + [9]=> + array(2) { + ["key"]=> + object(stdClass)#12 (0) { + } + ["value"]=> + int(8) + } + [10]=> + array(2) { + ["key"]=> + object(stdClass)#13 (0) { + } + ["value"]=> + int(9) + } +} \ No newline at end of file diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 563097500abfd..1e1efda92391a 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -351,8 +351,12 @@ static void zend_weakmap_write_dimension(zend_object *object, zval *offset, zval zval *zv = zend_hash_index_find(&wm->ht, (zend_ulong) obj_key); if (zv) { - zval_ptr_dtor(zv); + /* Because the destructors can have side effects such as resizing or rehashing the WeakMap storage, + * free the zval only after overwriting the original value. */ + zval zv_orig; + ZVAL_COPY_VALUE(&zv_orig, zv); ZVAL_COPY_VALUE(zv, value); + zval_ptr_dtor(&zv_orig); return; }