diff --git a/NEWS b/NEWS index c54c42628e232..ce2e4dc6c5a90 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,92 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.4.8 +03 Jul 2025, PHP 8.4.9 + +- BcMath: + . Fixed bug GH-18641 (Accessing a BcMath\Number property by ref crashes). + (nielsdos) + +- Core: + . Fixed bugs GH-17711 and GH-18022 (Infinite recursion on deprecated attribute + evaluation) and GH-18464 (Recursion protection for deprecation constants not + released on bailout). (DanielEScherzer and ilutov) + . Fixed GH-18695 (zend_ast_export() - float number is not preserved). + (Oleg Efimov) + . Fix handling of references in zval_try_get_long(). (nielsdos) + . Do not delete main chunk in zend_gc. (danog, Arnaud) + . Fix compile issues with zend_alloc and some non-default options. (nielsdos) + +- Curl: + . Fix memory leak when setting a list via curl_setopt fails. (nielsdos) + +- Date: + . Fix leaks with multiple calls to DatePeriod iterator current(). (nielsdos) + +- DOM: + . Fixed bug GH-18744 (classList works not correctly if copy HTMLElement by + clone keyword). (nielsdos) + +- FPM: + . Fixed GH-18662 (fpm_get_status segfault). (txuna) + +- Hash: + . Fixed bug GH-14551 (PGO build fails with xxhash). (nielsdos) + +- Intl: + . Fix memory leak in intl_datetime_decompose() on failure. (nielsdos) + . Fix memory leak in locale lookup on failure. (nielsdos) + +- Opcache: + . Fixed bug GH-18743 (Incompatibility in Inline TLS Assembly on Alpine 3.22). + (nielsdos, Arnaud) + +- ODBC: + . Fix memory leak on php_odbc_fetch_hash() failure. (nielsdos) + +- OpenSSL: + . Fix memory leak of X509_STORE in php_openssl_setup_verify() on failure. + (nielsdos) + . Fixed bug #74796 (Requests through http proxy set peer name). + (Jakub Zelenka) + +- PDO ODBC: + . Fix memory leak if WideCharToMultiByte() fails. (nielsdos) + +- PDO Sqlite: + . Fixed memory leak with Pdo_Sqlite::createCollation when the callback + has an incorrect return type. (David Carlier) + +- Phar: + . Add missing filter cleanups on phar failure. (nielsdos) + . Fixed bug GH-18642 (Signed integer overflow in ext/phar fseek). (nielsdos) + +- PHPDBG: + . Fix 'phpdbg --help' segfault on shutdown with USE_ZEND_ALLOC=0. (nielsdos) + +- PGSQL: + . Fix warning not being emitted when failure to cancel a query with + pg_cancel_query(). (Girgias) + +- Random: + . Fix reference type confusion and leak in user random engine. + (nielsdos, timwolla) + +- Readline: + . Fix memory leak when calloc() fails in php_readline_completion_cb(). + (nielsdos) + +- SimpleXML: + . Fixed bug GH-18597 (Heap-buffer-overflow in zend_alloc.c when assigning + string with UTF-8 bytes). (nielsdos) + +- Soap: + . Fix memory leaks in php_http.c when call_user_function() fails. (nielsdos) + +- Tidy: + . Fix memory leak in tidy output handler on error. (nielsdos) + . Fix tidyOptIsReadonly deprecation, using tidyOptGetCategory. (David Carlier) + +06 Jun 2025, PHP 8.4.8 - Core: . Fixed GH-18480 (array_splice with large values for offset/length arguments). diff --git a/Zend/tests/ast/ast_serialize_floats.phpt b/Zend/tests/ast/ast_serialize_floats.phpt new file mode 100644 index 0000000000000..164b8b03338cf --- /dev/null +++ b/Zend/tests/ast/ast_serialize_floats.phpt @@ -0,0 +1,26 @@ +--TEST-- +Serialization of floats are correct +--INI-- +zend.assertions=1 +--FILE-- +getMessage(), ' failed', PHP_EOL; +} +try { + assert(!is_float(1.1)); +} catch (AssertionError $e) { + echo 'assert(): ', $e->getMessage(), ' failed', PHP_EOL; +} +try { + assert(!is_float(1234.5678)); +} catch (AssertionError $e) { + echo 'assert(): ', $e->getMessage(), ' failed', PHP_EOL; +} +?> +--EXPECT-- +assert(): assert(!is_float(0.0)) failed +assert(): assert(!is_float(1.1)) failed +assert(): assert(!is_float(1234.5678)) failed diff --git a/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt b/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt new file mode 100644 index 0000000000000..abec209343ab3 --- /dev/null +++ b/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-17711: Infinite recursion through deprecated class constants self-referencing through deprecation message +--FILE-- + +--EXPECTF-- +Deprecated: Constant C::C is deprecated, Message in %s on line %d +string(7) "Message" + +Deprecated: Constant D::C is deprecated, test in %s on line %d +string(4) "test" diff --git a/Zend/tests/constants/gh18463-class-constant.phpt b/Zend/tests/constants/gh18463-class-constant.phpt new file mode 100644 index 0000000000000..2af977205dc8d --- /dev/null +++ b/Zend/tests/constants/gh18463-class-constant.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-18463: Recursion protection should not be applied to internal class constants +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTF-- +Constant _ZendTestClass::ZEND_TEST_DEPRECATED is deprecated in %s on line %d + +Fatal error: Cannot use "string" as a class name as it is reserved in %s(%d) : eval()'d code on line %d diff --git a/Zend/tests/exception_027.phpt b/Zend/tests/exception_027.phpt new file mode 100644 index 0000000000000..805127420b367 --- /dev/null +++ b/Zend/tests/exception_027.phpt @@ -0,0 +1,37 @@ +--TEST-- +Exception properties are overridden by property hooks +--FILE-- +modified) { + throw new Exception(); + } else { + $this->modified = true; + + $this->code = $value; + } + } + } +} + +$e = new MyException("foo", 1, new Exception()); + +try { + $e->__construct("bar", 2, null); +} catch (Exception) { +} + +var_dump($e->getMessage()); +var_dump($e->getCode()); +var_dump($e->getPrevious()::class); + +?> +--EXPECTF-- +string(3) "bar" +int(1) +string(9) "Exception" \ No newline at end of file diff --git a/Zend/tests/exception_028.phpt b/Zend/tests/exception_028.phpt new file mode 100644 index 0000000000000..233b545fb36bd --- /dev/null +++ b/Zend/tests/exception_028.phpt @@ -0,0 +1,43 @@ +--TEST-- +ErrorException properties are overridden by property hooks +--FILE-- +modified) { + throw new Exception(); + } else { + $this->modified = true; + + $this->code = $value; + } + } + } +} + +$e = new MyException("foo", 1, E_NOTICE, "file1", 1, new Exception()); + +try { + $e->__construct("bar", 2, E_WARNING, "file2", 2, null); +} catch (Exception) { +} + +var_dump($e->getMessage()); +var_dump($e->getCode()); +var_dump($e->getSeverity()); +var_dump($e->getFile()); +var_dump($e->getLine()); +var_dump($e->getPrevious()::class); + +?> +--EXPECTF-- +string(3) "bar" +int(1) +int(8) +string(5) "file1" +int(1) +string(9) "Exception" diff --git a/Zend/tests/gh18756.phpt b/Zend/tests/gh18756.phpt new file mode 100644 index 0000000000000..6e112d9060499 --- /dev/null +++ b/Zend/tests/gh18756.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug GH-18756: Zend MM may delete the main chunk +--EXTENSIONS-- +zend_test +--FILE-- + +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/zend.h b/Zend/zend.h index 0ce9956c99c99..fa31986334a4c 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.8-dev" +#define ZEND_VERSION "4.4.9" #define ZEND_ENGINE_3 diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5aac3c1f7d77c..6e254561d3745 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1439,7 +1439,7 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_ ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) { if (c->ce == class_type) { - if (Z_TYPE(c->value) == IS_CONSTANT_AST) { + if (Z_TYPE(c->value) == IS_CONSTANT_AST || (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) { new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); memcpy(new_c, c, sizeof(zend_class_constant)); c = new_c; diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 12e322d0347b3..adc94188f4651 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2182,7 +2182,7 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) i++; } } - if (chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE) { + if (chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE && chunk != heap->main_chunk) { zend_mm_chunk *next_chunk = chunk->next; zend_mm_delete_chunk(heap, chunk); @@ -2409,7 +2409,9 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) /* Make sure the heap free below does not use tracked_free(). */ heap->custom_heap._free = __zend_free; } +#if ZEND_MM_STAT heap->size = 0; +#endif } void (*shutdown)(bool, bool) = heap->custom_heap._shutdown; @@ -2947,6 +2949,7 @@ static zend_always_inline zval *tracked_get_size_zv(zend_mm_heap *heap, void *pt } static zend_always_inline void tracked_check_limit(zend_mm_heap *heap, size_t add_size) { +#if ZEND_MM_STAT if (add_size > heap->limit - heap->size && !heap->overflow) { #if ZEND_DEBUG zend_mm_safe_error(heap, @@ -2958,6 +2961,7 @@ static zend_always_inline void tracked_check_limit(zend_mm_heap *heap, size_t ad heap->limit, add_size); #endif } +#endif } static void *tracked_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) @@ -2971,7 +2975,9 @@ static void *tracked_malloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC } tracked_add(heap, ptr, size); +#if ZEND_MM_STAT heap->size += size; +#endif return ptr; } @@ -2982,7 +2988,9 @@ static void tracked_free(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { zend_mm_heap *heap = AG(mm_heap); zval *size_zv = tracked_get_size_zv(heap, ptr); +#if ZEND_MM_STAT heap->size -= Z_LVAL_P(size_zv); +#endif zend_hash_del_bucket(heap->tracked_allocs, (Bucket *) size_zv); free(ptr); } @@ -3007,7 +3015,9 @@ static void *tracked_realloc(void *ptr, size_t new_size ZEND_FILE_LINE_DC ZEND_F ptr = __zend_realloc(ptr, new_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); tracked_add(heap, ptr, new_size); +#if ZEND_MM_STAT heap->size += new_size - old_size; +#endif return ptr; } @@ -3193,7 +3203,7 @@ ZEND_API zend_mm_storage *zend_mm_get_storage(zend_mm_heap *heap) #if ZEND_MM_STORAGE return heap->storage; #else - return NULL + return NULL; #endif } diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 555a51c4d7a50..f3f03940c62a6 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -279,7 +279,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_va( ast->attr = attr; for (uint32_t i = 0; i < children; i++) { ast->child[i] = va_arg(*va, zend_ast *); - if (lineno != (uint32_t)-1 && ast->child[i]) { + if (lineno == (uint32_t)-1 && ast->child[i]) { lineno = zend_ast_get_lineno(ast->child[i]); } } @@ -1608,7 +1608,7 @@ static ZEND_COLD void zend_ast_export_zval(smart_str *str, zval *zv, int priorit break; case IS_DOUBLE: smart_str_append_double( - str, Z_DVAL_P(zv), (int) EG(precision), /* zero_fraction */ false); + str, Z_DVAL_P(zv), (int) EG(precision), /* zero_fraction */ true); break; case IS_STRING: smart_str_appendc(str, '\''); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 832dedc421042..195c65d504374 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8822,6 +8822,10 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as if (deprecated) { ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED; + /* For deprecated constants, we need to flag the zval for recursion + * detection. Make sure the zval is separated out of shm. */ + ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; + ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; } } } diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index d453b8bb73717..50bdb3ddad290 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -353,8 +353,15 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * } if (UNEXPECTED(ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) { - if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { + if ((flags & ZEND_FETCH_CLASS_SILENT) == 0 && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { goto failure; } diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 6f0710c0ce63e..bd759c2891500 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -27,6 +27,17 @@ #define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */ #define CONST_DEPRECATED (1<<2) /* Deprecated */ #define CONST_OWNED (1<<3) /* constant should be destroyed together with class */ +#define CONST_RECURSIVE (1<<4) /* Recursion protection for constant evaluation */ + +#define CONST_IS_RECURSIVE(c) (Z_CONSTANT_FLAGS((c)->value) & CONST_RECURSIVE) +#define CONST_PROTECT_RECURSION(c) \ + do { \ + Z_CONSTANT_FLAGS((c)->value) |= CONST_RECURSIVE; \ + } while (0) +#define CONST_UNPROTECT_RECURSION(c) \ + do { \ + Z_CONSTANT_FLAGS((c)->value) &= ~CONST_RECURSIVE; \ + } while (0) #define PHP_USER_CONSTANT 0x7fffff /* a constant defined in user space */ diff --git a/Zend/zend_cpuinfo.h b/Zend/zend_cpuinfo.h index c81865c4a4d1a..7e53ba654dd41 100644 --- a/Zend/zend_cpuinfo.h +++ b/Zend/zend_cpuinfo.h @@ -127,58 +127,86 @@ ZEND_API int zend_cpu_supports(zend_cpu_feature feature); * functions */ ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_sse2(void) { +#ifdef __aarch64__ + return 0; +#else #ifdef PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("sse2"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_sse3(void) { +#ifdef __aarch64__ + return 0; +#else #ifdef PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("sse3"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_ssse3(void) { +#ifdef __aarch64__ + return 0; +#else #ifdef PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("ssse3"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_sse41(void) { +#ifdef __aarch64__ + return 0; +#else #ifdef PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("sse4.1"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_sse42(void) { +#ifdef __aarch64__ + return 0; +#else #ifdef PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("sse4.2"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_avx(void) { +#ifdef __aarch64__ + return 0; +#else #ifdef PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("avx"); +#endif } ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_avx2(void) { +#ifdef __aarch64__ + return 0; +#else #ifdef PHP_HAVE_BUILTIN_CPU_INIT __builtin_cpu_init(); #endif return __builtin_cpu_supports("avx2"); +#endif } #ifdef PHP_HAVE_AVX512_SUPPORTS diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 207b781385452..446aba4ee4a2f 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -320,15 +320,24 @@ ZEND_METHOD(Exception, __construct) if (message) { ZVAL_STR(&tmp, message); zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } if (code) { ZVAL_LONG(&tmp, code); zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } if (previous) { zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } } /* }}} */ @@ -370,32 +379,53 @@ ZEND_METHOD(ErrorException, __construct) ZVAL_STR_COPY(&tmp, message); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp); zval_ptr_dtor(&tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } if (code) { ZVAL_LONG(&tmp, code); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } if (previous) { zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } ZVAL_LONG(&tmp, severity); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } if (filename) { ZVAL_STR_COPY(&tmp, filename); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_FILE), &tmp); zval_ptr_dtor(&tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } if (!lineno_is_null) { ZVAL_LONG(&tmp, lineno); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } else if (filename) { ZVAL_LONG(&tmp, 0); zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } } /* }}} */ diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index ae75e95a71c1d..252b8df1ea0fb 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -378,6 +378,7 @@ static zend_always_inline zend_result zendi_try_convert_scalar_to_number(zval *o static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval *op, bool *failed) /* {{{ */ { *failed = 0; +try_again: switch (Z_TYPE_P(op)) { case IS_NULL: case IS_FALSE: @@ -448,6 +449,14 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval * case IS_ARRAY: *failed = 1; return 0; + case IS_REFERENCE: + op = Z_REFVAL_P(op); + if (Z_TYPE_P(op) == IS_LONG) { + return Z_LVAL_P(op); + } else { + goto try_again; + } + break; EMPTY_SWITCH_DEFAULT_CASE() } } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 2eecfe035ea2f..01bdcf0ad6a00 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6094,8 +6094,15 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 40cbb4958708f..25e537dc1c07a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7615,8 +7615,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -8776,8 +8783,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -25882,8 +25896,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -26452,8 +26473,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -35294,8 +35322,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -35654,8 +35689,15 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (c->ce->type == ZEND_USER_CLASS) { + /* Recursion protection only applied to user constants, GH-18463 */ + CONST_PROTECT_RECURSION(c); + } zend_deprecated_class_constant(c, constant_name); + if (c->ce->type == ZEND_USER_CLASS) { + CONST_UNPROTECT_RECURSION(c); + } if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); diff --git a/configure.ac b/configure.ac index 6dc1e45b34fd6..3394bccfd848a 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.4.8-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.9],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c index 233045bd7cd7e..3628b95a78e81 100644 --- a/ext/bcmath/bcmath.c +++ b/ext/bcmath/bcmath.c @@ -971,6 +971,12 @@ static zval *bcmath_number_read_property(zend_object *obj, zend_string *name, in return zend_std_read_property(obj, name, type, cache_slot, rv); } +static zval *bcmath_number_get_property_ptr_ptr(zend_object *object, zend_string *member, int type, void **cache_slot) +{ + /* Must always go through read property because all properties are virtual, and no dynamic properties are allowed. */ + return NULL; +} + static int bcmath_number_has_property(zend_object *obj, zend_string *name, int check_empty, void **cache_slot) { if (check_empty == ZEND_PROPERTY_NOT_EMPTY) { @@ -1014,6 +1020,7 @@ static void bcmath_number_register_class(void) bcmath_number_obj_handlers.unset_property = bcmath_number_unset_property; bcmath_number_obj_handlers.has_property = bcmath_number_has_property; bcmath_number_obj_handlers.read_property = bcmath_number_read_property; + bcmath_number_obj_handlers.get_property_ptr_ptr = bcmath_number_get_property_ptr_ptr; bcmath_number_obj_handlers.get_properties_for = bcmath_number_get_properties_for; bcmath_number_obj_handlers.cast_object = bcmath_number_cast_object; } diff --git a/ext/bcmath/tests/number/gh18641.phpt b/ext/bcmath/tests/number/gh18641.phpt new file mode 100644 index 0000000000000..ed8f55e9145a7 --- /dev/null +++ b/ext/bcmath/tests/number/gh18641.phpt @@ -0,0 +1,13 @@ +--TEST-- +GH-18641 (Accessing a BcMath\Number property by ref crashes) +--EXTENSIONS-- +bcmath +--FILE-- +value; +var_dump($x); +?> +--EXPECT-- +string(1) "1" diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 4bf0c84c67a0d..3b13744abce5a 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2166,12 +2166,14 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue ZEND_HASH_FOREACH_VAL(ph, current) { ZVAL_DEREF(current); val = zval_get_tmp_string(current, &tmp_val); - slist = curl_slist_append(slist, ZSTR_VAL(val)); + struct curl_slist *new_slist = curl_slist_append(slist, ZSTR_VAL(val)); zend_tmp_string_release(tmp_val); - if (!slist) { + if (!new_slist) { + curl_slist_free_all(slist); php_error_docref(NULL, E_WARNING, "Could not build curl_slist"); return FAILURE; } + slist = new_slist; } ZEND_HASH_FOREACH_END(); if (slist) { diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 834db1e226ccf..94ffd974c082a 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -1617,6 +1617,7 @@ static zval *date_period_it_current_data(zend_object_iterator *iter) php_date_obj *newdateobj; /* Create new object */ + zval_ptr_dtor(&iterator->current); php_date_instantiate(get_base_date_class(object->start_ce), &iterator->current); newdateobj = Z_PHPDATE_P(&iterator->current); newdateobj->time = timelib_time_ctor(); diff --git a/ext/date/tests/multiple_calls_date_period_iterator_current.phpt b/ext/date/tests/multiple_calls_date_period_iterator_current.phpt new file mode 100644 index 0000000000000..b0e90873e6126 --- /dev/null +++ b/ext/date/tests/multiple_calls_date_period_iterator_current.phpt @@ -0,0 +1,42 @@ +--TEST-- +Multiple calls to DatePeriod iterator current() leak objects +--FILE-- +getIterator(); +var_dump($iter->current()); +var_dump($iter->current()); +$iter->current()->setTimestamp(0); +var_dump($iter->current()); + +?> +--EXPECT-- +object(DateTime)#9 (3) { + ["date"]=> + string(26) "2018-12-31 00:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} +object(DateTime)#9 (3) { + ["date"]=> + string(26) "2018-12-31 00:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} +object(DateTime)#9 (3) { + ["date"]=> + string(26) "2018-12-31 00:00:00.000000" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} diff --git a/ext/dom/element.c b/ext/dom/element.c index 64c53e4a2d8a1..e25805df53eb6 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -177,10 +177,7 @@ zend_result dom_element_class_name_write(dom_object *obj, zval *newval) } /* }}} */ -/* {{{ classList TokenList -URL: https://dom.spec.whatwg.org/#dom-element-classlist -*/ -zend_result dom_element_class_list_read(dom_object *obj, zval *retval) +zval *dom_element_class_list_zval(dom_object *obj) { const uint32_t PROP_INDEX = 0; @@ -191,7 +188,15 @@ zend_result dom_element_class_list_read(dom_object *obj, zval *retval) ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX); #endif - zval *cached_token_list = OBJ_PROP_NUM(&obj->std, PROP_INDEX); + return OBJ_PROP_NUM(&obj->std, PROP_INDEX); +} + +/* {{{ classList TokenList +URL: https://dom.spec.whatwg.org/#dom-element-classlist +*/ +zend_result dom_element_class_list_read(dom_object *obj, zval *retval) +{ + zval *cached_token_list = dom_element_class_list_zval(obj); if (Z_ISUNDEF_P(cached_token_list)) { object_init_ex(cached_token_list, dom_token_list_class_entry); dom_token_list_object *intern = php_dom_token_list_from_obj(Z_OBJ_P(cached_token_list)); diff --git a/ext/dom/inner_html_mixin.c b/ext/dom/inner_html_mixin.c index e72b205bf4628..0af47e2cf019f 100644 --- a/ext/dom/inner_html_mixin.c +++ b/ext/dom/inner_html_mixin.c @@ -98,7 +98,7 @@ zend_result dom_element_inner_html_read(dom_object *obj, zval *retval) status |= xmlOutputBufferFlush(out); status |= xmlOutputBufferClose(out); } - (void) xmlSaveClose(ctxt); + status |= xmlSaveClose(ctxt); xmlCharEncCloseFunc(handler); } if (UNEXPECTED(status < 0)) { diff --git a/ext/dom/lexbor/lexbor/core/sbst.h b/ext/dom/lexbor/lexbor/core/sbst.h index 40e0e9148a7ec..03444afdc747d 100644 --- a/ext/dom/lexbor/lexbor/core/sbst.h +++ b/ext/dom/lexbor/lexbor/core/sbst.h @@ -15,11 +15,21 @@ extern "C" { #include "lexbor/core/base.h" +#ifdef __has_attribute +# if __has_attribute(nonstring) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 15 +# define LXB_NONSTRING __attribute__((nonstring)) +# else +# define LXB_NONSTRING +# endif +#else +# define LXB_NONSTRING +#endif + typedef struct { lxb_char_t key; - lxb_char_t value[6]; + lxb_char_t value[6] LXB_NONSTRING; unsigned char value_len; unsigned short left; diff --git a/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch b/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch index dfb0c0a980ebb..24b5fda2bba9f 100644 --- a/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch +++ b/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch @@ -5,24 +5,37 @@ Subject: [PATCH 5/6] Shrink size of static binary search tree This also makes it more efficient on the data cache. --- - source/lexbor/core/sbst.h | 10 +++++----- + source/lexbor/core/sbst.h | 19 ++++++++++++++----- source/lexbor/html/tokenizer/state.c | 2 +- utils/lexbor/html/tmp/tokenizer_res.h | 2 +- utils/lexbor/html/tokenizer_entities_bst.py | 8 ++++---- utils/lexbor/lexbor/LXB.py | 2 +- - 5 files changed, 12 insertions(+), 12 deletions(-) + 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/source/lexbor/core/sbst.h b/source/lexbor/core/sbst.h -index b0fbc54..40e0e91 100644 +index b0fbc54..15a1d40 100644 --- a/source/lexbor/core/sbst.h +++ b/source/lexbor/core/sbst.h -@@ -19,12 +19,12 @@ extern "C" { +@@ -15,16 +15,25 @@ extern "C" { + + #include "lexbor/core/base.h" + ++#ifdef __has_attribute ++# if __has_attribute(nonstring) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 15 ++# define LXB_NONSTRING __attribute__((nonstring)) ++# else ++# define LXB_NONSTRING ++# endif ++#else ++# define LXB_NONSTRING ++#endif + typedef struct { lxb_char_t key; - void *value; - size_t value_len; -+ lxb_char_t value[6]; ++ lxb_char_t value[6] LXB_NONSTRING; + unsigned char value_len; - size_t left; @@ -35,10 +48,10 @@ index b0fbc54..40e0e91 100644 lexbor_sbst_entry_static_t; diff --git a/source/lexbor/html/tokenizer/state.c b/source/lexbor/html/tokenizer/state.c -index 70ca391..2f3414f 100644 +index 158aade..207b909 100644 --- a/source/lexbor/html/tokenizer/state.c +++ b/source/lexbor/html/tokenizer/state.c -@@ -1815,7 +1815,7 @@ lxb_html_tokenizer_state_char_ref_named(lxb_html_tokenizer_t *tkz, +@@ -1820,7 +1820,7 @@ lxb_html_tokenizer_state_char_ref_named(lxb_html_tokenizer_t *tkz, goto done; } @@ -61,7 +74,7 @@ index b3701d5..73ab66e 100644 * Do not change this file! */ diff --git a/utils/lexbor/html/tokenizer_entities_bst.py b/utils/lexbor/html/tokenizer_entities_bst.py -index ee7dcb4..7cd1335 100755 +index 4fa0999..8bd83b2 100755 --- a/utils/lexbor/html/tokenizer_entities_bst.py +++ b/utils/lexbor/html/tokenizer_entities_bst.py @@ -1,6 +1,6 @@ @@ -98,7 +111,7 @@ index ee7dcb4..7cd1335 100755 + "../../../source/lexbor/html/tokenizer/res.h", "data/entities.json"); diff --git a/utils/lexbor/lexbor/LXB.py b/utils/lexbor/lexbor/LXB.py -index 2370c66..c41e645 100755 +index 3e75812..b068ea3 100755 --- a/utils/lexbor/lexbor/LXB.py +++ b/utils/lexbor/lexbor/LXB.py @@ -27,7 +27,7 @@ class Temp: @@ -111,5 +124,5 @@ index 2370c66..c41e645 100755 fh.close() -- -2.44.0 +2.49.0 diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 3c8d6e15cea20..3242529d8842c 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -101,6 +101,7 @@ static zend_object_handlers dom_modern_nodelist_object_handlers; static zend_object_handlers dom_html_collection_object_handlers; static zend_object_handlers dom_object_namespace_node_handlers; static zend_object_handlers dom_modern_domimplementation_object_handlers; +static zend_object_handlers dom_modern_element_object_handlers; static zend_object_handlers dom_token_list_object_handlers; #ifdef LIBXML_XPATH_ENABLED zend_object_handlers dom_xpath_object_handlers; @@ -669,6 +670,21 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */ } /* }}} */ +static zend_object *dom_modern_element_clone_obj(zend_object *zobject) +{ + zend_object *clone = dom_objects_store_clone_obj(zobject); + + /* The $classList property is unique per element, and cached due to its [[SameObject]] requirement. + * Remove it from the clone so the clone will get a fresh instance upon demand. */ + zval *class_list = dom_element_class_list_zval(php_dom_obj_from_obj(clone)); + if (!Z_ISUNDEF_P(class_list)) { + zval_ptr_dtor(class_list); + ZVAL_UNDEF(class_list); + } + + return clone; +} + static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject) { dom_object_namespace_node *intern = php_dom_namespace_node_obj_from_obj(zobject); @@ -778,6 +794,9 @@ PHP_MINIT_FUNCTION(dom) * one instance per parent object. */ dom_modern_domimplementation_object_handlers.clone_obj = NULL; + memcpy(&dom_modern_element_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); + dom_modern_element_object_handlers.clone_obj = dom_modern_element_clone_obj; + memcpy(&dom_nnodemap_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); dom_nnodemap_object_handlers.free_obj = dom_nnodemap_objects_free_storage; dom_nnodemap_object_handlers.read_dimension = dom_nodemap_read_dimension; @@ -1108,7 +1127,7 @@ PHP_MINIT_FUNCTION(dom) dom_modern_element_class_entry = register_class_Dom_Element(dom_modern_node_class_entry, dom_modern_parentnode_class_entry, dom_modern_childnode_class_entry); dom_modern_element_class_entry->create_object = dom_objects_new; - dom_modern_element_class_entry->default_object_handlers = &dom_object_handlers; + dom_modern_element_class_entry->default_object_handlers = &dom_modern_element_object_handlers; zend_hash_init(&dom_modern_element_prop_handlers, 0, NULL, NULL, true); DOM_REGISTER_PROP_HANDLER(&dom_modern_element_prop_handlers, "namespaceURI", dom_node_namespace_uri_read, NULL); @@ -1132,7 +1151,7 @@ PHP_MINIT_FUNCTION(dom) dom_html_element_class_entry = register_class_Dom_HTMLElement(dom_modern_element_class_entry); dom_html_element_class_entry->create_object = dom_objects_new; - dom_html_element_class_entry->default_object_handlers = &dom_object_handlers; + dom_html_element_class_entry->default_object_handlers = &dom_modern_element_object_handlers; zend_hash_add_new_ptr(&classes, dom_html_element_class_entry->name, &dom_modern_element_prop_handlers); dom_text_class_entry = register_class_DOMText(dom_characterdata_class_entry); diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index bb0d83676dca1..22c738b20e0f6 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -187,6 +187,7 @@ bool php_dom_create_nullable_object(xmlNodePtr obj, zval *return_value, dom_obje xmlNodePtr dom_clone_node(php_dom_libxml_ns_mapper *ns_mapper, xmlNodePtr node, xmlDocPtr doc, bool recursive); void dom_set_document_ref_pointers(xmlNodePtr node, php_libxml_ref_obj *document); void dom_set_document_ref_pointers_attr(xmlAttrPtr attr, php_libxml_ref_obj *document); +zval *dom_element_class_list_zval(dom_object *obj); typedef enum { DOM_LOAD_STRING = 0, diff --git a/ext/dom/tests/modern/token_list/gh18744.phpt b/ext/dom/tests/modern/token_list/gh18744.phpt new file mode 100644 index 0000000000000..a9109df789d8f --- /dev/null +++ b/ext/dom/tests/modern/token_list/gh18744.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-18744 (classList works not correctly if copy HTMLElement by clone keyword.) +--EXTENSIONS-- +dom +--FILE-- +createElement('div'); +$ele1->classList->add('foo'); +$ele2 = clone $ele1; +$ele2->classList->add('bar'); + +echo "Element1 class: " . $ele1->getAttribute('class'); +echo "\n"; +echo "Element2 class: " . $ele2->getAttribute('class'); +echo "\n"; + +var_dump($ele1->classList !== $ele2->classList); +// These comparisons are not pointless: they're getters and should not create new objects +var_dump($ele1->classList === $ele1->classList); +var_dump($ele2->classList === $ele2->classList); + +?> +--EXPECT-- +Element1 class: foo +Element2 class: foo bar +bool(true) +bool(true) +bool(true) diff --git a/ext/dom/xml_document.c b/ext/dom/xml_document.c index 2bd3d908d7093..4d941de0f0686 100644 --- a/ext/dom/xml_document.c +++ b/ext/dom/xml_document.c @@ -282,7 +282,7 @@ static zend_string *php_new_dom_dump_node_to_str_ex(xmlNodePtr node, int options } else { xmlCharEncCloseFunc(handler); } - (void) xmlSaveClose(ctxt); + status |= xmlSaveClose(ctxt); } if (UNEXPECTED(status < 0)) { @@ -319,7 +319,7 @@ zend_long php_new_dom_dump_node_to_file(const char *filename, xmlDocPtr doc, xml if (EXPECTED(ctxt != NULL)) { status = dom_xml_serialize(ctxt, out, node, format, false, get_private_data_from_node(node)); status |= xmlOutputBufferFlush(out); - (void) xmlSaveClose(ctxt); + status |= xmlSaveClose(ctxt); } size_t offset = php_stream_tell(stream); diff --git a/ext/enchant/tests/broker_dict_exists.phpt b/ext/enchant/tests/broker_dict_exists.phpt index c4ca5d91eb3bd..494d2a539b3cc 100644 --- a/ext/enchant/tests/broker_dict_exists.phpt +++ b/ext/enchant/tests/broker_dict_exists.phpt @@ -6,8 +6,7 @@ marcosptf - enchant --SKIPIF-- --FILE-- --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- --FILE-- --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- enchant --SKIPIF-- --FILE-- ")) die('skip libenchant v1 only'); ?> --FILE-- diff --git a/ext/enchant/tests/dict_add_to_personal.phpt b/ext/enchant/tests/dict_add_to_personal.phpt index 1fe6c1ff4a475..54d2be5e5d177 100644 --- a/ext/enchant/tests/dict_add_to_personal.phpt +++ b/ext/enchant/tests/dict_add_to_personal.phpt @@ -6,8 +6,7 @@ marcosptf - enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- --FILE-- enchant --SKIPIF-- ")) die('skip libenchant v1 only'); ?> --FILE-- diff --git a/ext/enchant/tests/invalidobj.phpt b/ext/enchant/tests/invalidobj.phpt index 8dbfc2e1f9a60..d9f60075754d9 100644 --- a/ext/enchant/tests/invalidobj.phpt +++ b/ext/enchant/tests/invalidobj.phpt @@ -2,10 +2,6 @@ invalid object raise exception() function --EXTENSIONS-- enchant ---SKIPIF-- - --FILE-- --FILE-- data_file.c */ -const unsigned char php_magic_database[8313][1024] = { +const unsigned char php_magic_database[8313][1024] ZEND_NONSTRING = { "\034\004\036\361\022\0\0\0m?\0\0\001\031\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=/\005\0\0\0\0\0\0\0\0\0\0\0\0\0/\0\0\0\0\0\0\0\0\0\0\0Brain Vision Data Exchange Marker File, Version\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision Marker file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/brainvision\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=.\005\0\0\0\0\0\0\0\0\0\0\0\0\0z\0\0\0\0\0\0\0\0\0\0\0FileId=TMSi PortiLab sample log file\n\015Version=\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/TMSiLOG\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/tmsilog\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=+\005\0\0\0\0\0\004\0\0\0\0\0\0\0}\0\0\0\0\0\0\0\0\0\0\0Synergy\0\0048\0049(&\0048\0048)&\0048\0048\0048&\0048\0048\0048\0\0028\0\0\0\002\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\001\0\0\0=\017\005\0\0\0\0\0?\0\0\0\0\0\0\0~\0\0\0\0\0\0\0\0\0\0\0CRawDataElement\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\002\0\0\0=\016\005\0\0\0\0\0U\0\0\0\0\0\0\0\177\0\0\0\0\0\0\0\0\0\0\0CRawDataBuffer\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/S", "YNERGY\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0biosig/synergy\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=+\005\0\0\0\0\0\0\0\0\0\0\0\0\0.\0\0\0\0\0\0\0\0\0\0\0Brain Vision V-Amp Data Header File Version\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision V-Amp file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=&\005\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0\0\0\0\0\0\0\0\0\0Brain Vision Data Exchange Header File\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Biosig/Brainvision data file\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0=&\005\0\0\0\0\0\0\0\0\0\0\0\0\0\026\007\0\0\0\0\0\0\0\0\0\0MetaView Service Assurance Expor", diff --git a/ext/fileinfo/libmagic/apprentice.c b/ext/fileinfo/libmagic/apprentice.c index a4ead7fbce6e6..d7640e74a9dbe 100644 --- a/ext/fileinfo/libmagic/apprentice.c +++ b/ext/fileinfo/libmagic/apprentice.c @@ -184,6 +184,12 @@ file_private struct { { NULL, 0, NULL } }; +#if __has_attribute(nonstring) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 15 +# define ZEND_NONSTRING __attribute__((nonstring)) +#else +# define ZEND_NONSTRING +#endif + #include "../data_file.c" #ifdef COMPILE_ONLY diff --git a/ext/hash/hash_xxhash.c b/ext/hash/hash_xxhash.c index 203f3c7d2d9eb..1c1315afd4b28 100644 --- a/ext/hash/hash_xxhash.c +++ b/ext/hash/hash_xxhash.c @@ -158,7 +158,7 @@ const php_hash_ops php_hash_xxh3_64_ops = { typedef XXH_errorcode (*xxh3_reset_with_secret_func_t)(XXH3_state_t*, const void*, size_t); typedef XXH_errorcode (*xxh3_reset_with_seed_func_t)(XXH3_state_t*, XXH64_hash_t); -zend_always_inline static void _PHP_XXH3_Init(PHP_XXH3_64_CTX *ctx, HashTable *args, +static void _PHP_XXH3_Init(PHP_XXH3_64_CTX *ctx, HashTable *args, xxh3_reset_with_seed_func_t func_init_seed, xxh3_reset_with_secret_func_t func_init_secret, const char* algo_name) { memset(&ctx->s, 0, sizeof ctx->s); diff --git a/ext/hash/xxhash/xxhash.h b/ext/hash/xxhash/xxhash.h index 097f887be9e7f..259371a3addd6 100644 --- a/ext/hash/xxhash/xxhash.h +++ b/ext/hash/xxhash/xxhash.h @@ -931,7 +931,7 @@ XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2); /******* Canonical representation *******/ typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; -XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); +static zend_always_inline void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash); XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src); @@ -5503,7 +5503,7 @@ XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2) /*====== Canonical representation ======*/ /*! @ingroup xxh3_family */ -XXH_PUBLIC_API void +static zend_always_inline void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp index 2a52b7e63be5c..a412ddcdffcde 100644 --- a/ext/intl/common/common_date.cpp +++ b/ext/intl/common/common_date.cpp @@ -118,6 +118,7 @@ U_CFUNC int intl_datetime_decompose(zval *z, double *millis, TimeZone **tz, ZVAL_STRING(&zfuncname, "getTimestamp"); if (call_user_function(NULL, z, &zfuncname, &retval, 0, NULL) != SUCCESS || Z_TYPE(retval) != IS_LONG) { + zval_ptr_dtor(&retval); spprintf(&message, 0, "%s: error calling ::getTimeStamp() on the " "object", func); intl_errors_set(err, U_INTERNAL_PROGRAM_ERROR, diff --git a/ext/intl/dateformat/dateformat_parse.c b/ext/intl/dateformat/dateformat_parse.c index 129e007107db7..2bdde08bcaced 100644 --- a/ext/intl/dateformat/dateformat_parse.c +++ b/ext/intl/dateformat/dateformat_parse.c @@ -185,12 +185,10 @@ PHP_METHOD(IntlDateFormatter, parseToCalendar) DATE_FORMAT_METHOD_FETCH_OBJECT; if (z_parse_pos) { - zval *z_parse_pos_tmp = z_parse_pos; - ZVAL_DEREF(z_parse_pos_tmp); - bool failed = false; - zend_long long_parse_pos = zval_try_get_long(z_parse_pos_tmp, &failed); + bool failed; + zend_long long_parse_pos = zval_try_get_long(z_parse_pos, &failed); if (failed) { - zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos_tmp)); + zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos)); RETURN_THROWS(); } if (ZEND_LONG_INT_OVFL(long_parse_pos)) { diff --git a/ext/intl/locale/locale_methods.c b/ext/intl/locale/locale_methods.c index b2891de9d4aca..ff6c82f98f660 100644 --- a/ext/intl/locale/locale_methods.c +++ b/ext/intl/locale/locale_methods.c @@ -1488,6 +1488,7 @@ static zend_string* lookup_loc_range(const char* loc_range, HashTable* hash_arr, zend_string_release_ex(can_loc_range, 0); } if(result == 0) { + efree(cur_loc_range); intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0); LOOKUP_CLEAN_RETURN(NULL); } diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index cf818e97d199a..528f7bc0e12f7 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -986,8 +986,6 @@ PHP_FUNCTION(ldap_connect) RETURN_FALSE; } - object_init_ex(return_value, ldap_link_ce); - ld = Z_LDAP_LINK_P(return_value); { int rc = LDAP_SUCCESS; @@ -1010,13 +1008,17 @@ PHP_FUNCTION(ldap_connect) /* ensure all pending TLS options are applied in a new context */ if (ldap_set_option(NULL, LDAP_OPT_X_TLS_NEWCTX, &val) != LDAP_OPT_SUCCESS) { - zval_ptr_dtor(return_value); + if (url != host) { + efree(url); + } php_error_docref(NULL, E_WARNING, "Could not create new security context"); RETURN_FALSE; } LDAPG(tls_newctx) = false; } #endif + object_init_ex(return_value, ldap_link_ce); + ld = Z_LDAP_LINK_P(return_value); #ifdef LDAP_API_FEATURE_X_OPENLDAP /* ldap_init() is deprecated, use ldap_initialize() instead. @@ -1029,6 +1031,9 @@ PHP_FUNCTION(ldap_connect) ldap = ldap_init(host, port); if (ldap == NULL) { zval_ptr_dtor(return_value); + if (url != host) { + efree(url); + } php_error_docref(NULL, E_WARNING, "Could not create session handle"); RETURN_FALSE; } diff --git a/ext/ldap/tests/ldap_connect_port_error.phpt b/ext/ldap/tests/ldap_connect_port_error.phpt new file mode 100644 index 0000000000000..55d41d395b13c --- /dev/null +++ b/ext/ldap/tests/ldap_connect_port_error.phpt @@ -0,0 +1,24 @@ +--TEST-- +ldap_connect() - Connection errors +--EXTENSIONS-- +ldap +--INI-- +error_reporting=E_ALL & ~E_DEPRECATED +--FILE-- +getMessage(), PHP_EOL; +} + +try { + ldap_connect("nope://$host", 0); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +ldap_connect(): Argument #2 ($port) must be between 1 and 65535 +ldap_connect(): Argument #2 ($port) must be between 1 and 65535 diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 5ad67d1244987..c637d2cebf6a4 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -1519,7 +1519,7 @@ static zend_string *php_libxml_default_dump_doc_to_str(xmlDocPtr doc, int option } long status = xmlSaveDoc(ctxt, doc); - (void) xmlSaveClose(ctxt); + status |= xmlSaveClose(ctxt); if (status < 0) { smart_str_free_ex(&str, false); return NULL; diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index e848f21615bca..ca5a8b8ec72e4 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -3930,7 +3930,7 @@ static uint32_t *make_conversion_map(HashTable *target_hash, size_t *conversion_ uint32_t *mapelm = convmap; ZEND_HASH_FOREACH_VAL(target_hash, hash_entry) { - bool failed = true; + bool failed; zend_long tmp = zval_try_get_long(hash_entry, &failed); if (failed) { efree(convmap); diff --git a/ext/mbstring/tests/mb_encode_numericentity_references.phpt b/ext/mbstring/tests/mb_encode_numericentity_references.phpt new file mode 100644 index 0000000000000..682be9b2cd1e2 --- /dev/null +++ b/ext/mbstring/tests/mb_encode_numericentity_references.phpt @@ -0,0 +1,12 @@ +--TEST-- +mb_encode_numericentity() reference handling +--EXTENSIONS-- +mbstring +--FILE-- + +--EXPECT-- +string(0) "" diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 108c2f663ad1a..692841be10e16 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -1463,6 +1463,7 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) if (rc == SQL_ERROR) { odbc_sql_error(result->conn_ptr, result->stmt, "SQLGetData"); efree(buf); + zval_ptr_dtor(return_value); RETURN_FALSE; } diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 82ae1adce44c5..08abd7048d6dd 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3806,6 +3806,11 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { val = &c->value; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + /* For deprecated constants, we need to flag the zval for recursion + * detection. Make sure the zval is separated out of shm. */ + if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED) { + ok = false; + } if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) { was_changed = changed = true; } else { diff --git a/ext/opcache/jit/ir/ir.c b/ext/opcache/jit/ir/ir.c index d9f7e3d0f7836..a9f55cc0e466c 100644 --- a/ext/opcache/jit/ir/ir.c +++ b/ext/opcache/jit/ir/ir.c @@ -918,7 +918,7 @@ static ir_ref _ir_fold_cse(ir_ctx *ctx, uint32_t opt, ir_ref op1, ir_ref op2, ir #define IR_FOLD_EMIT goto ir_fold_emit #define IR_FOLD_NEXT break -#include "ir_fold_hash.h" +#include #define IR_FOLD_RULE(x) ((x) >> 21) #define IR_FOLD_KEY(x) ((x) & 0x1fffff) @@ -1485,6 +1485,18 @@ void ir_update_op(ir_ctx *ctx, ir_ref ref, uint32_t idx, ir_ref new_val) void ir_array_grow(ir_array *a, uint32_t size) { IR_ASSERT(size > a->size); + if (size >= 256) { + size = IR_ALIGNED_SIZE(size, 256); + } else { + /* Use big enough power of 2 */ + size -= 1; + size |= (size >> 1); + size |= (size >> 2); + size |= (size >> 4); +// size |= (size >> 8); +// size |= (size >> 16); + size += 1; + } a->refs = ir_mem_realloc(a->refs, size * sizeof(ir_ref)); a->size = size; } @@ -1820,7 +1832,7 @@ int ir_mem_flush(void *ptr, size_t size) #else void *ir_mem_mmap(size_t size) { - int prot_flags = PROT_EXEC; + int prot_flags = PROT_EXEC; #if defined(__NetBSD__) prot_flags |= PROT_MPROTECT(PROT_READ|PROT_WRITE); #endif diff --git a/ext/opcache/jit/ir/ir.h b/ext/opcache/jit/ir/ir.h index 60c501d0bd470..ec5e57129c9ab 100644 --- a/ext/opcache/jit/ir/ir.h +++ b/ext/opcache/jit/ir/ir.h @@ -154,19 +154,27 @@ typedef enum _ir_type { } ir_type; #ifdef IR_64 -# define IR_SIZE_T IR_U64 -# define IR_SSIZE_T IR_I64 -# define IR_UINTPTR_T IR_U64 -# define IR_INTPTR_T IR_I64 -# define IR_C_UINTPTR IR_U64 -# define IR_C_INTPTR IR_I64 +# define IR_SIZE_T IR_U64 +# define IR_SSIZE_T IR_I64 +# define IR_UINTPTR_T IR_U64 +# define IR_INTPTR_T IR_I64 +# define IR_C_UINTPTR IR_U64 +# define IR_C_INTPTR IR_I64 +# define ir_const_size_t ir_const_u64 +# define ir_const_ssize_t ir_const_i64 +# define ir_const_uintptr_t ir_const_u64 +# define ir_const_intptr_t ir_const_i64 #else -# define IR_SIZE_T IR_U32 -# define IR_SSIZE_T IR_I32 -# define IR_UINTPTR_T IR_U32 -# define IR_INTPTR_T IR_I32 -# define IR_C_UINTPTR IR_U32 -# define IR_C_INTPTR IR_I32 +# define IR_SIZE_T IR_U32 +# define IR_SSIZE_T IR_I32 +# define IR_UINTPTR_T IR_U32 +# define IR_INTPTR_T IR_I32 +# define IR_C_UINTPTR IR_U32 +# define IR_C_INTPTR IR_I32 +# define ir_const_size_t ir_const_u32 +# define ir_const_ssize_t ir_const_i32 +# define ir_const_uintptr_t ir_const_u32 +# define ir_const_intptr_t ir_const_i32 #endif /* List of IR opcodes @@ -401,8 +409,10 @@ typedef int32_t ir_ref; #define IR_CONSTS_LIMIT_MIN (-(IR_TRUE - 1)) #define IR_INSNS_LIMIT_MIN (IR_UNUSED + 1) +/* ADDR_MEMBER is neccessary to workaround MSVC C preprocessor bug */ #ifndef IR_64 -# define ADDR_MEMBER uintptr_t addr; +# define ADDR_MEMBER uintptr_t addr; \ + void *ptr; #else # define ADDR_MEMBER #endif @@ -412,6 +422,7 @@ typedef union _ir_val { int64_t i64; #ifdef IR_64 uintptr_t addr; + void *ptr; #endif IR_STRUCT_LOHI( union { @@ -466,6 +477,7 @@ typedef struct _ir_insn { }, union { ir_ref op1; + ir_ref ref; ir_ref prev_const; } ); diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c index 16facae51b1f6..01532c8ea3e30 100644 --- a/ext/opcache/jit/ir/ir_cfg.c +++ b/ext/opcache/jit/ir/ir_cfg.c @@ -77,6 +77,51 @@ void ir_reset_cfg(ir_ctx *ctx) } } +static uint32_t IR_NEVER_INLINE ir_cfg_remove_dead_inputs(ir_ctx *ctx, uint32_t *_blocks, ir_block *blocks, uint32_t bb_count) +{ + uint32_t b, count = 0; + ir_block *bb = blocks + 1; + ir_insn *insn; + ir_ref i, j, n, *ops, input; + + for (b = 1; b <= bb_count; b++, bb++) { + bb->successors = count; + count += ctx->use_lists[bb->end].count; + bb->successors_count = 0; + bb->predecessors = count; + insn = &ctx->ir_base[bb->start]; + if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) { + n = insn->inputs_count; + ops = insn->ops; + for (i = 1, j = 1; i <= n; i++) { + input = ops[i]; + if (_blocks[input]) { + if (i != j) { + ops[j] = ops[i]; + } + j++; + } else if (input > 0) { + ir_use_list_remove_one(ctx, input, bb->start); + } + } + j--; + if (j != n) { + if (j == 1) { + insn->op = IR_BEGIN; + } + insn->inputs_count = j; + bb->predecessors_count = j; + j++; + for (;j <= n; j++) { + ops[j] = IR_UNUSED; + } + } + } + count += bb->predecessors_count; + } + return count; +} + int ir_build_cfg(ir_ctx *ctx) { ir_ref n, *p, ref, start, end; @@ -239,7 +284,10 @@ int ir_build_cfg(ir_ctx *ctx) bb++; } IR_BITSET_FOREACH_END(); bb_count = b - 1; - IR_ASSERT(count == edges_count * 2); + if (UNEXPECTED(count != edges_count * 2)) { + count = ir_cfg_remove_dead_inputs(ctx, _blocks, blocks, bb_count); + IR_ASSERT(count != edges_count * 2); + } ir_mem_free(bb_starts); /* Create an array of successor/predecessors control edges */ diff --git a/ext/opcache/jit/ir/ir_emit.c b/ext/opcache/jit/ir/ir_emit.c index 5cf44a51d0f48..c82655daf48db 100644 --- a/ext/opcache/jit/ir/ir_emit.c +++ b/ext/opcache/jit/ir/ir_emit.c @@ -415,9 +415,9 @@ static int ir_const_label(ir_ctx *ctx, ir_ref ref) } #if defined(IR_TARGET_X86) || defined(IR_TARGET_X64) -# include "ir_emit_x86.h" +# include #elif defined(IR_TARGET_AARCH64) -# include "ir_emit_aarch64.h" +# include #else # error "Unknown IR target" #endif diff --git a/ext/opcache/jit/ir/ir_fold.h b/ext/opcache/jit/ir/ir_fold.h index 78f3ca0c01e5f..88539e52ab085 100644 --- a/ext/opcache/jit/ir/ir_fold.h +++ b/ext/opcache/jit/ir/ir_fold.h @@ -486,10 +486,36 @@ IR_FOLD(MUL(C_FLOAT, C_FLOAT)) } IR_FOLD(DIV(C_U8, C_U8)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.u8 / op2_insn->val.u8); +} + IR_FOLD(DIV(C_U16, C_U16)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.u16 / op2_insn->val.u16); +} + IR_FOLD(DIV(C_U32, C_U32)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.u32 / op2_insn->val.u32); +} + IR_FOLD(DIV(C_U64, C_U64)) -IR_FOLD(DIV(C_ADDR, C_ADDR)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); if (op2_insn->val.u64 == 0) { @@ -499,9 +525,46 @@ IR_FOLD(DIV(C_ADDR, C_ADDR)) IR_FOLD_CONST_U(op1_insn->val.u64 / op2_insn->val.u64); } +IR_FOLD(DIV(C_ADDR, C_ADDR)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.u64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_U(op1_insn->val.addr / op2_insn->val.addr); +} + IR_FOLD(DIV(C_I8, C_I8)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.i64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_I(op1_insn->val.i8 / op2_insn->val.i8); +} + IR_FOLD(DIV(C_I16, C_I16)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.i64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_I(op1_insn->val.i16 / op2_insn->val.i16); +} + IR_FOLD(DIV(C_I32, C_I32)) +{ + IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); + if (op2_insn->val.i64 == 0) { + /* division by zero */ + IR_FOLD_EMIT; + } + IR_FOLD_CONST_I(op1_insn->val.i32 / op2_insn->val.i32); +} + IR_FOLD(DIV(C_I64, C_I64)) { IR_ASSERT(IR_OPT_TYPE(opt) == op1_insn->type); @@ -1135,6 +1198,7 @@ IR_FOLD(MAX(C_FLOAT, C_FLOAT)) IR_FOLD(SEXT(C_I8)) IR_FOLD(SEXT(C_U8)) IR_FOLD(SEXT(C_BOOL)) +IR_FOLD(SEXT(C_CHAR)) { IR_ASSERT(IR_IS_TYPE_INT(IR_OPT_TYPE(opt))); IR_ASSERT(ir_type_size[IR_OPT_TYPE(opt)] > ir_type_size[op1_insn->type]); @@ -1160,6 +1224,7 @@ IR_FOLD(SEXT(C_U32)) IR_FOLD(ZEXT(C_I8)) IR_FOLD(ZEXT(C_U8)) IR_FOLD(ZEXT(C_BOOL)) +IR_FOLD(ZEXT(C_CHAR)) { IR_ASSERT(IR_IS_TYPE_INT(IR_OPT_TYPE(opt))); IR_ASSERT(ir_type_size[IR_OPT_TYPE(opt)] > ir_type_size[op1_insn->type]); @@ -1195,12 +1260,14 @@ IR_FOLD(TRUNC(C_U64)) default: IR_ASSERT(0); case IR_I8: + case IR_CHAR: IR_FOLD_CONST_I(op1_insn->val.i8); case IR_I16: IR_FOLD_CONST_I(op1_insn->val.i16); case IR_I32: IR_FOLD_CONST_I(op1_insn->val.i32); case IR_U8: + case IR_BOOL: IR_FOLD_CONST_U(op1_insn->val.u8); case IR_U16: IR_FOLD_CONST_U(op1_insn->val.u16); @@ -1474,6 +1541,10 @@ IR_FOLD(EQ(SEXT, C_ADDR)) } else { ir_type type = ctx->ir_base[op1_insn->op1].type; + if (op1_insn->op == IR_ZEXT + && (op2_insn->val.u64 >> (ir_type_size[type] * 8)) != 0) { + IR_FOLD_NEXT; + } if (IR_IS_TYPE_SIGNED(type)) { switch (ir_type_size[type]) { case 1: val.i64 = op2_insn->val.i8; break; @@ -1493,6 +1564,7 @@ IR_FOLD(EQ(SEXT, C_ADDR)) op2 = ir_const(ctx, val, type); IR_FOLD_RESTART; } + IR_FOLD_NEXT; } @@ -1518,6 +1590,10 @@ IR_FOLD(NE(SEXT, C_ADDR)) } else { ir_type type = ctx->ir_base[op1_insn->op1].type; + if (op1_insn->op == IR_ZEXT + && (op2_insn->val.u64 >> (ir_type_size[type] * 8)) != 0) { + IR_FOLD_NEXT; + } if (IR_IS_TYPE_SIGNED(type)) { switch (ir_type_size[type]) { case 1: val.i64 = op2_insn->val.i8; break; diff --git a/ext/opcache/jit/ir/ir_private.h b/ext/opcache/jit/ir/ir_private.h index 9c69d6074defe..69a0101d24ee2 100644 --- a/ext/opcache/jit/ir/ir_private.h +++ b/ext/opcache/jit/ir/ir_private.h @@ -255,9 +255,7 @@ IR_ALWAYS_INLINE void ir_arena_free(ir_arena *arena) IR_ALWAYS_INLINE void* ir_arena_alloc(ir_arena **arena_ptr, size_t size) { ir_arena *arena = *arena_ptr; - char *ptr = arena->ptr; - - size = IR_ALIGNED_SIZE(size, 8); + char *ptr = (char*)IR_ALIGNED_SIZE((uintptr_t)arena->ptr, 8); if (EXPECTED(size <= (size_t)(arena->end - ptr))) { arena->ptr = ptr + size; @@ -283,7 +281,7 @@ IR_ALWAYS_INLINE void* ir_arena_checkpoint(ir_arena *arena) return arena->ptr; } -IR_ALWAYS_INLINE void ir_release(ir_arena **arena_ptr, void *checkpoint) +IR_ALWAYS_INLINE void ir_arena_release(ir_arena **arena_ptr, void *checkpoint) { ir_arena *arena = *arena_ptr; diff --git a/ext/opcache/jit/ir/ir_sccp.c b/ext/opcache/jit/ir/ir_sccp.c index 8480861f91fe7..2e006516df818 100644 --- a/ext/opcache/jit/ir/ir_sccp.c +++ b/ext/opcache/jit/ir/ir_sccp.c @@ -1994,10 +1994,16 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref if (use_insn->op >= IR_EQ && use_insn->op <= IR_UGT) { if (use_insn->op1 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op2].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op2, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } } else if (use_insn->op2 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op1].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op1, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } @@ -2027,10 +2033,16 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref if (use_insn->op >= IR_EQ && use_insn->op <= IR_UGT) { if (use_insn->op1 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op2].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op2, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } } else if (use_insn->op2 == phi_ref) { + if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op1].type)) { + return 0; + } if (ir_is_cheaper_ext(ctx, use_insn->op1, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } @@ -3570,11 +3582,12 @@ void ir_iter_opt(ir_ctx *ctx, ir_bitqueue *worklist) if (val_insn->type == insn->type) { ir_iter_replace_insn(ctx, i, val, worklist); } else { - IR_ASSERT(!IR_IS_CONST_REF(insn->op2)); - ir_use_list_remove_one(ctx, insn->op2, i); - if (ir_is_dead(ctx, insn->op2)) { - /* schedule DCE */ - ir_bitqueue_add(worklist, insn->op2); + if (!IR_IS_CONST_REF(insn->op2)) { + ir_use_list_remove_one(ctx, insn->op2, i); + if (ir_is_dead(ctx, insn->op2)) { + /* schedule DCE */ + ir_bitqueue_add(worklist, insn->op2); + } } if (!IR_IS_CONST_REF(val)) { ir_use_list_add(ctx, val, i); diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index d01a8c41359aa..76602c2b4bcf5 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -3236,7 +3236,7 @@ static void ir_emit_store_mem_int_const(ir_ctx *ctx, ir_type type, ir_mem mem, i val = (int64_t)(intptr_t)ir_sym_val(ctx, val_insn); } - if (sizeof(void*) == 4 || IR_IS_SIGNED_32BIT(val)) { + if (ir_type_size[val_insn->type] <= 4 || IR_IS_SIGNED_32BIT(val)) { if (is_arg && ir_type_size[type] < 4) { type = IR_U32; } @@ -3664,7 +3664,7 @@ static int32_t ir_fuse_imm(ir_ctx *ctx, ir_ref ref) IR_ASSERT(IR_IS_SIGNED_32BIT((intptr_t)addr)); return (int32_t)(intptr_t)addr; } else { - IR_ASSERT(IR_IS_SIGNED_32BIT(val_insn->val.i32)); + IR_ASSERT(ir_type_size[val_insn->type] == 4 || IR_IS_SIGNED_32BIT(val_insn->val.i64)); return val_insn->val.i32; } } diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 1f833ad8f19c5..74fad38ffee87 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3458,7 +3458,7 @@ static void zend_jit_setup(bool reattached) __asm__( "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); + : "=D" (ti)); tsrm_tls_offset = ti[1]; tsrm_tls_index = ti[0] * 8; #elif defined(__FreeBSD__) @@ -3466,7 +3466,7 @@ static void zend_jit_setup(bool reattached) __asm__( "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); + : "=D" (ti)); tsrm_tls_offset = ti[1]; /* Index is offset by 1 on FreeBSD (https://github.com/freebsd/freebsd-src/blob/bf56e8b9c8639ac4447d223b83cdc128107cc3cd/libexec/rtld-elf/rtld.c#L5260) */ tsrm_tls_index = (ti[0] + 1) * 8; @@ -3475,7 +3475,7 @@ static void zend_jit_setup(bool reattached) __asm__( "leaq _tsrm_ls_cache@tlsgd(%%rip), %0\n" - : "=a" (ti)); + : "=D" (ti)); tsrm_tls_offset = ti[1]; tsrm_tls_index = ti[0] * 16; #endif diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index de4f20c96d519..8a0d58d25113b 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -2443,6 +2443,7 @@ static X509_STORE *php_openssl_setup_verify(zval *calist, uint32_t arg_num) ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(calist), item) { zend_string *str = zval_try_get_string(item); if (UNEXPECTED(!str)) { + X509_STORE_free(store); return NULL; } diff --git a/ext/openssl/tests/bug74796.phpt b/ext/openssl/tests/bug74796.phpt new file mode 100644 index 0000000000000..b3f594d5e60f4 --- /dev/null +++ b/ext/openssl/tests/bug74796.phpt @@ -0,0 +1,178 @@ +--TEST-- +Bug #74796: TLS encryption fails behind HTTP proxy +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'SNI_server_certs' => [ + "cs.php.net" => __DIR__ . "/sni_server_cs.pem", + "uk.php.net" => __DIR__ . "/sni_server_uk.pem", + "us.php.net" => __DIR__ . "/sni_server_us.pem" + ] + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $serverFlags, $ctx); + phpt_notify_server_start($server); + + for ($i=0; $i < 3; $i++) { + $conn = stream_socket_accept($server, 3); + fwrite($conn, "HTTP/1.0 200 OK\r\n\r\nHello from server $i"); + fclose($conn); + } + + phpt_wait(); +CODE; + +$proxyCode = <<<'CODE' + function parse_sni_from_client_hello($data) { + $sni = null; + + if (strlen($data) < 5 || ord($data[0]) != 0x16) return null; + + $session_id_len = ord($data[43]); + $ptr = 44 + $session_id_len; + + // Cipher suites length + $cipher_suites_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2 + $cipher_suites_len; + + // Compression methods length + $compression_methods_len = ord($data[$ptr]); + $ptr += 1 + $compression_methods_len; + + // Extensions length + if ($ptr + 2 > strlen($data)) return null; + $extensions_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2; + + $extensions_end = $ptr + $extensions_len; + + while ($ptr + 4 <= $extensions_end) { + $ext_type = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ext_len = (ord($data[$ptr+2]) << 8) | ord($data[$ptr+3]); + $ptr += 4; + + if ($ext_type === 0x00) { // SNI extension + if ($ptr + 2 > strlen($data)) break; + $name_list_len = (ord($data[$ptr]) << 8) | ord($data[$ptr+1]); + $ptr += 2; + + if ($ptr + 3 > strlen($data)) break; + $name_type = ord($data[$ptr]); + $name_len = (ord($data[$ptr+1]) << 8) | ord($data[$ptr+2]); + $ptr += 3; + + if ($name_type === 0) { // host_name type + $sni = substr($data, $ptr, $name_len); + break; + } + } + + $ptr += $ext_len; + } + + return $sni; + } + + $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN; + $server = stream_socket_server("tcp://127.0.0.1:0", $errornum, $errorstr, $flags); + phpt_notify_server_start($server); + + for ($i=0; $i < 3; $i++) { + $upstream = stream_socket_client("tcp://{{ ADDR }}", $errornum, $errorstr, 30, STREAM_CLIENT_CONNECT); + stream_set_blocking($upstream, false); + + $conn = stream_socket_accept($server); + stream_set_blocking($conn, true); + + // reading CONNECT request headers + while (($line = fgets($conn)) !== false) { + if (rtrim($line) === '') break; // empty line means end of headers + } + + // successful CONNECT response + fwrite($conn, "HTTP/1.0 200 Connection established\r\n\r\n"); + + // tunnel data + stream_set_blocking($conn, false); + $firstRead = true; + while (!feof($conn) && !feof($upstream)) { + $clientData = fread($conn, 8192); + if ($clientData !== false && $clientData !== '') { + if ($firstRead) { + $sni = parse_sni_from_client_hello($clientData); + if ($sni !== null) { + file_put_contents(__DIR__ . "/bug74796_proxy_sni.log", $sni . "\n", FILE_APPEND); + } + $firstRead = false; + } + fwrite($upstream, $clientData); + } + + $serverData = fread($upstream, 8192); + if ($serverData !== false && $serverData !== '') { + fwrite($conn, $serverData); + } + } + fclose($conn); + fclose($upstream); + phpt_wait(); + } +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create([ + 'ssl' => [ + 'cafile' => __DIR__ . '/sni_server_ca.pem', + 'verify_peer' => true, + 'verify_peer_name' => true, + ], + "http" => [ + "proxy" => "tcp://{{ ADDR }}" + ], + ]); + + // servers + $hosts = ["cs.php.net", "uk.php.net", "us.php.net"]; + foreach ($hosts as $host) { + var_dump(file_get_contents("https://$host/", false, $clientCtx)); + var_dump(stream_context_get_options($clientCtx)['ssl']['peer_name'] ?? null); + phpt_notify('proxy'); + } + + echo file_get_contents(__DIR__ . "/bug74796_proxy_sni.log"); + + phpt_notify('server'); +CODE; + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, [ + 'server' => $serverCode, + 'proxy' => $proxyCode, +]); +?> +--CLEAN-- + +--EXPECT-- +string(19) "Hello from server 0" +NULL +string(19) "Hello from server 1" +NULL +string(19) "Hello from server 2" +NULL +cs.php.net +uk.php.net +us.php.net diff --git a/ext/openssl/tests/memory_leak_x509_store.phpt b/ext/openssl/tests/memory_leak_x509_store.phpt new file mode 100644 index 0000000000000..bc9b113602a33 --- /dev/null +++ b/ext/openssl/tests/memory_leak_x509_store.phpt @@ -0,0 +1,22 @@ +--TEST-- +Memory leak of X509_STORE in php_openssl_setup_verify() on failure +--EXTENSIONS-- +openssl +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +stop diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 89631960d5677..25a4de9386d44 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -874,8 +874,7 @@ static bool php_pcntl_set_user_signal_infos( zval *user_signal_no; ZEND_HASH_FOREACH_VAL(user_signals, user_signal_no) { - bool failed = true; - ZVAL_DEREF(user_signal_no); + bool failed; zend_long tmp = zval_try_get_long(user_signal_no, &failed); if (failed) { diff --git a/ext/pdo/pdo_sqlstate.c b/ext/pdo/pdo_sqlstate.c index 3085d69192e3b..8d3377e2c6aea 100644 --- a/ext/pdo/pdo_sqlstate.c +++ b/ext/pdo/pdo_sqlstate.c @@ -24,8 +24,14 @@ #include "php_pdo.h" #include "php_pdo_driver.h" +#if __has_attribute(nonstring) +# define ZEND_NONSTRING __attribute__((nonstring)) +#else +# define ZEND_NONSTRING +#endif + struct pdo_sqlstate_info { - const char state[5]; + const char state[5] ZEND_NONSTRING; const char *desc; }; diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index 939adbfb202a6..ea9d5c788f571 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -104,6 +104,7 @@ static int pdo_odbc_ucs22utf8(pdo_stmt_t *stmt, int is_unicode, zval *result) zend_string *str = zend_string_alloc(ret, 0); ret = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) Z_STRVAL_P(result), Z_STRLEN_P(result)/sizeof(WCHAR), ZSTR_VAL(str), ZSTR_LEN(str), NULL, NULL); if (ret == 0) { + zend_string_efree(str); return PDO_ODBC_CONV_FAIL; } diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index ff56d04049424..493ba3f36009d 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -346,6 +346,9 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v zend_call_known_fcc(&collation->callback, &retval, /* argc */ 2, zargs, /* named_params */ NULL); + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + if (!Z_ISUNDEF(retval)) { if (Z_TYPE(retval) != IS_LONG) { zend_string *func_name = get_active_function_or_method_name(); @@ -362,9 +365,6 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v } } - zval_ptr_dtor(&zargs[0]); - zval_ptr_dtor(&zargs[1]); - return ret; } diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt new file mode 100644 index 0000000000000..a9d17bb230d56 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation_wrong_callback.phpt @@ -0,0 +1,24 @@ +--TEST-- +Pdo\Sqlite::createCollation() memory leaks on wrong callback return type +--EXTENSIONS-- +pdo_sqlite +--FILE-- +exec("CREATE TABLE test (c string)"); +$db->exec("INSERT INTO test VALUES('youwontseeme')"); +$db->exec("INSERT INTO test VALUES('neither')"); +$db->createCollation('NAT', function($a, $b): string { return $a . $b; }); + +try { + $db->query("SELECT c FROM test ORDER BY c COLLATE NAT"); +} catch (\TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +PDO::query(): Return value of the callback must be of type int, string returned diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 75b987d6b917e..4e5020d8c0965 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3843,8 +3843,14 @@ static void php_pgsql_do_async(INTERNAL_FUNCTION_PARAMETERS, int entry_type) int rc; c = PQgetCancel(pgsql); + /* PQcancel + * The return value of PQcancel is 1 if the cancel request was successfully dispatched and 0 if not. + * If not, errbuf is filled with an explanatory error message. + * errbuf must be a char array of size errbufsize (the recommended size is 256 bytes). + * https://www.postgresql.org/docs/current/libpq-cancel.html#LIBPQ-PQCANCEL + */ RETVAL_LONG((rc = PQcancel(c, err, sizeof(err)))); - if (rc < 0) { + if (rc == 0) { zend_error(E_WARNING, "cannot cancel the query: %s", err); } while ((pgsql_result = PQgetResult(pgsql))) { diff --git a/ext/phar/phar.c b/ext/phar/phar.c index a4a9b7d2139d2..4d8710b1b7982 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1706,6 +1706,7 @@ static zend_result phar_open_from_fp(php_stream* fp, char *fname, size_t fname_l php_stream_filter_append(&temp->writefilters, filter); if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) { + php_stream_filter_remove(filter, 1); if (err) { php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6") @@ -1752,6 +1753,7 @@ static zend_result phar_open_from_fp(php_stream* fp, char *fname, size_t fname_l php_stream_filter_append(&temp->writefilters, filter); if (SUCCESS != php_stream_copy_to_stream_ex(fp, temp, PHP_STREAM_COPY_ALL, NULL)) { + php_stream_filter_remove(filter, 1); php_stream_close(temp); MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file") } @@ -2796,6 +2798,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa } entry->cfp = shared_cfp; if (!entry->cfp) { + php_stream_filter_free(filter); if (error) { spprintf(error, 0, "unable to create temporary file"); } @@ -2810,6 +2813,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa entry->header_offset = php_stream_tell(entry->cfp); php_stream_flush(file); if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) { + php_stream_filter_free(filter); if (must_close_old_file) { php_stream_close(oldfile); } @@ -2821,6 +2825,7 @@ void phar_flush_ex(phar_archive_data *phar, zend_string *user_stub, bool is_defa } php_stream_filter_append((&entry->cfp->writefilters), filter); if (SUCCESS != php_stream_copy_to_stream_ex(file, entry->cfp, entry->uncompressed_filesize, NULL)) { + php_stream_filter_remove(filter, 1); if (must_close_old_file) { php_stream_close(oldfile); } diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 17563f7fad04f..e701611cf98cf 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -404,7 +404,7 @@ static int phar_stream_seek(php_stream *stream, zend_off_t offset, int whence, z phar_entry_data *data = (phar_entry_data *)stream->abstract; phar_entry_info *entry; int res; - zend_off_t temp; + zend_ulong temp; if (data->internal_file->link) { entry = phar_get_link_source(data->internal_file); @@ -414,26 +414,28 @@ static int phar_stream_seek(php_stream *stream, zend_off_t offset, int whence, z switch (whence) { case SEEK_END : - temp = data->zero + entry->uncompressed_filesize + offset; + temp = (zend_ulong) data->zero + (zend_ulong) entry->uncompressed_filesize + (zend_ulong) offset; break; case SEEK_CUR : - temp = data->zero + data->position + offset; + temp = (zend_ulong) data->zero + (zend_ulong) data->position + (zend_ulong) offset; break; case SEEK_SET : - temp = data->zero + offset; + temp = (zend_ulong) data->zero + (zend_ulong) offset; break; default: temp = 0; } - if (temp > data->zero + (zend_off_t) entry->uncompressed_filesize) { - *newoffset = -1; + + zend_off_t temp_signed = (zend_off_t) temp; + if (temp_signed > data->zero + (zend_off_t) entry->uncompressed_filesize) { + *newoffset = -1; /* FIXME: this will invalidate the ZEND_ASSERT(stream->position >= 0); assertion in streams.c */ return -1; } - if (temp < data->zero) { - *newoffset = -1; + if (temp_signed < data->zero) { + *newoffset = -1; /* FIXME: this will invalidate the ZEND_ASSERT(stream->position >= 0); assertion in streams.c */ return -1; } - res = php_stream_seek(data->fp, temp, SEEK_SET); + res = php_stream_seek(data->fp, temp_signed, SEEK_SET); *newoffset = php_stream_tell(data->fp) - data->zero; data->position = *newoffset; return res; diff --git a/ext/phar/tests/gh18642.phpt b/ext/phar/tests/gh18642.phpt new file mode 100644 index 0000000000000..a6872f7a62d86 --- /dev/null +++ b/ext/phar/tests/gh18642.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-18642 (Signed integer overflow in ext/phar fseek) +--EXTENSIONS-- +phar +--INI-- +phar.require_hash=0 +--FILE-- +setInfoClass('MyFile'); +$f = $phar['a.php']; +var_dump($f->fseek(PHP_INT_MAX)); +var_dump($f->fseek(0)); +var_dump($f->fseek(PHP_INT_MIN, SEEK_END)); +var_dump($f->fseek(0, SEEK_SET)); +var_dump($f->fseek(1, SEEK_CUR)); +var_dump($f->fseek(PHP_INT_MAX, SEEK_CUR)); +?> +--EXPECT-- +int(-1) +int(0) +int(-1) +int(0) +int(0) +int(-1) diff --git a/ext/phar/zip.c b/ext/phar/zip.c index ad0e53a80c4b2..5c7cd08bd6314 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -674,6 +674,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alia } if (!entry.uncompressed_filesize || !actual_alias) { + php_stream_filter_remove(filter, 1); pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } @@ -706,6 +707,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alia } if (!entry.uncompressed_filesize || !actual_alias) { + php_stream_filter_remove(filter, 1); pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("unable to read in alias, truncated"); } @@ -999,6 +1001,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ entry->cfp = php_stream_fopen_tmpfile(); if (!entry->cfp) { + php_stream_filter_free(filter); spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } @@ -1006,6 +1009,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ php_stream_flush(efp); if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) { + php_stream_filter_free(filter); spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } @@ -1013,6 +1017,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ php_stream_filter_append((&entry->cfp->writefilters), filter); if (SUCCESS != php_stream_copy_to_stream_ex(efp, entry->cfp, entry->uncompressed_filesize, NULL)) { + php_stream_filter_remove(filter, 1); spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname); return ZEND_HASH_APPLY_STOP; } diff --git a/ext/random/engine_user.c b/ext/random/engine_user.c index e343bc91d4894..955ddebdb99a0 100644 --- a/ext/random/engine_user.c +++ b/ext/random/engine_user.c @@ -27,6 +27,7 @@ static php_random_result generate(void *state) uint64_t result = 0; size_t size; zval retval; + zend_string *zstr; zend_call_known_instance_method_with_0_params(s->generate_method, s->object, &retval); @@ -37,7 +38,14 @@ static php_random_result generate(void *state) }; } - size = Z_STRLEN(retval); + if (UNEXPECTED(Z_ISREF(retval))) { + zstr = Z_STR_P(Z_REFVAL(retval)); + } else { + zstr = Z_STR(retval); + } + + /* Store generated size in a state */ + size = ZSTR_LEN(zstr); /* Guard for over 64-bit results */ if (size > sizeof(uint64_t)) { @@ -47,10 +55,11 @@ static php_random_result generate(void *state) if (size > 0) { /* Endianness safe copy */ for (size_t i = 0; i < size; i++) { - result += ((uint64_t) (unsigned char) Z_STRVAL(retval)[i]) << (8 * i); + result += ((uint64_t) (unsigned char) ZSTR_VAL(zstr)[i]) << (8 * i); } } else { zend_throw_error(random_ce_Random_BrokenRandomEngineError, "A random engine must return a non-empty string"); + zval_ptr_dtor(&retval); return (php_random_result){ .size = sizeof(uint64_t), .result = 0, diff --git a/ext/random/tests/02_engine/user_reference_return.phpt b/ext/random/tests/02_engine/user_reference_return.phpt new file mode 100644 index 0000000000000..f54e65bed7559 --- /dev/null +++ b/ext/random/tests/02_engine/user_reference_return.phpt @@ -0,0 +1,25 @@ +--TEST-- +Random: Engine: User: Returning by reference works +--FILE-- +field; + } +} + +$randomizer = new Randomizer(new ReferenceEngine()); + +var_dump($randomizer->getBytes(64)); + +?> +--EXPECT-- +string(64) "abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcd" diff --git a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt index 6aec7180b1ee5..fe402b82b57b3 100644 --- a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt +++ b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt @@ -10,7 +10,8 @@ final class EmptyStringEngine implements Engine { public function generate(): string { - return ''; + // Create a non-interned empty string. + return preg_replace('/./s', '', random_bytes(4)); } } diff --git a/ext/readline/readline.c b/ext/readline/readline.c index 30f84d610022f..d292921856909 100644 --- a/ext/readline/readline.c +++ b/ext/readline/readline.c @@ -473,13 +473,14 @@ char **php_readline_completion_cb(const char *text, int start, int end) /* libedit will read matches[2] */ matches = calloc(3, sizeof(char *)); if (!matches) { - return NULL; + goto out; } matches[0] = strdup(""); } } } +out: zval_ptr_dtor(¶ms[0]); zval_ptr_dtor(&_readline_array); diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 28923c4cb3925..619f627e8532a 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -1404,7 +1404,8 @@ PHP_METHOD(SimpleXMLElement, asXML) if (!result) { RETURN_FALSE; } else { - RETURN_NEW_STR(result); + /* Defense-in-depth: don't use the NEW variant in case somehow an empty string gets returned */ + RETURN_STR(result); } } /* }}} */ diff --git a/ext/simplexml/tests/gh18597.phpt b/ext/simplexml/tests/gh18597.phpt new file mode 100644 index 0000000000000..e9176bf7ae041 --- /dev/null +++ b/ext/simplexml/tests/gh18597.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-18597 (Heap-buffer-overflow in zend_alloc.c when assigning string with UTF-8 bytes) +--EXTENSIONS-- +simplexml +--FILE-- +"); +$sx1->node[0] = 'node1'; +$node = $sx1->node[0]; + +$node[0] = '��c'; + +$sx1->asXML(); // Depends on the available system encodings whether this fails or not, point is, it should not crash +echo "Done\n"; +?> +--EXPECT-- +Done diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index a55780615f4d8..a1bd7dff0c8a5 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -411,6 +411,7 @@ int make_http_soap_request(zval *this_ptr, } else { zval_ptr_dtor(¶ms[0]); zval_ptr_dtor(&func); + zval_ptr_dtor(&retval); if (request != buf) { zend_string_release_ex(request, 0); } @@ -1329,6 +1330,7 @@ int make_http_soap_request(zval *this_ptr, } else { zval_ptr_dtor(¶ms[0]); zval_ptr_dtor(&func); + zval_ptr_dtor(&retval); efree(content_encoding); zend_string_release_ex(http_headers, 0); zend_string_release_ex(http_body, 0); diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c index e1bf37686ac1d..a4911c6598422 100644 --- a/ext/soap/php_schema.c +++ b/ext/soap/php_schema.c @@ -1877,7 +1877,7 @@ static int schema_attribute(sdlPtr sdl, xmlAttrPtr tns, xmlNodePtr attrType, sdl } else { xmlNsPtr nsPtr = attr_find_ns(attr); - if (strncmp((char*)nsPtr->href, SCHEMA_NAMESPACE, sizeof(SCHEMA_NAMESPACE))) { + if (nsPtr && strncmp((char*)nsPtr->href, SCHEMA_NAMESPACE, sizeof(SCHEMA_NAMESPACE))) { smart_str key2 = {0}; sdlExtraAttributePtr ext; xmlNsPtr nsptr; diff --git a/ext/sockets/conversions.c b/ext/sockets/conversions.c index 04dad88ac5915..60ed970e89672 100644 --- a/ext/sockets/conversions.c +++ b/ext/sockets/conversions.c @@ -1457,7 +1457,11 @@ void to_zval_read_fd_array(const char *data, zval *zv, res_context *ctx) object_init_ex(&elem, socket_ce); php_socket *sock = Z_SOCKET_P(&elem); - socket_import_file_descriptor(fd, sock); + if (!socket_import_file_descriptor(fd, sock)) { + do_to_zval_err(ctx, "error getting protocol descriptor %d: getsockopt() call failed with errno %d", fd, errno); + zval_ptr_dtor(&elem); + return; + } } else { php_stream *stream = php_stream_fopen_from_fd(fd, "rw", NULL); php_stream_to_zval(stream, &elem); diff --git a/ext/standard/crypt_sha256.c b/ext/standard/crypt_sha256.c index dbd1c62fd58c5..4b2ec80b5ccab 100644 --- a/ext/standard/crypt_sha256.c +++ b/ext/standard/crypt_sha256.c @@ -312,8 +312,14 @@ static const char sha256_rounds_prefix[] = "rounds="; /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 +#if __has_attribute(nonstring) +# define ZEND_NONSTRING __attribute__((nonstring)) +#else +# define ZEND_NONSTRING +#endif + /* Table with characters for base64 transformation. */ -static const char b64t[64] = +static const char b64t[64] ZEND_NONSTRING = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; char * php_sha256_crypt_r(const char *key, const char *salt, char *buffer, int buflen) diff --git a/ext/standard/crypt_sha512.c b/ext/standard/crypt_sha512.c index d854a15604f5d..5a6464b2d53e7 100644 --- a/ext/standard/crypt_sha512.c +++ b/ext/standard/crypt_sha512.c @@ -349,8 +349,14 @@ static const char sha512_rounds_prefix[] = "rounds="; /* Maximum number of rounds. */ #define ROUNDS_MAX 999999999 +#if __has_attribute(nonstring) +# define ZEND_NONSTRING __attribute__((nonstring)) +#else +# define ZEND_NONSTRING +#endif + /* Table with characters for base64 transformation. */ -static const char b64t[64] = +static const char b64t[64] ZEND_NONSTRING = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index ea06e9dff61ea..9fefe153622fc 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -498,12 +498,14 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (stream && use_proxy && use_ssl) { smart_str header = {0}; + bool reset_ssl_peer_name = false; /* Set peer_name or name verification will try to use the proxy server name */ if (!context || (tmpzval = php_stream_context_get_option(context, "ssl", "peer_name")) == NULL) { ZVAL_STR_COPY(&ssl_proxy_peer_name, resource->host); php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name", &ssl_proxy_peer_name); zval_ptr_dtor(&ssl_proxy_peer_name); + reset_ssl_peer_name = true; } smart_str_appendl(&header, "CONNECT ", sizeof("CONNECT ")-1); @@ -566,6 +568,10 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, stream = NULL; } } + + if (reset_ssl_peer_name) { + php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name"); + } } php_stream_http_response_header_info_init(&header_info); diff --git a/ext/standard/tests/file/copy_variation2-win32-mb.phpt b/ext/standard/tests/file/copy_variation2-win32-mb.phpt index 4251a24e54cf7..67d84ee3e32c1 100644 --- a/ext/standard/tests/file/copy_variation2-win32-mb.phpt +++ b/ext/standard/tests/file/copy_variation2-win32-mb.phpt @@ -22,24 +22,24 @@ fclose($file_handle); $dest_files = array( /* File names containing special(non-alpha numeric) characters */ - "_copy_variation2.tmp", - "@copy_variation2.tmp", - "#copy_variation2.tmp", - "+copy_variation2.tmp", - "?copy_variation2.tmp", - ">copy_variation2.tmp", - "!copy_variation2.tmp", - "©_variation2.tmp", - "(copy_variation2.tmp", - ":copy_variation2.tmp", - ";copy_variation2.tmp", - "=copy_variation2.tmp", - "[copy_variation2.tmp", - "^copy_variation2.tmp", - "{copy_variation2.tmp", - "|copy_variation2.tmp", - "~copy_variation2.tmp", - "\$copy_variation2.tmp" + "_copy_variation2_mb.tmp", + "@copy_variation2_mb.tmp", + "#copy_variation2_mb.tmp", + "+copy_variation2_mb.tmp", + "?copy_variation2_mb.tmp", + ">copy_variation2_mb.tmp", + "!copy_variation2_mb.tmp", + "©_variation2_mb.tmp", + "(copy_variation2_mb.tmp", + ":copy_variation2_mb.tmp", + ";copy_variation2_mb.tmp", + "=copy_variation2_mb.tmp", + "[copy_variation2_mb.tmp", + "^copy_variation2_mb.tmp", + "{copy_variation2_mb.tmp", + "|copy_variation2_mb.tmp", + "~copy_variation2_mb.tmp", + "\$copy_variation2_mb.tmp" ); echo "Size of the source file before copy operation => "; @@ -90,28 +90,28 @@ Size of the source file before copy operation => int(1500) -- Iteration 1 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/_copy_variation2.tmp +Destination file name => %s/_copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 2 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/@copy_variation2.tmp +Destination file name => %s/@copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 3 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/#copy_variation2.tmp +Destination file name => %s/#copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 4 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/+copy_variation2.tmp +Destination file name => %s/+copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) @@ -130,21 +130,21 @@ Existence of destination file => bool(false) -- Iteration 7 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/!copy_variation2.tmp +Destination file name => %s/!copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 8 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/©_variation2.tmp +Destination file name => %s/©_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 9 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/(copy_variation2.tmp +Destination file name => %s/(copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) @@ -157,35 +157,35 @@ Existence of destination file => bool(false) -- Iteration 11 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/;copy_variation2.tmp +Destination file name => %s/;copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 12 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/=copy_variation2.tmp +Destination file name => %s/=copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 13 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/[copy_variation2.tmp +Destination file name => %s/[copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 14 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/^copy_variation2.tmp +Destination file name => %s/^copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 15 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/{copy_variation2.tmp +Destination file name => %s/{copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) @@ -198,14 +198,14 @@ Existence of destination file => bool(false) -- Iteration 17 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/~copy_variation2.tmp +Destination file name => %s/~copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 18 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/$copy_variation2.tmp +Destination file name => %s/$copy_variation2_mb.tmp Size of source file => int(1500) Size of destination file => int(1500) *** Done *** diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt index 37a47df060a1c..f3302d77c1d63 100644 --- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt +++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt @@ -44,7 +44,7 @@ array(6) { [0]=> string(14) "GET / HTTP/1.1" [1]=> - string(21) "Host: 127.0.0.1:%d" + string(%d) "Host: 127.0.0.1:%d" [2]=> string(17) "Connection: close" [3]=> diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt index 6c84679ff63bd..30d20f855419c 100644 --- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt +++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt @@ -44,7 +44,7 @@ array(7) { [0]=> string(14) "GET / HTTP/1.1" [1]=> - string(21) "Host: 127.0.0.1:%d" + string(%d) "Host: 127.0.0.1:%d" [2]=> string(17) "Connection: close" [3]=> diff --git a/ext/tidy/config.m4 b/ext/tidy/config.m4 index 8c3ceb38dafd7..bb85236346593 100644 --- a/ext/tidy/config.m4 +++ b/ext/tidy/config.m4 @@ -57,6 +57,12 @@ if test "$PHP_TIDY" != "no"; then [], [-L$TIDY_LIBDIR]) + PHP_CHECK_LIBRARY([$TIDY_LIB_NAME], [tidyOptGetCategory], + [AC_DEFINE([HAVE_TIDYOPTGETCATEGORY], [1], + [Define to 1 if Tidy library has the 'tidyOptGetCategory' function.])], + [], + [-L$TIDY_LIBDIR]) + PHP_ADD_LIBRARY_WITH_PATH([$TIDY_LIB_NAME], [$TIDY_LIBDIR], [TIDY_SHARED_LIBADD]) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index c5efe7a5b242d..ab991b2f8b7d4 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -234,7 +234,11 @@ static int _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value) return FAILURE; } +#if defined(HAVE_TIDYOPTGETCATEGORY) + if (tidyOptGetCategory(opt) == TidyInternalCategory) { +#else if (tidyOptIsReadOnly(opt)) { +#endif php_error_docref(NULL, E_WARNING, "Attempting to set read-only option \"%s\"", optname); return FAILURE; } @@ -961,6 +965,11 @@ static zend_result php_tidy_output_handler(void **nothing, php_output_context *o TidyBuffer inbuf, outbuf, errbuf; if (TG(clean_output) && (output_context->op & PHP_OUTPUT_HANDLER_START) && (output_context->op & PHP_OUTPUT_HANDLER_FINAL)) { + if (ZEND_SIZE_T_UINT_OVFL(output_context->in.used)) { + php_error_docref(NULL, E_WARNING, "Input string is too long"); + return status; + } + doc = tidyCreate(); tidyBufInit(&errbuf); @@ -968,11 +977,6 @@ static zend_result php_tidy_output_handler(void **nothing, php_output_context *o tidyOptSetBool(doc, TidyForceOutput, yes); tidyOptSetBool(doc, TidyMark, no); - if (ZEND_SIZE_T_UINT_OVFL(output_context->in.used)) { - php_error_docref(NULL, E_WARNING, "File content is too long"); - return status; - } - TIDY_SET_DEFAULT_CONFIG(doc); tidyBufInit(&inbuf); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index d52b8aa0cd172..62ff2526ea36c 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1576,3 +1576,13 @@ static PHP_FUNCTION(zend_test_create_throwing_resource) zend_resource *res = zend_register_resource(NULL, le_throwing_resource); ZVAL_RES(return_value, res); } + +static PHP_FUNCTION(zend_test_gh18756) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_mm_heap *heap = zend_mm_startup(); + zend_mm_gc(heap); + zend_mm_gc(heap); + zend_mm_shutdown(heap, true, false); +} diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 213c1b3f57476..fe1083ee942cc 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -316,6 +316,8 @@ function zend_test_cast_fread($stream): void {} function zend_test_is_zend_ptr(int $addr): bool {} function zend_test_log_err_debug(string $str): void {} + + function zend_test_gh18756(): void {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 9888ba39c14e4..00d45739ca119 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: 20a58913caf419fb60a3bc9cf275db605e2c3b0f */ + * Stub hash: e6374018fbb4fa793905bd5cb34e5a56b9e310fe */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -169,6 +169,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_log_err_debug, 0, 1, I ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) ZEND_END_ARG_INFO() +#define arginfo_zend_test_gh18756 arginfo_zend_test_void_return + #define arginfo_ZendTestNS2_namespaced_func arginfo_zend_test_is_pcre_bundled #define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_void_return @@ -303,6 +305,7 @@ static ZEND_FUNCTION(zend_test_set_fmode); static ZEND_FUNCTION(zend_test_cast_fread); static ZEND_FUNCTION(zend_test_is_zend_ptr); static ZEND_FUNCTION(zend_test_log_err_debug); +static ZEND_FUNCTION(zend_test_gh18756); static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); @@ -408,6 +411,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_test_cast_fread, arginfo_zend_test_cast_fread) ZEND_FE(zend_test_is_zend_ptr, arginfo_zend_test_is_zend_ptr) ZEND_FE(zend_test_log_err_debug, arginfo_zend_test_log_err_debug) + ZEND_FE(zend_test_gh18756, arginfo_zend_test_gh18756) #if (PHP_VERSION_ID >= 80400) ZEND_RAW_FENTRY(ZEND_NS_NAME("ZendTestNS2", "namespaced_func"), zif_ZendTestNS2_namespaced_func, arginfo_ZendTestNS2_namespaced_func, 0, NULL, NULL) #else diff --git a/main/network.c b/main/network.c index 06b637b878912..14f4ca4dff987 100644 --- a/main/network.c +++ b/main/network.c @@ -229,7 +229,7 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka for (n = 1; (sai = sai->ai_next) != NULL; n++) ; - *sal = safe_emalloc((n + 1), sizeof(*sal), 0); + *sal = safe_emalloc((n + 1), sizeof(**sal), 0); sai = res; sap = *sal; @@ -264,7 +264,7 @@ PHPAPI int php_network_getaddresses(const char *host, int socktype, struct socka in = *((struct in_addr *) host_info->h_addr); } - *sal = safe_emalloc(2, sizeof(*sal), 0); + *sal = safe_emalloc(2, sizeof(**sal), 0); sap = *sal; *sap = emalloc(sizeof(struct sockaddr_in)); (*sap)->sa_family = AF_INET; diff --git a/main/php_version.h b/main/php_version.h index 9ac406dfc3208..2a0421bae2ec3 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 4 -#define PHP_RELEASE_VERSION 8 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.8-dev" -#define PHP_VERSION_ID 80408 +#define PHP_RELEASE_VERSION 9 +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.4.9" +#define PHP_VERSION_ID 80409 diff --git a/main/streams/php_stream_context.h b/main/streams/php_stream_context.h index d4ebe29bc162e..56a1f53747116 100644 --- a/main/streams/php_stream_context.h +++ b/main/streams/php_stream_context.h @@ -59,7 +59,8 @@ PHPAPI zval *php_stream_context_get_option(php_stream_context *context, const char *wrappername, const char *optionname); PHPAPI void php_stream_context_set_option(php_stream_context *context, const char *wrappername, const char *optionname, zval *optionvalue); - +void php_stream_context_unset_option(php_stream_context *context, + const char *wrappername, const char *optionname); PHPAPI php_stream_notifier *php_stream_notification_alloc(void); PHPAPI void php_stream_notification_free(php_stream_notifier *notifier); END_EXTERN_C() diff --git a/main/streams/streams.c b/main/streams/streams.c index 661a0da9c883c..e951d455ccd3a 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -2432,6 +2432,20 @@ PHPAPI void php_stream_context_set_option(php_stream_context *context, SEPARATE_ARRAY(wrapperhash); zend_hash_str_update(Z_ARRVAL_P(wrapperhash), optionname, strlen(optionname), optionvalue); } + +void php_stream_context_unset_option(php_stream_context *context, + const char *wrappername, const char *optionname) +{ + zval *wrapperhash; + + wrapperhash = zend_hash_str_find(Z_ARRVAL(context->options), wrappername, strlen(wrappername)); + if (NULL == wrapperhash) { + return; + } + SEPARATE_ARRAY(&context->options); + SEPARATE_ARRAY(wrapperhash); + zend_hash_str_del(Z_ARRVAL_P(wrapperhash), optionname, strlen(optionname)); +} /* }}} */ /* {{{ php_stream_dirent_alphasort */ diff --git a/sapi/cli/tests/bug80092.phpt b/sapi/cli/tests/bug80092.phpt index 350b46b3f57a6..1fb2e8664cc1e 100644 --- a/sapi/cli/tests/bug80092.phpt +++ b/sapi/cli/tests/bug80092.phpt @@ -43,5 +43,5 @@ foreach (explode("\n", $output) as $line) { preloaded PHP %s Copyright (c) The PHP Group -Zend Engine %s +%AZend Engine %s %A with Zend OPcache %a diff --git a/sapi/fpm/fpm/fpm_request.c b/sapi/fpm/fpm/fpm_request.c index f07c6979f7064..4ec24c8084563 100644 --- a/sapi/fpm/fpm/fpm_request.c +++ b/sapi/fpm/fpm/fpm_request.c @@ -22,6 +22,7 @@ #include "zlog.h" static const char *requests_stages[] = { + [FPM_REQUEST_CREATING] = "Creating", [FPM_REQUEST_ACCEPTING] = "Idle", [FPM_REQUEST_READING_HEADERS] = "Reading headers", [FPM_REQUEST_INFO] = "Getting request information", diff --git a/sapi/fpm/fpm/fpm_request.h b/sapi/fpm/fpm/fpm_request.h index c1cde0111be47..1dcc7f78902fc 100644 --- a/sapi/fpm/fpm/fpm_request.h +++ b/sapi/fpm/fpm/fpm_request.h @@ -25,7 +25,8 @@ const char *fpm_request_get_stage_name(int stage); int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv); enum fpm_request_stage_e { - FPM_REQUEST_ACCEPTING = 1, + FPM_REQUEST_CREATING, + FPM_REQUEST_ACCEPTING, FPM_REQUEST_READING_HEADERS, FPM_REQUEST_INFO, FPM_REQUEST_EXECUTING, diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index c4b3aa4cb374b..49a3d00ffb29d 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -180,12 +180,6 @@ static PHP_MSHUTDOWN_FUNCTION(phpdbg) /* {{{ */ phpdbg_notice("Script ended normally"); } - /* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */ - if (use_mm_wrappers) { - /* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */ - *(int *) zend_mm_get_heap() = 0; - } - if (PHPDBG_G(buffer)) { free(PHPDBG_G(buffer)); PHPDBG_G(buffer) = NULL;