diff --git a/NEWS b/NEWS index b38484a71ac1..839dfa42e9ff 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,11 @@ PHP NEWS - MySQLi: . Fix GH-22854: Fixed failed assertion when accessing mysqli property after failed reconnection. (Kamil Tekiela) + . Fixed memory leak when closing a prepared statement after its connection + was killed. (Kamil Tekiela) + +- MySQLnd: + . Fixed field_count not resetting on OK packet. (Kamil Tekiela) - Opcache: . Fixed bug GH-23693 (Tracing JIT produces wrong results for a guard on a diff --git a/UPGRADING b/UPGRADING index 5d551005042e..10994928470a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -19,6 +19,11 @@ PHP 8.7 UPGRADE NOTES 1. Backward Incompatible Changes ======================================== +- PDO: + . PDOStatement::bindColumn() now throws a ValueError when the column name is + not present in the result set. It previously reported the condition + through PDO::ATTR_ERRMODE and returned false. + - Standard: . The number of filters that can be chained in a php://filter URL is limited to 16 by default. Set the stream context option max_filter_count to change @@ -79,3 +84,4 @@ PHP 8.7 UPGRADE NOTES - Standard: . Improved performance of array_splice() when inserting without removing elements. + . Improved performance of str_rot13(). diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 0f79ee786aab..82f273974cd5 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -17,6 +17,8 @@ PHP 8.7 INTERNALS UPGRADE NOTES - Removed zend_execute_scripts(). Manually call zend_execute_script() in a loop instead. - Removed ZEND_STATIC_ASSERT(). Use C11 static_assert() directly instead. +- Removed zend_atomic.h and the zend_atomic_* types and functions. + Use the standard C11 atomic_* types and functions instead. - The sapi_terminate_process() function was removed. - The sapi_force_http_10() function was removed. - The sapi_get_fd() function was removed. @@ -33,8 +35,8 @@ PHP 8.7 INTERNALS UPGRADE NOTES ======================== - Windows build system changes: - . C sources are now compiled as C17. MSVC builds require Visual Studio - 2026 or later and enable /experimental:c11atomics. + . C sources are now compiled as C17 and require C11 atomics. MSVC builds require + Visual Studio 2026 or later and enable /experimental:c11atomics. . C++-only extension compiler flags can be added to CXXFLAGS_. - Unix build system changes: diff --git a/Zend/Optimizer/escape_analysis.c b/Zend/Optimizer/escape_analysis.c index ca1cb6c1fdee..59e0cea05a79 100644 --- a/Zend/Optimizer/escape_analysis.c +++ b/Zend/Optimizer/escape_analysis.c @@ -170,6 +170,8 @@ static bool is_allocation_def(const zend_op_array *op_array, const zend_ssa *ssa && !ce->destructor && !ce->__get && !ce->__set + && !ce->__isset + && !ce->num_hooked_props && !(ce->ce_flags & forbidden_flags) && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { return true; @@ -239,6 +241,8 @@ static bool is_local_def(const zend_op_array *op_array, const zend_ssa *ssa, int && !ce->destructor && !ce->__get && !ce->__set + && !ce->__isset + && !ce->num_hooked_props && !ce->parent) { return true; } diff --git a/Zend/tests/fibers/gh23921.phpt b/Zend/tests/fibers/gh23921.phpt new file mode 100644 index 000000000000..62c2a458fa62 --- /dev/null +++ b/Zend/tests/fibers/gh23921.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-23921 (Fibers start with error_reporting = 0 when the error_reporting INI directive is not set) +--FILE-- +start(); +?> +--EXPECTF-- +30719 +30719 + +Warning: Undefined variable $undefined in %s on line 5 +after diff --git a/Zend/tests/fibers/silence-operator-outside-fiber-should-no-widen-error-reporting.phpt b/Zend/tests/fibers/silence-operator-outside-fiber-should-no-widen-error-reporting.phpt new file mode 100644 index 000000000000..9cb57c746936 --- /dev/null +++ b/Zend/tests/fibers/silence-operator-outside-fiber-should-no-widen-error-reporting.phpt @@ -0,0 +1,18 @@ +--TEST-- +Silence operator must not change the error_reporting value within the fiber +--FILE-- +start(); + +?> +--EXPECT-- +int(30719) +int(2) +int(2) diff --git a/Zend/tests/lazy_objects/gh23842.phpt b/Zend/tests/lazy_objects/gh23842.phpt new file mode 100644 index 000000000000..fe791dde685b --- /dev/null +++ b/Zend/tests/lazy_objects/gh23842.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-23842: skipLazyInitialization() copies an unresolved constant default with opcache +--CREDITS-- +DirkTrunkstar +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache_only=0 +--FILE-- +newLazyGhost(function () { + throw new \Exception('initializer'); +}); +$reflector->getProperty('currency')->skipLazyInitialization($product); + +var_dump($product->currency); + +?> +--EXPECT-- +string(3) "EUR" diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 9b865b7b5d28..db20926dd96d 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3791,13 +3791,13 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc const zend_class_entry *frame_scope = get_scope(frame); fcc->calling_scope = ce; if (frame_scope && !fcc->object) { - zend_object *object = zend_get_this_object(frame); + zend_object *this_ptr = zend_get_this_object(frame); - if (object && - instanceof_function(object->ce, frame_scope) && + if (this_ptr && + instanceof_function(this_ptr->ce, frame_scope) && instanceof_function(frame_scope, ce)) { - fcc->object = object; - fcc->called_scope = object->ce; + fcc->object = this_ptr; + fcc->called_scope = this_ptr->ce; } else { fcc->called_scope = ce; } @@ -3870,13 +3870,13 @@ static zend_always_inline bool zend_is_method_callable(zend_string *callable, co if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { fcc->calling_scope = ZSTR_GET_CE_CACHE(class_name); if (scope && !fcc->object) { - zend_object *object = zend_get_this_object(frame); + zend_object *this_ptr = zend_get_this_object(frame); - if (object && - instanceof_function(object->ce, scope) && + if (this_ptr && + instanceof_function(this_ptr->ce, scope) && instanceof_function(scope, fcc->calling_scope)) { - fcc->object = object; - fcc->called_scope = object->ce; + fcc->object = this_ptr; + fcc->called_scope = this_ptr->ce; } else { fcc->called_scope = fcc->calling_scope; } @@ -3982,10 +3982,10 @@ static zend_always_inline bool zend_is_method_callable(zend_string *callable, co retval = true; call_via_handler = (fcc->function_handler->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) != 0; if (call_via_handler && !fcc->object) { - zend_object *object = zend_get_this_object(frame); - if (object && - instanceof_function(object->ce, fcc->calling_scope)) { - fcc->object = object; + zend_object *this_ptr = zend_get_this_object(frame); + if (this_ptr && + instanceof_function(this_ptr->ce, fcc->calling_scope)) { + fcc->object = this_ptr; } } } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 786ccafbb9d0..5c4083052202 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -60,7 +60,7 @@ typedef struct _zend_fcall_info_cache { zend_function *function_handler; zend_class_entry *calling_scope; zend_class_entry *called_scope; - zend_object *object; /* Instance of object for method calls */ + zend_object *object; /* Pointer for object representing $this */ zend_object *closure; /* Closure reference, only if the callable *is* the object */ } zend_fcall_info_cache; @@ -858,16 +858,16 @@ static zend_always_inline zend_result zend_call_function_with_return_value( /* Call the provided zend_function with the given params. * If retval_ptr is NULL, the return value is discarded. - * If object is NULL, this must be a free function or static call. + * If this_ptr is NULL, this must be a free function or static call. * called_scope must be provided for instance and static method calls. */ ZEND_API void zend_call_known_function_ex( - zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr, + zend_function *fn, zend_object *this_ptr, zend_class_entry *called_scope, zval *retval_ptr, uint32_t param_count, zval *params, HashTable *named_params, uint32_t consumed_args); static zend_always_inline void zend_call_known_function( - zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr, + zend_function *fn, zend_object *this_ptr, zend_class_entry *called_scope, zval *retval_ptr, uint32_t param_count, zval *params, HashTable *named_params) { - zend_call_known_function_ex(fn, object, called_scope, retval_ptr, param_count, params, named_params, 0); + zend_call_known_function_ex(fn, this_ptr, called_scope, retval_ptr, param_count, params, named_params, 0); } static zend_always_inline void zend_call_known_fcc_ex( @@ -892,32 +892,32 @@ static zend_always_inline void zend_call_known_fcc( /* Call the provided zend_function instance method on an object. */ static zend_always_inline void zend_call_known_instance_method( - zend_function *fn, zend_object *object, zval *retval_ptr, + zend_function *fn, zend_object *this_ptr, zval *retval_ptr, uint32_t param_count, zval *params) { - zend_call_known_function(fn, object, object->ce, retval_ptr, param_count, params, NULL); + zend_call_known_function(fn, this_ptr, this_ptr->ce, retval_ptr, param_count, params, NULL); } static zend_always_inline void zend_call_known_instance_method_with_0_params( - zend_function *fn, zend_object *object, zval *retval_ptr) + zend_function *fn, zend_object *this_ptr, zval *retval_ptr) { - zend_call_known_instance_method(fn, object, retval_ptr, 0, NULL); + zend_call_known_instance_method(fn, this_ptr, retval_ptr, 0, NULL); } static zend_always_inline void zend_call_known_instance_method_with_1_params( - zend_function *fn, zend_object *object, zval *retval_ptr, zval *param) + zend_function *fn, zend_object *this_ptr, zval *retval_ptr, zval *param) { - zend_call_known_instance_method(fn, object, retval_ptr, 1, param); + zend_call_known_instance_method(fn, this_ptr, retval_ptr, 1, param); } ZEND_API void zend_call_known_instance_method_with_2_params( - zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2); + zend_function *fn, zend_object *this_ptr, zval *retval_ptr, zval *param1, zval *param2); /* Call method if it exists. Return FAILURE if method does not exist or call failed. * If FAILURE is returned, retval will be UNDEF. As such, destroying retval unconditionally * is legal. */ ZEND_API zend_result zend_call_method_if_exists( - zend_object *object, zend_string *method_name, zval *retval, + zend_object *this_ptr, zend_string *method_name, zval *retval, uint32_t param_count, zval *params); ZEND_API zend_result zend_delete_global_variable(zend_string *name); diff --git a/Zend/zend_atomic.c b/Zend/zend_atomic.c deleted file mode 100644 index 98a39c583437..000000000000 --- a/Zend/zend_atomic.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Levi Morrison | - +----------------------------------------------------------------------+ - */ - -#include "zend_atomic.h" - -/* This file contains the non-inline copy of atomic functions. This is useful - * for extensions written in languages such as Rust. C and C++ compilers are - * probably going to inline these functions, but in the case they don't, this - * is also where the code will go. - */ - -/* Defined for FFI users; everyone else use ZEND_ATOMIC_*_INIT. - * This is NOT ATOMIC as it is meant for initialization. - */ -ZEND_API void zend_atomic_bool_init(zend_atomic_bool *obj, bool desired) { - ZEND_ATOMIC_BOOL_INIT(obj, desired); -} - -ZEND_API void zend_atomic_int_init(zend_atomic_int *obj, int desired) { - ZEND_ATOMIC_INT_INIT(obj, desired); -} - -ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) { - return zend_atomic_bool_exchange_ex(obj, desired); -} - -ZEND_API int zend_atomic_int_exchange(zend_atomic_int *obj, int desired) { - return zend_atomic_int_exchange_ex(obj, desired); -} - -ZEND_API bool zend_atomic_bool_compare_exchange(zend_atomic_bool *obj, bool *expected, bool desired) -{ - return zend_atomic_bool_compare_exchange_ex(obj, expected, desired); -} - -ZEND_API bool zend_atomic_int_compare_exchange(zend_atomic_int *obj, int *expected, int desired) -{ - return zend_atomic_int_compare_exchange_ex(obj, expected, desired); -} - -ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) { - zend_atomic_bool_store_ex(obj, desired); -} - -ZEND_API void zend_atomic_int_store(zend_atomic_int *obj, int desired) { - zend_atomic_int_store_ex(obj, desired); -} - -#if (defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)) && !defined(HAVE_C11_ATOMICS) -/* On these platforms it is non-const due to underlying APIs. */ -ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj) { - return zend_atomic_bool_load_ex(obj); -} -ZEND_API int zend_atomic_int_load(zend_atomic_int *obj) { - return zend_atomic_int_load_ex(obj); -} -#else -ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) { - return zend_atomic_bool_load_ex(obj); -} -ZEND_API int zend_atomic_int_load(const zend_atomic_int *obj) { - return zend_atomic_int_load_ex(obj); -} -#endif diff --git a/Zend/zend_atomic.h b/Zend/zend_atomic.h deleted file mode 100644 index 5dee78c0570a..000000000000 --- a/Zend/zend_atomic.h +++ /dev/null @@ -1,407 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright © The PHP Group and Contributors. | - +----------------------------------------------------------------------+ - | This source file is subject to the Modified BSD License that is | - | bundled with this package in the file LICENSE, and is available | - | through the World Wide Web at . | - | | - | SPDX-License-Identifier: BSD-3-Clause | - +----------------------------------------------------------------------+ - | Authors: Levi Morrison | - +----------------------------------------------------------------------+ - */ - -#ifndef ZEND_ATOMIC_H -#define ZEND_ATOMIC_H - -#include "zend_portability.h" - -#include - -#define ZEND_GCC_PREREQ(x, y) \ - ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || (__GNUC__ > (x))) - -/* Builtins are used to avoid library linkage */ -#if __has_feature(c_atomic) && defined(__clang__) -#define HAVE_C11_ATOMICS 1 -#elif ZEND_GCC_PREREQ(4, 7) -#define HAVE_GNUC_ATOMICS 1 -#elif defined(__GNUC__) -#define HAVE_SYNC_ATOMICS 1 -#elif !defined(ZEND_WIN32) -#define HAVE_NO_ATOMICS 1 -#endif - -#undef ZEND_GCC_PREREQ - -/* Treat zend_atomic_* types as opaque. They have definitions only for size - * and alignment purposes. - */ - -#if (defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)) && !defined(HAVE_C11_ATOMICS) -typedef struct zend_atomic_bool_s { - volatile char value; -} zend_atomic_bool; -typedef struct zend_atomic_int_s { -# ifdef ZEND_WIN32 - volatile long value; -# else - volatile int value; -# endif -} zend_atomic_int; -#elif defined(HAVE_C11_ATOMICS) -typedef struct zend_atomic_bool_s { - _Atomic(bool) value; -} zend_atomic_bool; -typedef struct zend_atomic_int_s { - _Atomic(int) value; -} zend_atomic_int; -#else -typedef struct zend_atomic_bool_s { - volatile bool value; -} zend_atomic_bool; -typedef struct zend_atomic_int_s { - volatile int value; -} zend_atomic_int; -#endif - -BEGIN_EXTERN_C() - -#if defined(ZEND_WIN32) && !defined(HAVE_C11_ATOMICS) - -#ifndef InterlockedExchange8 -#define InterlockedExchange8 _InterlockedExchange8 -#endif -#ifndef InterlockedOr8 -#define InterlockedOr8 _InterlockedOr8 -#endif -#ifndef InterlockedCompareExchange8 -#define InterlockedCompareExchange8 _InterlockedCompareExchange8 -#endif -#ifndef InterlockedExchange -#define InterlockedExchange _InterlockedExchange -#endif -#ifndef InterlockedOr -#define InterlockedOr _InterlockedOr -#endif -#ifndef InterlockedCompareExchange -#define InterlockedCompareExchange _InterlockedCompareExchange -#endif - -#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired)) -#define ZEND_ATOMIC_INT_INIT(obj, desired) ((obj)->value = (desired)) - -#define ZEND_ATOMIC_BOOL_INITIALIZER(desired) {.value = (desired)} -#define ZEND_ATOMIC_INT_INITIALIZER(desired) {.value = (desired)} - -static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) { - return InterlockedExchange8(&obj->value, desired); -} - -static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) { - return (int) InterlockedExchange(&obj->value, desired); -} - -static zend_always_inline bool zend_atomic_bool_compare_exchange_ex(zend_atomic_bool *obj, bool *expected, bool desired) { - bool prev = (bool) InterlockedCompareExchange8(&obj->value, *expected, desired); - if (prev == *expected) { - return true; - } else { - *expected = prev; - return false; - } -} - -static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_int *obj, int *expected, int desired) { - int prev = (int) InterlockedCompareExchange(&obj->value, *expected, desired); - if (prev == *expected) { - return true; - } else { - *expected = prev; - return false; - } -} - -/* On this platform it is non-const due to Interlocked API */ -static zend_always_inline bool zend_atomic_bool_load_ex(zend_atomic_bool *obj) { - /* Or'ing with false won't change the value. */ - return InterlockedOr8(&obj->value, false); -} - -static zend_always_inline int zend_atomic_int_load_ex(zend_atomic_int *obj) { - /* Or'ing with 0 won't change the value. */ - return (int) InterlockedOr(&obj->value, 0); -} - -static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) { - (void)InterlockedExchange8(&obj->value, desired); -} - -static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) { - (void)InterlockedExchange(&obj->value, desired); -} - -#elif defined(HAVE_C11_ATOMICS) - -#define ZEND_ATOMIC_BOOL_INIT(obj, desired) __c11_atomic_init(&(obj)->value, (desired)) -#define ZEND_ATOMIC_INT_INIT(obj, desired) __c11_atomic_init(&(obj)->value, (desired)) - -#define ZEND_ATOMIC_BOOL_INITIALIZER(desired) {.value = (desired)} -#define ZEND_ATOMIC_INT_INITIALIZER(desired) {.value = (desired)} - -static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) { - return __c11_atomic_exchange(&obj->value, desired, __ATOMIC_SEQ_CST); -} - -static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) { - return __c11_atomic_exchange(&obj->value, desired, __ATOMIC_SEQ_CST); -} - -static zend_always_inline bool zend_atomic_bool_compare_exchange_ex(zend_atomic_bool *obj, bool *expected, bool desired) { - return __c11_atomic_compare_exchange_strong(&obj->value, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); -} - -static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_int *obj, int *expected, int desired) { - return __c11_atomic_compare_exchange_strong(&obj->value, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); -} - -static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) { - return __c11_atomic_load(&obj->value, __ATOMIC_SEQ_CST); -} - -static zend_always_inline int zend_atomic_int_load_ex(const zend_atomic_int *obj) { - return __c11_atomic_load(&obj->value, __ATOMIC_SEQ_CST); -} - -static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) { - __c11_atomic_store(&obj->value, desired, __ATOMIC_SEQ_CST); -} - -static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) { - __c11_atomic_store(&obj->value, desired, __ATOMIC_SEQ_CST); -} - -#elif defined(HAVE_GNUC_ATOMICS) - -/* bool */ - -#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired)) -#define ZEND_ATOMIC_INT_INIT(obj, desired) ((obj)->value = (desired)) - -#define ZEND_ATOMIC_BOOL_INITIALIZER(desired) {.value = (desired)} -#define ZEND_ATOMIC_INT_INITIALIZER(desired) {.value = (desired)} - -static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) { - bool prev = false; - __atomic_exchange(&obj->value, &desired, &prev, __ATOMIC_SEQ_CST); - return prev; -} - -static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) { - int prev = false; - __atomic_exchange(&obj->value, &desired, &prev, __ATOMIC_SEQ_CST); - return prev; -} - -static zend_always_inline bool zend_atomic_bool_compare_exchange_ex(zend_atomic_bool *obj, bool *expected, bool desired) { - return __atomic_compare_exchange(&obj->value, expected, &desired, /* weak */ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); -} - -static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_int *obj, int *expected, int desired) { - return __atomic_compare_exchange(&obj->value, expected, &desired, /* weak */ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); -} - -static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) { - bool prev = false; - __atomic_load(&obj->value, &prev, __ATOMIC_SEQ_CST); - return prev; -} - -static zend_always_inline int zend_atomic_int_load_ex(const zend_atomic_int *obj) { - int prev = false; - __atomic_load(&obj->value, &prev, __ATOMIC_SEQ_CST); - return prev; -} - -static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) { - __atomic_store(&obj->value, &desired, __ATOMIC_SEQ_CST); -} - -static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) { - __atomic_store(&obj->value, &desired, __ATOMIC_SEQ_CST); -} - -#elif defined(HAVE_SYNC_ATOMICS) - -#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired)) -#define ZEND_ATOMIC_INT_INIT(obj, desired) ((obj)->value = (desired)) - -#define ZEND_ATOMIC_BOOL_INITIALIZER(desired) {.value = (desired)} -#define ZEND_ATOMIC_INT_INITIALIZER(desired) {.value = (desired)} - -static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) { - bool prev = __sync_lock_test_and_set(&obj->value, desired); - - /* __sync_lock_test_and_set only does an acquire barrier, so sync - * immediately after. - */ - __sync_synchronize(); - return prev; -} - -static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) { - int prev = __sync_lock_test_and_set(&obj->value, desired); - - /* __sync_lock_test_and_set only does an acquire barrier, so sync - * immediately after. - */ - __sync_synchronize(); - return prev; -} - -static zend_always_inline bool zend_atomic_bool_compare_exchange_ex(zend_atomic_bool *obj, bool *expected, bool desired) { - bool prev = __sync_val_compare_and_swap(&obj->value, *expected, desired); - if (prev == *expected) { - return true; - } else { - *expected = prev; - return false; - } -} - -static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_int *obj, int *expected, int desired) { - int prev = __sync_val_compare_and_swap(&obj->value, *expected, desired); - if (prev == *expected) { - return true; - } else { - *expected = prev; - return false; - } -} - -static zend_always_inline bool zend_atomic_bool_load_ex(zend_atomic_bool *obj) { - /* Or'ing false won't change the value */ - return __sync_fetch_and_or(&obj->value, false); -} - -static zend_always_inline int zend_atomic_int_load_ex(zend_atomic_int *obj) { - /* Or'ing 0 won't change the value */ - return __sync_fetch_and_or(&obj->value, 0); -} - -static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) { - __sync_synchronize(); - obj->value = desired; - __sync_synchronize(); -} - -static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) { - __sync_synchronize(); - obj->value = desired; - __sync_synchronize(); -} - -#elif defined(HAVE_NO_ATOMICS) - -#warning No atomics support detected. Please open an issue with platform details. - -#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired)) -#define ZEND_ATOMIC_INT_INIT(obj, desired) ((obj)->value = (desired)) - -#define ZEND_ATOMIC_BOOL_INITIALIZER(desired) {.value = (desired)} -#define ZEND_ATOMIC_INT_INITIALIZER(desired) {.value = (desired)} - -static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) { - obj->value = desired; -} - -static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) { - obj->value = desired; -} - -static zend_always_inline bool zend_atomic_bool_compare_exchange_ex(zend_atomic_int *obj, bool *expected, bool desired) { - bool prev = obj->value; - if (prev == *expected) { - obj->value = desired; - return true; - } else { - *expected = prev; - return false; - } -} - -static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_int *obj, int *expected, int desired) { - int prev = obj->value; - if (prev == *expected) { - obj->value = desired; - return true; - } else { - *expected = prev; - return false; - } -} - -static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) { - return obj->value; -} - -static zend_always_inline int zend_atomic_int_load_ex(const zend_atomic_int *obj) { - return obj->value; -} - -static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) { - bool prev = obj->value; - obj->value = desired; - return prev; -} - -static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) { - int prev = obj->value; - obj->value = desired; - return prev; -} - -#endif - -ZEND_API void zend_atomic_bool_init(zend_atomic_bool *obj, bool desired); -ZEND_API void zend_atomic_int_init(zend_atomic_int *obj, int desired); - -ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired); -ZEND_API int zend_atomic_int_exchange(zend_atomic_int *obj, int desired); - -ZEND_API bool zend_atomic_bool_compare_exchange(zend_atomic_bool *obj, bool *expected, bool desired); -ZEND_API bool zend_atomic_int_compare_exchange(zend_atomic_int *obj, int *expected, int desired); - -ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired); -ZEND_API void zend_atomic_int_store(zend_atomic_int *obj, int desired); - -#if (defined(ZEND_WIN32) && !defined(HAVE_C11_ATOMICS)) || defined(HAVE_SYNC_ATOMICS) -/* On these platforms it is non-const due to underlying APIs. */ -ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj); -ZEND_API int zend_atomic_int_load(zend_atomic_int *obj); -#else -ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj); -ZEND_API int zend_atomic_int_load(const zend_atomic_int *obj); -#endif - -#if defined(HAVE_C11_ATOMICS) -# define ZEND_ATOMIC_FENCE_RELEASE() __c11_atomic_thread_fence(__ATOMIC_RELEASE) -# define ZEND_ATOMIC_FENCE_ACQUIRE() __c11_atomic_thread_fence(__ATOMIC_ACQUIRE) -#elif defined(HAVE_GNUC_ATOMICS) -# define ZEND_ATOMIC_FENCE_RELEASE() __atomic_thread_fence(__ATOMIC_RELEASE) -# define ZEND_ATOMIC_FENCE_ACQUIRE() __atomic_thread_fence(__ATOMIC_ACQUIRE) -#elif defined(HAVE_SYNC_ATOMICS) -# define ZEND_ATOMIC_FENCE_RELEASE() __sync_synchronize() -# define ZEND_ATOMIC_FENCE_ACQUIRE() __sync_synchronize() -#elif defined(ZEND_WIN32) -# define ZEND_ATOMIC_FENCE_RELEASE() MemoryBarrier() -# define ZEND_ATOMIC_FENCE_ACQUIRE() MemoryBarrier() -#else -# define ZEND_ATOMIC_FENCE_RELEASE() ((void)0) -# define ZEND_ATOMIC_FENCE_ACQUIRE() ((void)0) -#endif - -END_EXTERN_C() - -#endif diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index a5f9d1e8c848..ff3a5f0b3de9 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4344,8 +4344,8 @@ ZEND_API void ZEND_FASTCALL zend_free_compiled_variables(zend_execute_data *exec ZEND_API ZEND_COLD void ZEND_FASTCALL zend_fcall_interrupt(zend_execute_data *call) { - zend_atomic_bool_store_ex(&EG(vm_interrupt), false); - if (zend_atomic_bool_load_ex(&EG(timed_out))) { + atomic_store(&EG(vm_interrupt), false); + if (atomic_load(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { zend_interrupt_function(call); @@ -4353,7 +4353,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_fcall_interrupt(zend_execute_data *ca } #define ZEND_VM_INTERRUPT_CHECK() do { \ - if (UNEXPECTED(zend_atomic_bool_load_ex(&EG(vm_interrupt)))) { \ + if (UNEXPECTED(atomic_load(&EG(vm_interrupt)))) { \ ZEND_VM_INTERRUPT(); \ } \ } while (0) @@ -4365,14 +4365,14 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_fcall_interrupt(zend_execute_data *ca #endif #define ZEND_VM_LOOP_INTERRUPT_CHECK() do { \ - if (UNEXPECTED(zend_atomic_bool_load_ex(&EG(vm_interrupt)))) { \ + if (UNEXPECTED(atomic_load(&EG(vm_interrupt)))) { \ ZEND_VM_KIND_TAILCALL_SAVE_OPLINE(); \ ZEND_VM_LOOP_INTERRUPT(); \ } \ } while (0) #define ZEND_VM_FCALL_INTERRUPT_CHECK(call) do { \ - if (UNEXPECTED(zend_atomic_bool_load_ex(&EG(vm_interrupt)))) { \ + if (UNEXPECTED(atomic_load(&EG(vm_interrupt)))) { \ zend_fcall_interrupt(call); \ } \ } while (0) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 900487cf3898..3d0b690944b4 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -172,8 +172,8 @@ void init_executor(void) /* {{{ */ zend_lazy_objects_init(&EG(lazy_objects_store)); EG(full_tables_cleanup) = 0; - ZEND_ATOMIC_BOOL_INIT(&EG(vm_interrupt), false); - ZEND_ATOMIC_BOOL_INIT(&EG(timed_out), false); + atomic_init(&EG(vm_interrupt), false); + atomic_init(&EG(timed_out), false); EG(exception) = NULL; @@ -1099,8 +1099,8 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ /* This flag is regularly checked while running user functions, but not internal * So see whether interrupt flag was set while the function was running... */ - if (zend_atomic_bool_exchange_ex(&EG(vm_interrupt), false)) { - if (zend_atomic_bool_load_ex(&EG(timed_out))) { + if (atomic_exchange(&EG(vm_interrupt), false)) { + if (atomic_load(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { zend_interrupt_function(EG(current_execute_data)); @@ -1133,7 +1133,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ /* }}} */ ZEND_API void zend_call_known_function_ex( - zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr, + zend_function *fn, zend_object *this_ptr, zend_class_entry *called_scope, zval *retval_ptr, uint32_t param_count, zval *params, HashTable *named_params, uint32_t consumed_args) { zval retval; @@ -1143,16 +1143,17 @@ ZEND_API void zend_call_known_function_ex( ZEND_ASSERT(fn && "zend_function must be passed!"); fci.size = sizeof(fci); - fci.object = object; fci.retval = retval_ptr ? retval_ptr : &retval; fci.param_count = param_count; fci.params = params; fci.named_params = named_params; fci.consumed_args = consumed_args; - ZVAL_UNDEF(&fci.function_name); /* Unused */ + /* Unused */ + ZVAL_UNDEF(&fci.function_name); + fci.object = NULL; fcic.function_handler = fn; - fcic.object = object; + fcic.object = this_ptr; fcic.called_scope = called_scope; zend_result result = zend_call_function(&fci, &fcic); @@ -1170,16 +1171,16 @@ ZEND_API void zend_call_known_function_ex( } ZEND_API void zend_call_known_instance_method_with_2_params( - zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2) + zend_function *fn, zend_object *this_ptr, zval *retval_ptr, zval *param1, zval *param2) { zval params[2]; ZVAL_COPY_VALUE(¶ms[0], param1); ZVAL_COPY_VALUE(¶ms[1], param2); - zend_call_known_instance_method(fn, object, retval_ptr, 2, params); + zend_call_known_instance_method(fn, this_ptr, retval_ptr, 2, params); } ZEND_API zend_result zend_call_method_if_exists( - zend_object *object, zend_string *method_name, zval *retval, + zend_object *this_ptr, zend_string *method_name, zval *retval, uint32_t param_count, zval *params) { zval zval_method; @@ -1187,7 +1188,7 @@ ZEND_API zend_result zend_call_method_if_exists( ZVAL_STR(&zval_method, method_name); - if (UNEXPECTED(!zend_is_callable_ex(&zval_method, object, IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL, &fcc, NULL))) { + if (UNEXPECTED(!zend_is_callable_ex(&zval_method, this_ptr, IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL, &fcc, NULL))) { ZVAL_UNDEF(retval); return FAILURE; } @@ -1476,14 +1477,14 @@ ZEND_NORETURN ZEND_API void ZEND_FASTCALL zend_timeout(void) /* {{{ */ timer is not restarted properly, it could hang in the shutdown function. */ if (EG(hard_timeout) > 0) { - zend_atomic_bool_store_ex(&EG(timed_out), false); + atomic_store(&EG(timed_out), false); zend_set_timeout_ex(EG(hard_timeout), true); /* XXX Abused, introduce an additional flag if the value needs to be kept. */ EG(hard_timeout) = 0; } # endif #else - zend_atomic_bool_store_ex(&EG(timed_out), false); + atomic_store(&EG(timed_out), false); zend_set_timeout_ex(0, true); #endif @@ -1528,7 +1529,7 @@ static void zend_timeout_handler(int dummy) /* {{{ */ return; } #else - if (zend_atomic_bool_load_ex(&EG(timed_out))) { + if (atomic_load(&EG(timed_out))) { /* Die on hard timeout */ const char *error_filename = NULL; uint32_t error_lineno = 0; @@ -1563,8 +1564,8 @@ static void zend_timeout_handler(int dummy) /* {{{ */ zend_on_timeout(EG(timeout_seconds)); } - zend_atomic_bool_store_ex(&EG(timed_out), true); - zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + atomic_store(&EG(timed_out), true); + atomic_store(&EG(vm_interrupt), true); #ifndef ZTS if (EG(hard_timeout) > 0) { @@ -1588,8 +1589,8 @@ VOID CALLBACK tq_timer_cb(PVOID arg, BOOLEAN timed_out) } eg = (zend_executor_globals *)arg; - zend_atomic_bool_store_ex(&eg->timed_out, true); - zend_atomic_bool_store_ex(&eg->vm_interrupt, true); + atomic_store(&eg->timed_out, true); + atomic_store(&eg->vm_interrupt, true); } #endif @@ -1697,7 +1698,7 @@ void zend_set_timeout(zend_long seconds, bool reset_signals) /* {{{ */ EG(timeout_seconds) = seconds; zend_set_timeout_ex(seconds, reset_signals); - zend_atomic_bool_store_ex(&EG(timed_out), false); + atomic_store(&EG(timed_out), false); } /* }}} */ @@ -1706,7 +1707,7 @@ void zend_unset_timeout(void) /* {{{ */ #ifdef ZEND_WIN32 if (NULL != tq_timer) { if (!DeleteTimerQueueTimer(NULL, tq_timer, INVALID_HANDLE_VALUE)) { - zend_atomic_bool_store_ex(&EG(timed_out), false); + atomic_store(&EG(timed_out), false); tq_timer = NULL; zend_error_noreturn(E_ERROR, "Could not delete queued timer"); } @@ -1729,7 +1730,7 @@ void zend_unset_timeout(void) /* {{{ */ # endif } #endif - zend_atomic_bool_store_ex(&EG(timed_out), false); + atomic_store(&EG(timed_out), false); } /* }}} */ diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 6d392f3dcc3d..d016d450a646 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -570,12 +570,9 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) zend_fiber *fiber = EG(active_fiber); - /* Determine the current error_reporting ini setting. */ + /* We cannot rely on EG(error_reporting) as a silence operator @ may modify the executor global error_reporting + * value without changing the underlying INI value */ zend_long error_reporting = zend_ini_long_literal("error_reporting"); - /* If error_reporting is 0 and not explicitly set to 0, zend_ini_str returns a null pointer. */ - if (!error_reporting && !zend_ini_str_literal("error_reporting")) { - error_reporting = E_ALL; - } EG(vm_stack) = NULL; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index b7835ed63226..d74bd8e63447 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -26,7 +26,6 @@ #include "zend_globals_macros.h" -#include "zend_atomic.h" #include "zend_stack.h" #include "zend_ptr_stack.h" #include "zend_hash.h" @@ -223,8 +222,8 @@ struct _zend_executor_globals { bool full_tables_cleanup; - zend_atomic_bool vm_interrupt; - zend_atomic_bool timed_out; + atomic_bool vm_interrupt; + atomic_bool timed_out; HashTable autoload_current_classnames; diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index 0ffd8baa2cfa..a3028efe172b 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -52,6 +52,23 @@ #include #include +#ifdef __cplusplus +extern "C++" { +# include +/* Make the atomic types used by Zend available to C++ extensions. */ +using std::atomic_bool; +using std::atomic_int; +} +#else +# if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L +# error "Zend requires C11 or later" +# endif +# ifdef __STDC_NO_ATOMICS__ +# error "Zend requires C11 atomics" +# endif +# include +#endif + #ifdef HAVE_UNIX_H # include #endif diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index c0fd2eef277b..9c9e262169f8 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6682,7 +6682,11 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMP|CV, ANY, EVAL, SPEC(OBSERVER } } FREE_OP1(); - ZEND_VM_NEXT_OPCODE(); + if (OP1_TYPE & IS_CONST) { + ZEND_VM_NEXT_OPCODE(); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } } ZEND_VM_HANDLER(153, ZEND_UNSET_CV, CV, UNUSED) @@ -10711,14 +10715,14 @@ ZEND_VM_DEFINE_OP(137, ZEND_OP_DATA); ZEND_VM_HELPER(zend_interrupt_helper, ANY, ANY) { - zend_atomic_bool_store_ex(&EG(vm_interrupt), false); + atomic_store(&EG(vm_interrupt), false); #if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL /* opline is &call_interrupt_op. Load orig opline. */ LOAD_OPLINE(); #else SAVE_OPLINE(); #endif - if (zend_atomic_bool_load_ex(&EG(timed_out))) { + if (atomic_load(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { zend_interrupt_function(execute_data); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index b956b638b7e4..942f18eb7040 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4032,14 +4032,14 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_J static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) { - zend_atomic_bool_store_ex(&EG(vm_interrupt), false); + atomic_store(&EG(vm_interrupt), false); #if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL /* opline is &call_interrupt_op. Load orig opline. */ LOAD_OPLINE(); #else SAVE_OPLINE(); #endif - if (zend_atomic_bool_load_ex(&EG(timed_out))) { + if (atomic_load(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { zend_interrupt_function(execute_data); @@ -5464,7 +5464,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INCLUDE_OR_EV } - ZEND_VM_NEXT_OPCODE(); + if (IS_CONST & IS_CONST) { + ZEND_VM_NEXT_OPCODE(); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } } static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -5547,7 +5551,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INCLUDE_OR_EV } } FREE_OP(opline->op1_type, opline->op1.var); - ZEND_VM_NEXT_OPCODE(); + if (opline->op1_type & IS_CONST) { + ZEND_VM_NEXT_OPCODE(); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } } static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -17652,7 +17660,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INCLUDE_OR_EV } } zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); - ZEND_VM_NEXT_OPCODE(); + if (IS_TMP_VAR & IS_CONST) { + ZEND_VM_NEXT_OPCODE(); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } } static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -40426,7 +40438,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INCLUDE_OR_EV } - ZEND_VM_NEXT_OPCODE(); + if (IS_CV & IS_CONST) { + ZEND_VM_NEXT_OPCODE(); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } } static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -56877,14 +56893,14 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_JMP_FO static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS) { - zend_atomic_bool_store_ex(&EG(vm_interrupt), false); + atomic_store(&EG(vm_interrupt), false); #if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL /* opline is &call_interrupt_op. Load orig opline. */ LOAD_OPLINE(); #else SAVE_OPLINE(); #endif - if (zend_atomic_bool_load_ex(&EG(timed_out))) { + if (atomic_load(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { zend_interrupt_function(execute_data); @@ -58309,7 +58325,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INCLUDE_OR_EVAL_SP } - ZEND_VM_NEXT_OPCODE(); + if (IS_CONST & IS_CONST) { + ZEND_VM_NEXT_OPCODE(); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } } static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -58392,7 +58412,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INCLUDE_OR_EVAL_SP } } FREE_OP(opline->op1_type, opline->op1.var); - ZEND_VM_NEXT_OPCODE(); + if (opline->op1_type & IS_CONST) { + ZEND_VM_NEXT_OPCODE(); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } } static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FE_RESET_R_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -70395,7 +70419,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INCLUDE_OR_EVAL_SP } } zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); - ZEND_VM_NEXT_OPCODE(); + if (IS_TMP_VAR & IS_CONST) { + ZEND_VM_NEXT_OPCODE(); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } } static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FE_RESET_R_SPEC_TMP_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -93069,7 +93097,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INCLUDE_OR_EVAL_SP } - ZEND_VM_NEXT_OPCODE(); + if (IS_CV & IS_CONST) { + ZEND_VM_NEXT_OPCODE(); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } } static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FE_RESET_R_SPEC_CV_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) diff --git a/configure.ac b/configure.ac index 50df06a6b479..f48f1e8de90e 100644 --- a/configure.ac +++ b/configure.ac @@ -1757,7 +1757,6 @@ PHP_ADD_SOURCES([Zend], m4_normalize([ zend_alloc.c zend_API.c zend_ast.c - zend_atomic.c zend_attributes.c zend_autoload.c zend_builtin_functions.c diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c index 0e0a9a23b94a..dc693ad65e91 100644 --- a/ext/bcmath/bcmath.c +++ b/ext/bcmath/bcmath.c @@ -1058,6 +1058,9 @@ static zend_always_inline void bcmath_number_add_internal( } *ret = bc_add(n1, n2, *scale); (*ret)->n_scale = MIN(*scale, (*ret)->n_scale); + if (bc_is_zero(*ret)) { + (*ret)->n_sign = PLUS; + } bc_rm_trailing_zeros(*ret); } @@ -1070,6 +1073,9 @@ static zend_always_inline void bcmath_number_sub_internal( } *ret = bc_sub(n1, n2, *scale); (*ret)->n_scale = MIN(*scale, (*ret)->n_scale); + if (bc_is_zero(*ret)) { + (*ret)->n_sign = PLUS; + } bc_rm_trailing_zeros(*ret); } @@ -1086,6 +1092,9 @@ static zend_always_inline zend_result bcmath_number_mul_internal( } *ret = bc_multiply(n1, n2, *scale); (*ret)->n_scale = MIN(*scale, (*ret)->n_scale); + if (bc_is_zero(*ret)) { + (*ret)->n_sign = PLUS; + } bc_rm_trailing_zeros(*ret); return SUCCESS; } diff --git a/ext/bcmath/libbcmath/src/div.c b/ext/bcmath/libbcmath/src/div.c index 24ec9a64d77f..56db164e4448 100644 --- a/ext/bcmath/libbcmath/src/div.c +++ b/ext/bcmath/libbcmath/src/div.c @@ -350,7 +350,11 @@ bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale) /* If divisor is 1 / -1, the quotient's n_value is equal to numerator's n_value. */ if (_bc_do_compare(divisor, BCG(_one_), divisor->n_scale, false) == BCMATH_EQUAL) { bc_divide_by_one(numerator, quot, quot_scale); - (*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS; + if (bc_is_zero(*quot)) { + (*quot)->n_sign = PLUS; + } else { + (*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS; + } return true; } @@ -405,7 +409,11 @@ bool bc_divide(bc_num numerator, bc_num divisor, bc_num *quot, size_t scale) /* If divisor is 1 here, return the result of adjusting the decimal point position of numerator. */ if (divisor_size == 1 && *divisorptr == 1) { bc_divide_by_pow_10(numeratorptr, numerator_readable_size, quot, quot_size, quot_scale); - (*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS; + if (bc_is_zero(*quot)) { + (*quot)->n_sign = PLUS; + } else { + (*quot)->n_sign = numerator->n_sign == divisor->n_sign ? PLUS : MINUS; + } return true; } diff --git a/ext/bcmath/libbcmath/src/raise.c b/ext/bcmath/libbcmath/src/raise.c index 959ba924e57f..ef39148327f8 100644 --- a/ext/bcmath/libbcmath/src/raise.c +++ b/ext/bcmath/libbcmath/src/raise.c @@ -254,6 +254,9 @@ bc_raise_status bc_raise(bc_num base, long exponent, bc_num *result, size_t scal bc_free_num (result); *result = power; (*result)->n_scale = MIN(scale, (*result)->n_scale); + if (bc_is_zero(*result)) { + (*result)->n_sign = PLUS; + } } return BC_RAISE_STATUS_OK; } diff --git a/ext/bcmath/tests/number/methods/calc_methods_zero_result_sign.phpt b/ext/bcmath/tests/number/methods/calc_methods_zero_result_sign.phpt new file mode 100644 index 000000000000..1871f0e3ca2c --- /dev/null +++ b/ext/bcmath/tests/number/methods/calc_methods_zero_result_sign.phpt @@ -0,0 +1,35 @@ +--TEST-- +BcMath\Number calc methods return an unsigned zero when the result truncates to zero +--EXTENSIONS-- +bcmath +--FILE-- +$method($arg, $scale); + echo "{$num} {$method} {$arg}: {$ret} ", $ret <=> 0, ' ', var_export($ret == 0, true), "\n"; +} + +[$quot, $rem] = (new BcMath\Number('-0.001'))->divmod('1', 0); +echo "-0.001 divmod 1: {$quot} ", $quot <=> 0, ' ', var_export($quot == 0, true), "\n"; +?> +--EXPECT-- +-0.001 div 1: 0 0 true +0.001 div -1: 0 0 true +-0.001 div 10: 0.00 0 true +-0.001 div 0.1: 0.0 0 true +-0.001 add -0.001: 0 0 true +-0.001 sub 0.001: 0 0 true +-0.001 mul 1: 0 0 true +-0.1 pow 3: 0.00 0 true +-0.001 divmod 1: 0 0 true diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 361339a3466f..e53746379428 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -1466,6 +1466,7 @@ PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *o obj_node->_private = NULL; } } + object->node = NULL; } if (object != NULL && object->document != NULL) { /* Safe to call as if the resource were freed then doc pointer is NULL */ diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 23fd70247fe4..5e6319ae77cc 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -111,7 +111,6 @@ void php_clear_stmt_bind(MY_STMT *stmt) if (stmt->stmt) { if (mysqli_stmt_close(stmt->stmt, true)) { php_error_docref(NULL, E_WARNING, "Error occurred while closing statement"); - return; } } diff --git a/ext/mysqli/tests/mysqli_stmt_close_killed_connection.phpt b/ext/mysqli/tests/mysqli_stmt_close_killed_connection.phpt new file mode 100644 index 000000000000..8b573a7666cb --- /dev/null +++ b/ext/mysqli/tests/mysqli_stmt_close_killed_connection.phpt @@ -0,0 +1,32 @@ +--TEST-- +Closing a prepared statement after its connection was killed must not leak +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--FILE-- +prepare('DO 1'); +} +$link->query('KILL ' . $link->thread_id); + +// Over TCP, a few COM_STMT_CLOSE writes may be accepted before one fails +do { + array_pop($stmts); + usleep(1000); +} while ($stmts && !$link->errno); + +echo "done!\n"; +?> +--EXPECTF-- +Warning: main(): Error occurred while closing statement in %s on line %d +done! diff --git a/ext/mysqli/tests/mysqli_stmt_next_result_field_count.phpt b/ext/mysqli/tests/mysqli_stmt_next_result_field_count.phpt new file mode 100644 index 000000000000..4b04151d054d --- /dev/null +++ b/ext/mysqli/tests/mysqli_stmt_next_result_field_count.phpt @@ -0,0 +1,34 @@ +--TEST-- +mysqli_stmt::$field_count is 0 for the trailing OK packet of a stored procedure +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--FILE-- +query('DROP PROCEDURE IF EXISTS test_field_count_p'); +$link->query('CREATE PROCEDURE test_field_count_p() BEGIN SELECT 1 AS a; SELECT 2 AS b, 3 AS c; END'); + +$stmt = $link->prepare('CALL test_field_count_p()'); +$stmt->execute(); +do { + var_dump($stmt->field_count); + $stmt->get_result(); +} while ($stmt->next_result()); +?> +--CLEAN-- +query('DROP PROCEDURE IF EXISTS test_field_count_p'); +?> +--EXPECT-- +int(1) +int(2) +int(0) diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index b961c0436c92..522b37e9d813 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -513,6 +513,7 @@ mysqlnd_stmt_execute_parse_response(MYSQLND_STMT * const s, enum_mysqlnd_parse_e stmt->state = MYSQLND_STMT_EXECUTED; if (conn->last_query_type == QUERY_UPSERT || conn->last_query_type == QUERY_LOAD_LOCAL) { + stmt->field_count = conn->field_count; DBG_INF("PASS"); DBG_RETURN(PASS); } @@ -1755,6 +1756,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, close_on_server)(MYSQLND_STMT * const s, bo MYSQLND_STMT_DATA * stmt = s? s->data : NULL; MYSQLND_CONN_DATA * conn = stmt? stmt->conn : NULL; enum_mysqlnd_collected_stats statistic = STAT_LAST; + enum_func_status ret = PASS; DBG_ENTER("mysqlnd_stmt::close_on_server"); if (!stmt || !conn) { @@ -1792,14 +1794,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, close_on_server)(MYSQLND_STMT * const s, bo STAT_FREE_RESULT_EXPLICIT); if (GET_CONNECTION_STATE(&conn->state) == CONN_READY) { - enum_func_status ret = FAIL; - const size_t stmt_id = stmt->stmt_id; - - ret = conn->command->stmt_close(conn, stmt_id); - if (ret == FAIL) { - COPY_CLIENT_ERROR(stmt->error_info, *conn->error_info); - DBG_RETURN(FAIL); - } + ret = conn->command->stmt_close(conn, stmt->stmt_id); } } switch (stmt->execute_count) { @@ -1828,7 +1823,7 @@ MYSQLND_METHOD_PRIVATE(mysqlnd_stmt, close_on_server)(MYSQLND_STMT * const s, bo stmt->conn = NULL; } - DBG_RETURN(PASS); + DBG_RETURN(ret); } /* }}} */ diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 9d6167ba73e3..4e4c815377ac 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2541,7 +2541,7 @@ static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce, zend_inheritance_cache_entry *entry = ce->inheritance_cache; if (entry) { - ZEND_ATOMIC_FENCE_ACQUIRE(); + atomic_thread_fence(memory_order_acquire); } while (entry) { @@ -2702,7 +2702,7 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, entry->next = proto->inheritance_cache; ZCSG(map_ptr_last) = CG(map_ptr_last); - ZEND_ATOMIC_FENCE_RELEASE(); + atomic_thread_fence(memory_order_release); proto->inheritance_cache = entry; zend_shared_alloc_destroy_xlat_table(); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 45936ba01f48..21c15630bdf8 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -8836,7 +8836,7 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf EX(opline) = opline; } - if (zend_atomic_bool_load_ex(&EG(vm_interrupt)) || JIT_G(tracing)) { + if (atomic_load(&EG(vm_interrupt)) || JIT_G(tracing)) { return 1; /* Lock-free check if the side trace was already JIT-ed or blacklist-ed in another process */ } else if (t->exit_info[exit_num].flags & (ZEND_JIT_EXIT_JITED|ZEND_JIT_EXIT_BLACKLISTED)) { diff --git a/ext/opcache/tests/opt/dce_016.phpt b/ext/opcache/tests/opt/dce_016.phpt new file mode 100644 index 000000000000..2185a4abfee7 --- /dev/null +++ b/ext/opcache/tests/opt/dce_016.phpt @@ -0,0 +1,50 @@ +--TEST-- +DCE must not remove assignments to properties of an object escaping through __isset +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.file_update_protection=0 +--EXTENSIONS-- +opcache +--FILE-- +foo); + $o->x = 42; +} + +f(); +var_dump($g->x); + +#[AllowDynamicProperties] +class F { + function __isset($n) { + $this->x = 2; + return true; + } +} + +function i() { + $o = new F; + $o->x = 1; + isset($o->foo); + var_dump($o->x); +} +i(); + +?> +--EXPECT-- +int(42) +int(2) diff --git a/ext/opcache/tests/opt/dce_017.phpt b/ext/opcache/tests/opt/dce_017.phpt new file mode 100644 index 000000000000..0cf8ad9bf366 --- /dev/null +++ b/ext/opcache/tests/opt/dce_017.phpt @@ -0,0 +1,55 @@ +--TEST-- +DCE must not remove assignments to properties of an object escaping through a property hook +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.file_update_protection=0 +--EXTENSIONS-- +opcache +--FILE-- +h; + $o->x = 42; +} + +g(); +var_dump($g->x); + +class E { + public $x = 0; + public $h { + set { + global $g; + $g = $this; + } + } +} + +function h() { + $o = new E; + $o->h = 1; + $o->x = 42; +} + +h(); +var_dump($g->x); + +?> +--EXPECT-- +int(42) +int(42) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 3fcb34e9b801..b05ee7418d0c 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -4555,6 +4555,8 @@ PHP_FUNCTION(openssl_sign) Z_PARAM_LONG(salt_length) ZEND_PARSE_PARAMETERS_END(); + PHP_OPENSSL_CHECK_LONG_TO_INT(salt_length, salt_length, 6); + pkey = php_openssl_pkey_from_zval(key, 0, "", 0, 3); if (pkey == NULL) { if (!EG(exception)) { @@ -4573,7 +4575,6 @@ PHP_FUNCTION(openssl_sign) php_error_docref(NULL, E_WARNING, "Unknown digest algorithm"); RETURN_FALSE; } - PHP_OPENSSL_CHECK_LONG_TO_INT(salt_length, salt_length, 6); md_ctx = EVP_MD_CTX_create(); size_t siglen; diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 0646e7ba043f..c73478fe5835 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -27,6 +27,7 @@ #include "php.h" #include "ext/standard/info.h" +#include "ext/standard/php_filestat.h" #include "php_signal.h" #include "php_ticks.h" #include "zend_exceptions.h" @@ -1345,7 +1346,7 @@ static void pcntl_signal_handler(int signo, siginfo_t *siginfo, void *context) PCNTL_G(tail) = psig; PCNTL_G(pending_signals) = true; if (PCNTL_G(async_signals)) { - zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + atomic_store(&EG(vm_interrupt), true); } } @@ -1453,7 +1454,7 @@ void pcntl_signal_dispatch(void) PCNTL_G(tail) = next; if (PCNTL_G(async_signals)) { - zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + atomic_store(&EG(vm_interrupt), true); } } else { PCNTL_G(pending_signals) = false; diff --git a/ext/pcntl/tests/gh23986.phpt b/ext/pcntl/tests/gh23986.phpt new file mode 100644 index 000000000000..7b9976d50079 --- /dev/null +++ b/ext/pcntl/tests/gh23986.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-23986 (/proc/self paths resolve to the parent process after pcntl_fork()) +--EXTENSIONS-- +pcntl +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 56320c3eb323..6b0d22d09399 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -631,7 +631,7 @@ PHP_METHOD(PDO, prepare) if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 1)) != NULL) { if (Z_TYPE_P(item) != IS_ARRAY) { zend_type_error("PDO::ATTR_STATEMENT_CLASS constructor_args must be of type ?array, %s given", - zend_zval_value_name(value)); + zend_zval_value_name(item)); RETURN_THROWS(); } ZVAL_COPY_VALUE(&ctor_args, item); @@ -924,17 +924,18 @@ static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value, u zend_argument_type_error(value_arg_num, "User-supplied statement class cannot have a public constructor"); return false; } + item = zend_hash_index_find(Z_ARRVAL_P(value), 1); + if (item != NULL && Z_TYPE_P(item) != IS_ARRAY) { + zend_argument_type_error(value_arg_num, "PDO::ATTR_STATEMENT_CLASS constructor_args must be of type ?array, %s given", + zend_zval_value_name(item)); + return false; + } dbh->def_stmt_ce = pce; if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) { zval_ptr_dtor(&dbh->def_stmt_ctor_args); ZVAL_UNDEF(&dbh->def_stmt_ctor_args); } - if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 1)) != NULL) { - if (Z_TYPE_P(item) != IS_ARRAY) { - zend_argument_type_error(value_arg_num, "PDO::ATTR_STATEMENT_CLASS constructor_args must be of type ?array, %s given", - zend_zval_value_name(value)); - return false; - } + if (item != NULL) { ZVAL_COPY(&dbh->def_stmt_ctor_args, item); } return true; diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index c7cf92cfdfa8..9774b197b7d9 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -281,10 +281,6 @@ static bool really_register_bound_param(struct pdo_bound_param_data *param, pdo_ param->stmt = stmt; param->is_param = is_param; - if (Z_REFCOUNTED(param->driver_params)) { - Z_ADDREF(param->driver_params); - } - if (!is_param && param->name && stmt->columns) { /* try to map the name to the column */ int i; @@ -299,12 +295,7 @@ static bool really_register_bound_param(struct pdo_bound_param_data *param, pdo_ /* if you prepare and then execute passing an array of params keyed by names, * then this will trigger, and we don't want that */ if (param->paramno == -1) { - /* Should this always be an Error? */ - char *tmp; - /* TODO Error? */ - spprintf(&tmp, 0, "Did not find column name '%s' in the defined columns; it will not be bound", ZSTR_VAL(param->name)); - pdo_raise_impl_error(stmt->dbh, stmt, "HY000", tmp); - efree(tmp); + zend_argument_value_error(1, "must refer to a column present in the result set, \"%s\" given", ZSTR_VAL(param->name)); return false; } } @@ -368,6 +359,7 @@ static bool really_register_bound_param(struct pdo_bound_param_data *param, pdo_ } else { zend_hash_index_del(hash, pparam->paramno); } + ZVAL_UNDEF(¶m->driver_params); /* param->parameter is freed by hash dtor */ ZVAL_UNDEF(¶m->parameter); return false; @@ -1337,6 +1329,9 @@ static void register_bound_param(INTERNAL_FUNCTION_PARAMETERS, int is_param) /* if (!Z_ISUNDEF(param.parameter)) { zval_ptr_dtor(&(param.parameter)); } + if (!Z_ISUNDEF(param.driver_params)) { + zval_ptr_dtor(¶m.driver_params); + } RETURN_FALSE; } diff --git a/ext/pdo/tests/pdo_030.phpt b/ext/pdo/tests/pdo_030.phpt index 88308c18ca83..19cc059c9e6a 100644 --- a/ext/pdo/tests/pdo_030.phpt +++ b/ext/pdo/tests/pdo_030.phpt @@ -75,6 +75,17 @@ echo "===QUERY===\n"; var_dump($db->getAttribute(PDO::ATTR_STATEMENT_CLASS)); $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDOStatementx', array($db))); var_dump($db->getAttribute(PDO::ATTR_STATEMENT_CLASS)); +try { + $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, [PDOStatement::class, 'invalid-args']); +} catch (TypeError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +var_dump($db->getAttribute(PDO::ATTR_STATEMENT_CLASS)); +try { + $db->prepare('SELECT * FROM test030', [PDO::ATTR_STATEMENT_CLASS => [PDOStatement::class, 'invalid-args']]); +} catch (TypeError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} $stmt = $db->query('SELECT * FROM test030'); var_dump(get_class($stmt)); var_dump(get_class($stmt->dbh)); @@ -112,6 +123,18 @@ array(2) { } } } +TypeError: PDO::setAttribute(): Argument #2 ($value) PDO::ATTR_STATEMENT_CLASS constructor_args must be of type ?array, string given +array(2) { + [0]=> + string(13) "PDOStatementX" + [1]=> + array(1) { + [0]=> + object(PDODatabase)#%d (0) { + } + } +} +TypeError: PDO::ATTR_STATEMENT_CLASS constructor_args must be of type ?array, string given PDODatabase::query() PDOStatementX::__construct() string(13) "PDOStatementX" diff --git a/ext/pdo/tests/pdo_bindcolumn_unknown_column.phpt b/ext/pdo/tests/pdo_bindcolumn_unknown_column.phpt index 44e85ec07c31..3e3ba30e8c41 100644 --- a/ext/pdo/tests/pdo_bindcolumn_unknown_column.phpt +++ b/ext/pdo/tests/pdo_bindcolumn_unknown_column.phpt @@ -1,5 +1,5 @@ --TEST-- -PDO: bindColumn() must fail for a column name that is not in the result set +PDO: bindColumn() must throw for a column name that is not in the result set --EXTENSIONS-- pdo --SKIPIF-- @@ -17,16 +17,20 @@ require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc'; $db = PDOTest::factory(); $db->exec('CREATE TABLE pdo_bindcolumn_unknown_column (name varchar(255))'); -$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); -$stmt = $db->query('SELECT name FROM pdo_bindcolumn_unknown_column'); -var_dump(@$stmt->bindColumn('nosuchcolumn', $var)); - -$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); -try { - $stmt->bindColumn('nosuchcolumn', $var); -} catch (PDOException $e) { - echo $e::class, ": ", $e->getMessage(), PHP_EOL; +// The error mode must not affect a ValueError. +foreach ([PDO::ERRMODE_SILENT, PDO::ERRMODE_WARNING, PDO::ERRMODE_EXCEPTION] as $mode) { + $db->setAttribute(PDO::ATTR_ERRMODE, $mode); + $stmt = $db->query('SELECT name FROM pdo_bindcolumn_unknown_column'); + try { + $stmt->bindColumn('nosuchcolumn', $var); + } catch (ValueError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } } + +// A column that does exist still binds. +$stmt = $db->query('SELECT name FROM pdo_bindcolumn_unknown_column'); +var_dump($stmt->bindColumn('name', $var)); ?> --CLEAN-- exec('DROP TABLE pdo_bindcolumn_unknown_column'); ?> --EXPECT-- -bool(false) -PDOException: SQLSTATE[HY000]: General error: Did not find column name 'nosuchcolumn' in the defined columns; it will not be bound +ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "nosuchcolumn" given +ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "nosuchcolumn" given +ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "nosuchcolumn" given +bool(true) diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c index 7189954ec412..68055e192e62 100644 --- a/ext/pdo_dblib/dblib_driver.c +++ b/ext/pdo_dblib/dblib_driver.c @@ -75,11 +75,12 @@ static void dblib_handle_closer(pdo_dbh_t *dbh) pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data; if (H) { - pdo_dblib_err_dtor(&H->err); if (H->link) { + dbsetuserdata(H->link, (BYTE*) &H->err); dbclose(H->link); H->link = NULL; } + pdo_dblib_err_dtor(&H->err); if (H->login) { dbfreelogin(H->login); H->login = NULL; @@ -200,6 +201,8 @@ static bool pdo_dblib_transaction_cmd(const char *cmd, pdo_dbh_t *dbh) { pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data; + dbsetuserdata(H->link, (BYTE*) &H->err); + if (FAIL == dbcmd(H->link, cmd)) { return false; } @@ -238,6 +241,8 @@ zend_string *dblib_handle_last_id(pdo_dbh_t *dbh, const zend_string *name) * Would use scope_identity() but it's not implemented on Sybase */ + dbsetuserdata(H->link, (BYTE*) &H->err); + if (FAIL == dbcmd(H->link, "SELECT @@IDENTITY")) { return NULL; } diff --git a/ext/pdo_dblib/dblib_stmt.c b/ext/pdo_dblib/dblib_stmt.c index 0b97b1dfb076..e244a39fc32c 100644 --- a/ext/pdo_dblib/dblib_stmt.c +++ b/ext/pdo_dblib/dblib_stmt.c @@ -93,6 +93,8 @@ static int pdo_dblib_stmt_cursor_closer(pdo_stmt_t *stmt) pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; pdo_dblib_db_handle *H = S->H; + dbsetuserdata(H->link, (BYTE*) &S->err); + /* Cancel any pending results */ dbcancel(H->link); @@ -150,6 +152,8 @@ static int pdo_dblib_stmt_next_rowset(pdo_stmt_t *stmt) pdo_dblib_db_handle *H = S->H; RETCODE ret = SUCCESS; + dbsetuserdata(H->link, (BYTE*) &S->err); + /* Ideally use dbcanquery here, but there is a bug in FreeTDS's implementation of dbcanquery * It has been resolved but is currently only available in nightly builds */ @@ -199,6 +203,8 @@ static int pdo_dblib_stmt_fetch(pdo_stmt_t *stmt, pdo_dblib_stmt *S = (pdo_dblib_stmt*)stmt->driver_data; pdo_dblib_db_handle *H = S->H; + dbsetuserdata(H->link, (BYTE*) &S->err); + ret = dbnextrow(H->link); if (FAIL == ret) { @@ -224,6 +230,8 @@ static int pdo_dblib_stmt_describe(pdo_stmt_t *stmt, int colno) return FAILURE; } + dbsetuserdata(H->link, (BYTE*) &S->err); + if (colno == 0) { S->computed_column_name_count = 0; } @@ -348,6 +356,8 @@ static int pdo_dblib_stmt_get_col(pdo_stmt_t *stmt, int colno, zval *zv, enum pd DBCHAR *tmp_data; DBINT data_len, tmp_data_len; + dbsetuserdata(H->link, (BYTE*) &S->err); + coltype = dbcoltype(H->link, colno+1); data = dbdata(H->link, colno+1); data_len = dbdatlen(H->link, colno+1); @@ -470,7 +480,7 @@ static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zva return FAILURE; } - array_init(return_value); + dbsetuserdata(H->link, (BYTE*) &S->err); dbtypeinfo = dbcoltypeinfo(H->link, colno+1); @@ -478,6 +488,8 @@ static int pdo_dblib_stmt_get_column_meta(pdo_stmt_t *stmt, zend_long colno, zva coltype = dbcoltype(H->link, colno+1); + array_init(return_value); + add_assoc_long(return_value, "max_length", dbcollen(H->link, colno+1) ); add_assoc_long(return_value, "precision", (int) dbtypeinfo->precision ); add_assoc_long(return_value, "scale", (int) dbtypeinfo->scale ); diff --git a/ext/pdo_dblib/tests/gh23741.phpt b/ext/pdo_dblib/tests/gh23741.phpt new file mode 100644 index 000000000000..f3fc1a5ce445 --- /dev/null +++ b/ext/pdo_dblib/tests/gh23741.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-23741 (pdo_dblib: segfault after a failed query inside a PDO transaction) +--EXTENSIONS-- +pdo_dblib +--SKIPIF-- + +--FILE-- + PDO::ERRMODE_SILENT]); + +$db->query('CREATE TABLE gh23741 (id int)'); + +$db->beginTransaction(); +echo 'failing query inside the transaction: '; +var_dump($db->query('CREATE VIEW gh23741 AS SELECT 1 AS x')); +$db->rollBack(); + +echo 'query after the failure: '; +var_dump($db->query('DROP TABLE IF EXISTS gh23741') instanceof PDOStatement); + +echo "survived connection teardown\n"; +?> +--CLEAN-- +exec('DROP VIEW IF EXISTS gh23741'); +$db->exec('DROP TABLE IF EXISTS gh23741'); +?> +--EXPECT-- +failing query inside the transaction: bool(false) +query after the failure: bool(true) +survived connection teardown diff --git a/ext/pdo_dblib/tests/gh23741_2.phpt b/ext/pdo_dblib/tests/gh23741_2.phpt new file mode 100644 index 000000000000..d8297c3671ea --- /dev/null +++ b/ext/pdo_dblib/tests/gh23741_2.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-23741 (pdo_dblib: crash reading metadata after a sibling statement is freed) +--EXTENSIONS-- +pdo_dblib +--SKIPIF-- + +--FILE-- + PDO::ERRMODE_SILENT]); + +$wide = $db->query('SELECT 1 AS a, 2 AS b, 3 AS c'); +$narrow = $db->query('SELECT 9 AS z'); +unset($narrow); + +echo 'metadata for a column the connection no longer has: '; +var_dump($wide->getColumnMeta(2)); + +echo "survived the out-of-range column\n"; +?> +--EXPECT-- +metadata for a column the connection no longer has: bool(false) +survived the out-of-range column diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset_columncount.phpt b/ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset_columncount.phpt new file mode 100644 index 000000000000..94d739dc7c59 --- /dev/null +++ b/ext/pdo_mysql/tests/pdo_mysql_stmt_nextrowset_columncount.phpt @@ -0,0 +1,39 @@ +--TEST-- +MySQL PDOStatement->columnCount() is 0 for the trailing OK packet of a stored procedure +--EXTENSIONS-- +pdo_mysql +--SKIPIF-- + +--FILE-- +exec('DROP PROCEDURE IF EXISTS pdo_mysql_stmt_nextrowset_columncount_p'); +$db->exec('CREATE PROCEDURE pdo_mysql_stmt_nextrowset_columncount_p() BEGIN SELECT 1 AS a; SELECT 2 AS b, 3 AS c; END'); + +foreach ([true, false] as $emulate) { + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate); + $stmt = $db->prepare('CALL pdo_mysql_stmt_nextrowset_columncount_p()'); + $stmt->execute(); + do { + var_dump($stmt->columnCount()); + $stmt->fetchAll(); + } while ($stmt->nextRowset()); +} +?> +--CLEAN-- +exec('DROP PROCEDURE IF EXISTS pdo_mysql_stmt_nextrowset_columncount_p'); +?> +--EXPECT-- +int(1) +int(2) +int(0) +int(1) +int(2) +int(0) diff --git a/ext/pdo_sqlite/tests/pdo_driver_options_weakref.phpt b/ext/pdo_sqlite/tests/pdo_driver_options_weakref.phpt new file mode 100644 index 000000000000..15cafa656e70 --- /dev/null +++ b/ext/pdo_sqlite/tests/pdo_driver_options_weakref.phpt @@ -0,0 +1,38 @@ +--TEST-- +PDO SQLite releases driverOptions objects after binding or failed registration +--EXTENSIONS-- +pdo_sqlite +--FILE-- +prepare('SELECT ? AS value'); +$value = 1; +$driverOptions = [new Tracked()]; +$weakReference = WeakReference::create($driverOptions[0]); +var_dump($stmt->bindParam(1, $value, PDO::PARAM_STR, 0, $driverOptions)); +unset($driverOptions, $value, $stmt); +gc_collect_cycles(); +var_dump($weakReference->get()); + +$stmt = $db->prepare('SELECT ? AS value'); +$stmt->execute(); +$value = null; +$driverOptions = [new Tracked()]; +$weakReference = WeakReference::create($driverOptions[0]); +try { + $stmt->bindColumn('missing', $value, PDO::PARAM_STR, 0, $driverOptions); +} catch (ValueError $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +unset($e, $driverOptions); +var_dump($weakReference->get()); +unset($value, $stmt); +?> +--EXPECT-- +bool(true) +NULL +ValueError: PDOStatement::bindColumn(): Argument #1 ($column) must refer to a column present in the result set, "missing" given +NULL diff --git a/ext/random/csprng.c b/ext/random/csprng.c index 4c474fb86796..b6333aaea284 100644 --- a/ext/random/csprng.c +++ b/ext/random/csprng.c @@ -24,7 +24,6 @@ #include "php.h" #include "Zend/zend_exceptions.h" -#include "Zend/zend_atomic.h" #include "php_random.h" #include "php_random_csprng.h" @@ -61,7 +60,7 @@ #endif #ifndef PHP_WIN32 -static zend_atomic_int random_fd = ZEND_ATOMIC_INT_INITIALIZER(-1); +static atomic_int random_fd = -1; #endif ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_bytes_ex(void *bytes, size_t size, char *errstr, size_t errstr_size) @@ -146,7 +145,7 @@ ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_bytes_ex(void *bytes, size_ } # endif if (read_bytes < size) { - int fd = zend_atomic_int_load_ex(&random_fd); + int fd = atomic_load(&random_fd); struct stat st; if (fd < 0) { @@ -179,7 +178,7 @@ ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_bytes_ex(void *bytes, size_ return FAILURE; } int expected = -1; - if (!zend_atomic_int_compare_exchange_ex(&random_fd, &expected, fd)) { + if (!atomic_compare_exchange_strong(&random_fd, &expected, fd)) { close(fd); /* expected is now the actual value of random_fd */ fd = expected; @@ -265,7 +264,7 @@ ZEND_ATTRIBUTE_NONNULL PHPAPI zend_result php_random_int(zend_long min, zend_lon PHPAPI void php_random_csprng_shutdown(void) { #ifndef PHP_WIN32 - int fd = zend_atomic_int_exchange(&random_fd, -1); + int fd = atomic_exchange(&random_fd, -1); if (fd != -1) { close(fd); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8466cbcd27da..7552b00c1151 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6223,7 +6223,7 @@ ZEND_METHOD(ReflectionProperty, skipLazyInitialization) RETURN_THROWS(); } - const zval *src = &object->ce->default_properties_table[OBJ_PROP_TO_NUM(prop->offset)]; + const zval *src = &CE_DEFAULT_PROPERTIES_TABLE(object->ce)[OBJ_PROP_TO_NUM(prop->offset)]; zval *dst = OBJ_PROP(object, prop->offset); if (!(Z_PROP_FLAG_P(dst) & IS_PROP_LAZY)) { diff --git a/ext/simplexml/tests/reconstruct_with_retained_child.phpt b/ext/simplexml/tests/reconstruct_with_retained_child.phpt new file mode 100644 index 000000000000..c6a1cb418089 --- /dev/null +++ b/ext/simplexml/tests/reconstruct_with_retained_child.phpt @@ -0,0 +1,19 @@ +--TEST-- +SimpleXMLElement reconstruction with a retained child +--EXTENSIONS-- +simplexml +--FILE-- +old'); +$child = $xml->child; + +$xml->__construct('new'); + +var_dump((string) $xml->new); +var_dump((string) $child); +var_dump($child->asXML()); +?> +--EXPECT-- +string(3) "new" +string(3) "old" +string(18) "old" diff --git a/ext/standard/string.c b/ext/standard/string.c index ddd3f31bf14f..d23c117321a4 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -6054,34 +6054,26 @@ static zend_string *php_str_rot13(zend_string *str) gt = _mm_cmpgt_epi8(in, a_minus_1); lt = _mm_cmplt_epi8(in, m_plus_1); cmp = _mm_and_si128(lt, gt); - if (_mm_movemask_epi8(cmp)) { - cmp = _mm_and_si128(cmp, add); - delta = _mm_or_si128(delta, cmp); - } + cmp = _mm_and_si128(cmp, add); + delta = _mm_or_si128(delta, cmp); gt = _mm_cmpgt_epi8(in, n_minus_1); lt = _mm_cmplt_epi8(in, z_plus_1); cmp = _mm_and_si128(lt, gt); - if (_mm_movemask_epi8(cmp)) { - cmp = _mm_and_si128(cmp, sub); - delta = _mm_or_si128(delta, cmp); - } + cmp = _mm_and_si128(cmp, sub); + delta = _mm_or_si128(delta, cmp); gt = _mm_cmpgt_epi8(in, A_minus_1); lt = _mm_cmplt_epi8(in, M_plus_1); cmp = _mm_and_si128(lt, gt); - if (_mm_movemask_epi8(cmp)) { - cmp = _mm_and_si128(cmp, add); - delta = _mm_or_si128(delta, cmp); - } + cmp = _mm_and_si128(cmp, add); + delta = _mm_or_si128(delta, cmp); gt = _mm_cmpgt_epi8(in, N_minus_1); lt = _mm_cmplt_epi8(in, Z_plus_1); cmp = _mm_and_si128(lt, gt); - if (_mm_movemask_epi8(cmp)) { - cmp = _mm_and_si128(cmp, sub); - delta = _mm_or_si128(delta, cmp); - } + cmp = _mm_and_si128(cmp, sub); + delta = _mm_or_si128(delta, cmp); in = _mm_add_epi8(in, delta); _mm_storeu_si128((__m128i *)target, in); diff --git a/ext/standard/tests/streams/stream_errors_operation_depth_limit.phpt b/ext/standard/tests/streams/stream_errors_operation_depth_limit.phpt index 1eae9c5fd31d..3c1f934ef30d 100644 --- a/ext/standard/tests/streams/stream_errors_operation_depth_limit.phpt +++ b/ext/standard/tests/streams/stream_errors_operation_depth_limit.phpt @@ -2,8 +2,30 @@ Stream errors: operations refused by the depth limit keep the stack consistent --INI-- zend.max_allowed_stack_size=-1 +--SKIPIF-- + --FILE-- = 32 * 1024 * 1024)) { + posix_setrlimit(POSIX_RLIMIT_STACK, 32 * 1024 * 1024, $hard === 'unlimited' ? -1 : $hard); + } +} + class RecursiveStream { public $context; diff --git a/ext/zend_test/object_handlers.c b/ext/zend_test/object_handlers.c index 6b8bf4ab9912..9e78e9fdf524 100644 --- a/ext/zend_test/object_handlers.c +++ b/ext/zend_test/object_handlers.c @@ -250,7 +250,7 @@ static int vm_interrupt_comparable_compare(zval *op1, zval *op2) { ZEND_COMPARE_OBJECTS_FALLBACK(op1, op2); - zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + atomic_store(&EG(vm_interrupt), true); return ZEND_THREEWAY_COMPARE( Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op1), 0)), diff --git a/ext/zend_test/observer.c b/ext/zend_test/observer.c index 2fd4073b4af0..77ce0cc2525e 100644 --- a/ext/zend_test/observer.c +++ b/ext/zend_test/observer.c @@ -76,7 +76,7 @@ static void observer_begin(zend_execute_data *execute_data) assert_observer_opline(execute_data); if (ZT_G(observer_set_vm_interrupt_on_begin)) { - zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + atomic_store(&EG(vm_interrupt), true); } if (!ZT_G(observer_show_output)) { diff --git a/main/main.c b/main/main.c index 7a8d440c0d75..aed44e60c28e 100644 --- a/main/main.c +++ b/main/main.c @@ -1814,6 +1814,7 @@ PHPAPI void php_child_init(void) { refresh_memory_manager(); zend_max_execution_timer_init(); + php_clear_stat_cache(true, NULL, 0); } /* {{{ php_request_startup */ diff --git a/main/poll/poll_backend_epoll.c b/main/poll/poll_backend_epoll.c index 1cc05b3005e5..ef8d42435ec6 100644 --- a/main/poll/poll_backend_epoll.c +++ b/main/poll/poll_backend_epoll.c @@ -18,6 +18,11 @@ #include +#ifdef HAVE_EPOLL_PWAIT2 +/* Cleared when the running kernel returns ENOSYS */ +static atomic_bool epoll_pwait2_available = true; +#endif + typedef struct epoll_backend_data { int epoll_fd; struct epoll_event *events; @@ -186,13 +191,21 @@ static int epoll_backend_wait( backend_data->events_capacity = max_events; } - int nfds; + int nfds = 0; #ifdef HAVE_EPOLL_PWAIT2 - nfds = epoll_pwait2(backend_data->epoll_fd, backend_data->events, max_events, timeout, NULL); -#else - int timeout_ms = php_poll_timespec_to_ms(timeout); - nfds = epoll_wait(backend_data->epoll_fd, backend_data->events, max_events, timeout_ms); + if (EXPECTED(atomic_load(&epoll_pwait2_available))) { + nfds = epoll_pwait2( + backend_data->epoll_fd, backend_data->events, max_events, timeout, NULL); + if (UNEXPECTED(nfds < 0 && (errno == ENOSYS || errno == ENOTSUP))) { + atomic_store(&epoll_pwait2_available, false); + } + } + if (UNEXPECTED(!atomic_load(&epoll_pwait2_available))) #endif + { + int timeout_ms = php_poll_timespec_to_ms(timeout); + nfds = epoll_wait(backend_data->epoll_fd, backend_data->events, max_events, timeout_ms); + } if (nfds > 0) { for (int i = 0; i < nfds; i++) { diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index 88afd1b3752e..b879cde43653 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -1652,7 +1652,7 @@ void phpdbg_execute_ex(zend_execute_data *execute_data) /* {{{ */ } #ifdef ZEND_WIN32 - if (zend_atomic_bool_load_ex(&EG(timed_out))) { + if (atomic_load(&EG(timed_out))) { zend_timeout(); } #endif diff --git a/win32/build/config.w32 b/win32/build/config.w32 index f352d4c794aa..253dab653986 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -240,7 +240,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_default_classes.c zend_execute.c zend_strtod.c zend_gc.c zend_closures.c zend_weakrefs.c \ zend_float.c zend_string.c zend_generators.c zend_virtual_cwd.c zend_ast.c \ zend_inheritance.c zend_smart_str.c zend_cpuinfo.c zend_observer.c zend_system_id.c \ - zend_enum.c zend_fibers.c zend_atomic.c zend_hrtime.c zend_frameless_function.c zend_property_hooks.c \ + zend_enum.c zend_fibers.c zend_hrtime.c zend_frameless_function.c zend_property_hooks.c \ zend_lazy_objects.c zend_autoload.c zend_partial.c"); ADD_SOURCES("Zend\\Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c"); diff --git a/win32/signal.c b/win32/signal.c index abce3edb2fd1..1cf917d595a0 100644 --- a/win32/signal.c +++ b/win32/signal.c @@ -20,7 +20,7 @@ /* true globals; only used from main thread and from kernel callback */ static zend_fcall_info_cache ctrl_handler; static DWORD ctrl_evt = (DWORD)-1; -static zend_atomic_bool *vm_interrupt_flag = NULL; +static atomic_bool *vm_interrupt_flag = NULL; static void (*orig_interrupt_function)(zend_execute_data *execute_data); @@ -91,7 +91,7 @@ static BOOL WINAPI php_win32_signal_system_ctrl_handler(DWORD evt) return FALSE; } - zend_atomic_bool_store_ex(vm_interrupt_flag, true); + atomic_store(vm_interrupt_flag, true); ctrl_evt = evt;