From b576bb901ebeaf944c442629e122e26dbcb4bbc7 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Thu, 21 Jul 2022 13:20:24 +0200 Subject: [PATCH 1/5] Avoid using a stack allocated zend_function in Closure::call, to avoid prevent crashes on bailout Having a stack allocated zend_function may cause crashes if the stack is polluted between bailout and the actual unwinding in zend_observer_fcall_end_all. Signed-off-by: Bob Weinand --- Zend/zend_closures.c | 91 ++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 1dd7715e55b..3a2c3b11d38 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -121,7 +121,6 @@ ZEND_METHOD(Closure, call) zend_closure *closure; zend_fcall_info fci; zend_fcall_info_cache fci_cache; - zend_function my_function; zend_object *newobj; zend_class_entry *newclass; @@ -142,55 +141,71 @@ ZEND_METHOD(Closure, call) return; } - if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) { - zval new_closure; - zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis); - closure = (zend_closure *) Z_OBJ(new_closure); - fci_cache.function_handler = &closure->func; - } else { - memcpy(&my_function, &closure->func, closure->func.type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function)); - my_function.common.fn_flags &= ~ZEND_ACC_CLOSURE; - /* use scope of passed object */ - my_function.common.scope = newclass; - if (closure->func.type == ZEND_INTERNAL_FUNCTION) { - my_function.internal_function.handler = closure->orig_internal_handler; - } - fci_cache.function_handler = &my_function; - - /* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */ - if (ZEND_USER_CODE(my_function.type) - && (closure->func.common.scope != newclass - || (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) { - void *ptr; - - my_function.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE; - ptr = emalloc(my_function.op_array.cache_size); - ZEND_MAP_PTR_INIT(my_function.op_array.run_time_cache, ptr); - memset(ptr, 0, my_function.op_array.cache_size); - } - } - fci_cache.called_scope = newclass; fci_cache.object = fci.object = newobj; fci.size = sizeof(fci); ZVAL_OBJ(&fci.function_name, &closure->std); + ZVAL_UNDEF(&closure_result); fci.retval = &closure_result; - if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(closure_result) != IS_UNDEF) { + if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) { + zval new_closure; + zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis); + closure = (zend_closure *) Z_OBJ(new_closure); + fci_cache.function_handler = &closure->func; + + zend_call_function(&fci, &fci_cache); + + /* copied upon generator creation */ + GC_DELREF(&closure->std); + } else { + zend_function *my_function; + if (ZEND_USER_CODE(closure->func.type)) { + my_function = emalloc(sizeof(zend_op_array)); + memcpy(my_function, &closure->func, sizeof(zend_op_array)); + } else { + my_function = emalloc(sizeof(zend_internal_function)); + memcpy(my_function, &closure->func, sizeof(zend_internal_function)); + } + my_function->common.fn_flags &= ~ZEND_ACC_CLOSURE; + /* use scope of passed object */ + my_function->common.scope = newclass; + if (closure->func.type == ZEND_INTERNAL_FUNCTION) { + my_function->internal_function.handler = closure->orig_internal_handler; + } + fci_cache.function_handler = my_function; + + /* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */ + if (ZEND_USER_CODE(my_function->type) + && (closure->func.common.scope != newclass + || (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) { + void *ptr; + + my_function->op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE; + ptr = emalloc(my_function->op_array.cache_size); + ZEND_MAP_PTR_INIT(my_function->op_array.run_time_cache, ptr); + memset(ptr, 0, my_function->op_array.cache_size); + } + + zend_call_function(&fci, &fci_cache); + + if (ZEND_USER_CODE(my_function->type)) { + if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE) { + efree(ZEND_MAP_PTR(my_function->op_array.run_time_cache)); + } + efree_size(my_function, sizeof(zend_op_array)); + } else { + efree_size(my_function, sizeof(zend_internal_function)); + } + } + + if (Z_TYPE(closure_result) != IS_UNDEF) { if (Z_ISREF(closure_result)) { zend_unwrap_reference(&closure_result); } ZVAL_COPY_VALUE(return_value, &closure_result); } - - if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) { - /* copied upon generator creation */ - GC_DELREF(&closure->std); - } else if (ZEND_USER_CODE(my_function.type) - && (fci_cache.function_handler->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE)) { - efree(ZEND_MAP_PTR(my_function.op_array.run_time_cache)); - } } /* }}} */ From 966d22b1bdafd047cea89ddd04788367ea3be0a4 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 28 Jul 2022 10:40:10 +0200 Subject: [PATCH 2/5] Fix property fetch on magic constants in constant expressions Closes GH-9136 Closes GH-9138 Closes GH-9172 --- NEWS | 2 ++ Zend/tests/gh9136.phpt | 10 ++++++++++ Zend/tests/gh9138.phpt | 11 +++++++++++ Zend/zend_compile.c | 4 ++++ 4 files changed, 27 insertions(+) create mode 100644 Zend/tests/gh9136.phpt create mode 100644 Zend/tests/gh9138.phpt diff --git a/NEWS b/NEWS index e4f488e8ace..a578fdbf482 100644 --- a/NEWS +++ b/NEWS @@ -62,6 +62,8 @@ PHP NEWS (Tobias Bachert) . Added error_log_mode ini setting. (Mikhail Galanin) . Updated request startup messages. (Eric Norris) + . Fixed GH-9136 and GH-9138 (Fixed fetching property of magic constant in + constant expressions). (ilutov) - COM: . Fixed bug GH-8750 (Can not create VT_ERROR variant type). (cmb) diff --git a/Zend/tests/gh9136.phpt b/Zend/tests/gh9136.phpt new file mode 100644 index 00000000000..22c4a056520 --- /dev/null +++ b/Zend/tests/gh9136.phpt @@ -0,0 +1,10 @@ +--TEST-- +GH-9136: Assertion when fetching property of magic constant in constant expression +--FILE-- +foo; + +?> +--EXPECTF-- +Warning: Attempt to read property "foo" on string in %s on line %d diff --git a/Zend/tests/gh9138.phpt b/Zend/tests/gh9138.phpt new file mode 100644 index 00000000000..33550580527 --- /dev/null +++ b/Zend/tests/gh9138.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-9138: NULL pointer dereference when fetching property of "bad" list in constant expression +--FILE-- +e)] +class Foo {} + +?> +--EXPECTF-- +Fatal error: Cannot use empty array elements in arrays in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7dd3d598df1..ff7c9b152cd 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -10616,6 +10616,10 @@ static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ case ZEND_AST_CONST_ENUM_INIT: zend_eval_const_expr(&ast->child[2]); return; + case ZEND_AST_PROP: + zend_eval_const_expr(&ast->child[0]); + zend_eval_const_expr(&ast->child[1]); + return; default: return; } From 3c016467c7e49a5eb8ced09fbeb008251a1ca608 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Thu, 28 Jul 2022 09:28:04 +0200 Subject: [PATCH 3/5] Escape \U and \u in generated stubs This fixes an issue where a namespaced class beginning with "U" or "u" would yield an invalid arginfo file due to the occurrence of a unicode escape sequence, causing a compile error. Co-authored-by: Guilliam Xavier Closes GH-9154. --- build/gen_stub.php | 7 ++++++- ext/zend_test/test.c | 9 +++++++++ ext/zend_test/test.stub.php | 6 ++++++ ext/zend_test/test_arginfo.h | 22 +++++++++++++++++++++- ext/zend_test/tests/gen_stub_test_01.phpt | 4 ++++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index 84e212163bb..d1274fa20fc 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -533,7 +533,12 @@ class SimpleType { } public function toEscapedName(): string { - return str_replace('\\', '\\\\', $this->name); + // Escape backslashes, and also encode \u and \U to avoid compilation errors in generated macros + return str_replace( + ['\\', '\\u', '\\U'], + ['\\\\', '\\\\165', '\\\\125'], + $this->name + ); } public function toVarEscapedName(): string { diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 7a9c97a0baf..e7a778e520f 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -43,6 +43,7 @@ static zend_class_entry *zend_test_class_with_method_with_parameter_attribute; static zend_class_entry *zend_test_child_class_with_method_with_parameter_attribute; static zend_class_entry *zend_test_forbid_dynamic_call; static zend_class_entry *zend_test_ns_foo_class; +static zend_class_entry *zend_test_ns_unlikely_compile_error_class; static zend_class_entry *zend_test_ns2_foo_class; static zend_class_entry *zend_test_ns2_ns_foo_class; static zend_class_entry *zend_test_unit_enum; @@ -535,6 +536,13 @@ static ZEND_METHOD(ZendTestNS_Foo, method) RETURN_LONG(0); } +static ZEND_METHOD(ZendTestNS_UnlikelyCompileError, method) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_NULL(); +} + static ZEND_METHOD(ZendTestNS2_Foo, method) { ZEND_PARSE_PARAMETERS_NONE(); @@ -698,6 +706,7 @@ PHP_MINIT_FUNCTION(zend_test) zend_test_forbid_dynamic_call = register_class_ZendTestForbidDynamicCall(); zend_test_ns_foo_class = register_class_ZendTestNS_Foo(); + zend_test_ns_unlikely_compile_error_class = register_class_ZendTestNS_UnlikelyCompileError(); zend_test_ns2_foo_class = register_class_ZendTestNS2_Foo(); zend_test_ns2_ns_foo_class = register_class_ZendTestNS2_ZendSubNS_Foo(); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 8b26d552e5d..5dbd8596546 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -150,6 +150,12 @@ namespace ZendTestNS { public function method(): int {} } + class UnlikelyCompileError { + /* This method signature would create a compile error due to the string + * "ZendTestNS\UnlikelyCompileError" in the generated macro call */ + public function method(): ?UnlikelyCompileError {} + } + } namespace ZendTestNS2 { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index fb3e7943313..186f813e5dd 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: 64a10ff1af71cb2f9024f73ddaa34a924b85b968 */ + * Stub hash: 2c654cefda278094fa4cdc25b83ced269e83cadf */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -131,6 +131,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZendTestNS_Foo_method, 0, 0, 0) #endif ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ZendTestNS_UnlikelyCompileError_method, 0, 0, ZendTestNS\\\125nlikelyCompileError, 1) +ZEND_END_ARG_INFO() + #define arginfo_class_ZendTestNS2_Foo_method arginfo_zend_test_void_return #define arginfo_class_ZendTestNS2_ZendSubNS_Foo_method arginfo_zend_test_void_return @@ -173,6 +176,7 @@ static ZEND_METHOD(ZendTestChildClassWithMethodWithParameterAttribute, override) static ZEND_METHOD(ZendTestForbidDynamicCall, call); static ZEND_METHOD(ZendTestForbidDynamicCall, callStatic); static ZEND_METHOD(ZendTestNS_Foo, method); +static ZEND_METHOD(ZendTestNS_UnlikelyCompileError, method); static ZEND_METHOD(ZendTestNS2_Foo, method); static ZEND_METHOD(ZendTestNS2_ZendSubNS_Foo, method); @@ -284,6 +288,12 @@ static const zend_function_entry class_ZendTestNS_Foo_methods[] = { }; +static const zend_function_entry class_ZendTestNS_UnlikelyCompileError_methods[] = { + ZEND_ME(ZendTestNS_UnlikelyCompileError, method, arginfo_class_ZendTestNS_UnlikelyCompileError_method, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + static const zend_function_entry class_ZendTestNS2_Foo_methods[] = { ZEND_ME(ZendTestNS2_Foo, method, arginfo_class_ZendTestNS2_Foo_method, ZEND_ACC_PUBLIC) ZEND_FE_END @@ -560,6 +570,16 @@ static zend_class_entry *register_class_ZendTestNS_Foo(void) return class_entry; } +static zend_class_entry *register_class_ZendTestNS_UnlikelyCompileError(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ZendTestNS", "UnlikelyCompileError", class_ZendTestNS_UnlikelyCompileError_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + return class_entry; +} + static zend_class_entry *register_class_ZendTestNS2_Foo(void) { zend_class_entry ce, *class_entry; diff --git a/ext/zend_test/tests/gen_stub_test_01.phpt b/ext/zend_test/tests/gen_stub_test_01.phpt index 8b13cbaa034..0a020a6f345 100644 --- a/ext/zend_test/tests/gen_stub_test_01.phpt +++ b/ext/zend_test/tests/gen_stub_test_01.phpt @@ -9,6 +9,8 @@ $foo = new \ZendTestNS2\Foo(); var_dump($foo); $foo->foo = new \ZendTestNS2\ZendSubNS\Foo(); var_dump($foo); +$foo = new \ZendTestNS\UnlikelyCompileError(); +var_dump($foo); ?> --EXPECTF-- object(ZendTestNS2\Foo)#%d (%d) { @@ -20,3 +22,5 @@ object(ZendTestNS2\Foo)#%d (%d) { object(ZendTestNS2\ZendSubNS\Foo)#%d (%d) { } } +object(ZendTestNS\UnlikelyCompileError)#%d (%d) { +} From 60cc37630fa8bb080b10d9916c78fe2f4dd845ec Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Tue, 26 Jul 2022 14:31:56 +0200 Subject: [PATCH 4/5] Drop Windows specific implementation of openssl_random_pseudo_bytes() Despite commit 69c3f8c[1] claiming otherwise, there is no need for any Windows specific implementation here. Users can use random_bytes(), if they desire so. [1] Closes GH-9153. --- ext/openssl/openssl.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index e8752f5d3a7..7fa6664e183 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -7652,15 +7652,6 @@ PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long buffer_le } buffer = zend_string_alloc(buffer_length, 0); -#ifdef PHP_WIN32 - /* random/urandom equivalent on Windows */ - if (php_win32_get_random_bytes((unsigned char*)(buffer)->val, (size_t) buffer_length) == FAILURE){ - zend_string_release_ex(buffer, 0); - zend_throw_exception(zend_ce_exception, "Error reading from source device", 0); - return NULL; - } -#else - PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(buffer_length, length); PHP_OPENSSL_RAND_ADD_TIME(); if (RAND_bytes((unsigned char*)ZSTR_VAL(buffer), (int)buffer_length) <= 0) { @@ -7670,7 +7661,7 @@ PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long buffer_le } else { php_openssl_store_errors(); } -#endif + return buffer; } From b56492be9c5b3071f25d15bd8ce72e0decb0284d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 28 Jul 2022 15:14:00 +0200 Subject: [PATCH 5/5] Do not add inherited interface methods to the class synopsis page These are not displayed currently in the manual so gen_stub.php should be adapted to this behavior. --- 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 d1274fa20fc..eb7f42cd4af 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2981,7 +2981,7 @@ class ClassInfo { $parentInfo->collectInheritedMembers( $parentsWithInheritedConstants, $unusedParentsWithInheritedProperties, - $parentsWithInheritedMethods, + $unusedParentsWithInheritedMethods, $classMap ); }