diff --git a/.gdbinit b/.gdbinit index d3b456239e375..c4705b2f59a95 100644 --- a/.gdbinit +++ b/.gdbinit @@ -66,18 +66,18 @@ define dump_bt if $func if $ex->This->value.obj if $func->common.scope - printf "%s->", $func->common.scope->name->val + printf "%s->", (char*)$func->common.scope->name->val else - printf "%s->", $ex->This->value.obj->ce.name->val + printf "%s->", (char*)$ex->This->value.obj->ce.name->val end else if $func->common.scope - printf "%s::", $func->common.scope->name->val + printf "%s::", (char*)$func->common.scope->name->val end end if $func->common.function_name - printf "%s(", $func->common.function_name->val + printf "%s(", (char*)$func->common.function_name->val else printf "(main" end @@ -109,7 +109,7 @@ define dump_bt printf "%f", $zvalue->value.dval end if $type == 6 - ____print_str $zvalue->value.str->val $zvalue->value.str->len + ____print_str (char*)$zvalue->value.str->val $zvalue->value.str->len end if $type == 7 printf "array(%d)[%p]", $zvalue->value.arr->nNumOfElements, $zvalue @@ -135,7 +135,7 @@ define dump_bt end if $func != 0 if $func->type == 2 - printf "%s:%d ", $func->op_array.filename->val, $ex->opline->lineno + printf "%s:%d ", (char*)$func->op_array.filename->val, $ex->opline->lineno else printf "[internal function]" end @@ -186,7 +186,7 @@ define ____printzv_contents printf "double: %f", $zvalue->value.dval end if $type == 6 - printf "string: %s", $zvalue->value.str->val + printf "string: %s", (char*)$zvalue->value.str->val end if $type == 7 printf "array: " @@ -208,7 +208,7 @@ define ____printzv_contents set $handle = $zvalue->value.obj.handle set $handlers = $zvalue->value.obj.handlers set $zobj = $zvalue->value.obj - set $cname = $zobj->ce->name->val + set $cname = (char*)$zobj->ce->name->val printf "(%s) #%d", $cname, $handle if ! $arg1 if $handlers->get_properties == &zend_std_get_properties @@ -233,7 +233,7 @@ define ____printzv_contents set $name = $p->key set $prop = (zend_property_info*)$p->val.value.ptr set $val = (zval*)((char*)$zobj + $prop->offset) - printf "%s => ", $name->val + printf "%s => ", (char*)$name->val printzv $val set $k = $k + 1 end @@ -348,7 +348,7 @@ define ____print_ht end printf "[%d] ", $i if $key - ____print_str $key->val $key->len + ____print_str (char*)$key->val $key->len printf " => " else printf "%d => ", $h @@ -365,7 +365,7 @@ define ____print_ht end if $arg1 == 3 set $func = (zend_function*)$val->value.ptr - printf "\"%s\"\n", $func->common.function_name->val + printf "\"%s\"\n", (char*)$func->common.function_name->val end if $arg1 == 4 set $const = (zend_constant *)$val->value.ptr @@ -423,15 +423,15 @@ define ____print_inh_class printf "final " end end - printf "class %s", $ce->name->val + printf "class %s", (char*)$ce->name->val if $ce->parent != 0 - printf " extends %s", $ce->parent->name->val + printf " extends %s", (char*)$ce->parent->name->val end if $ce->num_interfaces != 0 printf " implements" set $tmp = 0 while $tmp < $ce->num_interfaces - printf " %s", $ce->interfaces[$tmp]->name->val + printf " %s", (char*)$ce->interfaces[$tmp]->name->val set $tmp = $tmp + 1 if $tmp < $ce->num_interfaces printf "," @@ -443,10 +443,10 @@ end define ____print_inh_iface set $ce = $arg0 - printf "interface %s", $ce->name->val + printf "interface %s", (char*)$ce->name->val if $ce->num_interfaces != 0 set $ce = $ce->interfaces[0] - printf " extends %s", $ce->name->val + printf " extends %s", (char*)$ce->name->val else set $ce = 0 end @@ -486,7 +486,7 @@ define print_pi set $ptr_to_val = (zval*)((char*)$pi->ce->default_properties_table + $pi->offset - $initial_offset) printf "[%p] {\n", $pi printf " offset = %p\n", $pi->offset - printf " ce = [%p] %s\n", $pi->ce, $pi->ce->name->val + printf " ce = [%p] %s\n", $pi->ce, (char*)$pi->ce->name->val printf " flags = 0x%x (", $pi->flags if $pi->flags & 0x100 printf "ZEND_ACC_PUBLIC" @@ -608,7 +608,7 @@ define print_zstr set $maxlen = $zstr->len end printf "string(%d) ", $zstr->len - ____print_str $zstr->val $zstr->len $maxlen + ____print_str (char*)$zstr->val $zstr->len $maxlen printf "\n" end diff --git a/LICENSE b/LICENSE index 47a594e38a4d2..0815d7eb79119 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -------------------------------------------------------------------- The PHP License, version 3.01 -Copyright (c) 1999 - 2023 The PHP Group. All rights reserved. +Copyright (c) 1999 - 2024 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without diff --git a/NEWS b/NEWS index 70829cbc981a5..dfa7a44b644ad 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,70 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.2 +15 Feb 2024, PHP 8.3.3 + +- Core: + . Fixed timer leak in zend-max-execution-timers builds. (withinboredom) + . Fixed bug GH-12349 (linking failure on ARM with mold). (Jan Palus) + . Fixed bug GH-13097 (Anonymous class reference in trigger_error / thrown + Exception). (nielsdos) + . Fixed bug GH-13177 (PHP 8.3.2: final private constructor not allowed + when used in trait). (nielsdos) + . Fixed bug GH-13215 (GCC 14 build failure). (Remi) + +- Curl: + . Fix missing error check in curl_multi_init(). (divinity76) + +- FPM: + . Fixed bug GH-12996 (Incorrect SCRIPT_NAME with Apache ProxyPassMatch when + plus in path). (Jakub Zelenka) + +- GD: + . Fixed bug GH-10344 (imagettfbbox(): Could not find/open font UNC path). + (nielsdos) + . Fixed bug GH-10614 (imagerotate will turn the picture all black, when + rotated 90). (nielsdos) + +- LibXML: + . Fix crashes with entity references and predefined entities. (nielsdos) + +- MySQLnd: + . Fixed bug GH-12107 (When running a stored procedure (that returns a result + set) twice, PHP crashes). (nielsdos) + +- Opcache: + . Fixed bug GH-13145 (strtok() is not comptime). (ilutov) + . Fixed type inference of range(). (ilutov) + . Fixed bug GH-13232 (Segmentation fault will be reported when JIT is off but + JIT_debug is still on). (nielsdos) + +- OpenSSL: + . Fixed LibreSSL undefined reference when OPENSSL_NO_ENGINE not set. + (David Carlier). + +- PDO_Firebird: + . Fix GH-13119 (Changed to convert float and double values ​​into strings using + `H` format). (SakiTakamachi) + +- Phar: + . Fixed bug #71465 (PHAR doesn't know about litespeed). (nielsdos) + . Fixed bug GH-13037 (PharData incorrectly extracts zip file). (nielsdos) + +- Random: + . Fixed bug GH-13138 (Randomizer::pickArrayKeys() does not detect broken + engines). (timwolla) + +- Session: + . Fixed bug GH-12504 (Corrupted session written when there's a fatal error + in autoloader). (nielsdos) + +- Standard: + . Fixed bug GH-13094 (range(9.9, '0') causes segmentation fault). (nielsdos) + +- Streams: + . Fixed bug GH-13071 (Copying large files using mmap-able source streams may + exhaust available memory and fail). (nielsdos) + +18 Jan 2024, PHP 8.3.2 - Core: . Fixed bug GH-12953 (false positive SSA integrity verification failed when @@ -29,6 +93,10 @@ PHP NEWS (Jakub Zelenka) . Fixed bug GH-12905 (FFI::new interacts badly with observers). (nielsdos) +- GD: + . Fixed GH-13082 undefined behavior with GdFont instances handling with + imageload* and imagechar*. (David Carlier) + - Intl: . Fixed GH-12943 (IntlDateFormatter::__construct accepts 'C' as valid locale). (David Carlier) @@ -37,6 +105,11 @@ PHP NEWS . Fixed bug GH-12936 (hash() function hangs endlessly if using sha512 on strings >= 4GiB). (nielsdos) +- MBString: + . When operating on a string with invalid encoding, mb_substr (as well + as mb_strstr and its variants) defines character indices in the same + way as other mbstring functions such as mb_strpos. (Alex Dowad) + - ODBC: . Fix crash on Apache shutdown with persistent connections. (nielsdos) diff --git a/SECURITY.md b/SECURITY.md index 161e0810b5862..deb5a7a950a4d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,127 +1,4 @@ -# Vulnerability Disclosure Policy - -*This document was originally published at .* - -## Introduction - -For the sake of our users, we classify some of the issues found in PHP -as "security issues". This document is intended to explain which issues -are thus classified, how we handle those issues and how to report them. - -## Classification - -We classify as security issues bugs that: - -- allow users to execute unauthorized actions -- cross security boundaries -- access data that is not intended to be accessible -- severely impact accessibility or performance of the system - -The purpose of this classification is to alert the users and the -developers about the bugs that need to be prioritized in their handling. - -We define three categories of security issues, by their severity, -described below. Please note that this categorization is in many aspects -subjective, so it ultimately relies on the judgement of the PHP -developers. - -### High severity - -These issues may allow: - -- third party to compromise any, or most installations of PHP -- the execution of arbitrary code -- disabling the system completely -- access to any file a local PHP user can access. - -The issue can be triggered on any, or on most typical installations, and -does not require exotic and non-recommended settings to be triggered. - -This category also includes issues that can be triggered in code or -functions known to be frequently used (session, json, mysql, openssl, -etc.) during typical usage, and that require settings or configurations -that may not be strictly the best practice, but are commonly used. - -This category also may include issues that require special code or code -pattern if such code or pattern is present in many popular libraries. - -This kind of issues usually requires a CVE report. - -### Medium severity - -These issues may have the same potential to compromise an installation -as a high severity issue, but may also require: - -- an extension that is not commonly used -- a particular type of configuration that is used only in narrow - specific circumstances -- relies on old version of a third-party library being used -- code, or patterns of code, that are known to be used infrequently -- code that is very old, or extremely uncommon (and so is used - infrequently) - -This kind of issues usually will have a CVE number, unless the required -configuration is particularly exotic to the point it's not practically -usable. - -### Low severity - -This issue allows theoretical compromise of security, but practical -attack is usually impossible or extremely hard due to common practices -or limitations that are virtually always present or imposed. - -This also includes problems with configuration, documentation, and other -non-code parts of the PHP project that may mislead users, or cause them -to make their system, or their code less secure. - -Issues that can trigger unauthorized actions that do not seem to be -useful for any practical attack can also be categorized as low severity. - -Security issues, that are present only in unstable branches, belong to -this category, too. Any branch that has no stable release, is per se not -intended for the production use. - -Low severity issues usually do not need to have CVE and may, at the -discretion of the PHP developers, be disclosed publicly before the fix -is released or available. - -### Not a security issue - -We do not classify as a security issue any issue that: - -- requires invocation of specific code, which may be valid but is - obviously malicious -- requires invocation of functions with specific arguments, which may - be valid but are obviously malicious -- requires specific actions to be performed on the server, which are - not commonly performed, or are not commonly permissible for the user - (uid) executing PHP -- requires privileges superior to that of the user (uid) executing PHP -- requires the use of debugging facilities - ex. xdebug, var_dump -- requires the use of settings not recommended for production - ex. - error reporting to output -- requires the use of non-standard environment variables - ex. - USE_ZEND_ALLOC -- requires the use of non-standard builds - ex. obscure embedded - platform, not commonly used compiler -- requires the use of code or settings known to be insecure -- requires the use of FFI -- requires an open_basedir bypass - -## Handling issues - -High and medium severity fixes are merged into a private security repository, -and then merged to the main repository before the release is tagged. - -Low severity fixes are merged immediately after the fix is available and -handled like all regular bugs are handled consequently. However, release -managers may choose to pull those fixes into the RC branch after the -branch is created, and also backport them into a security-only release -branch. - -## FAQ - -### How do I report a security issue? +# Reporting Security Issues Please report security vulnerabilities on GitHub at: @@ -134,35 +11,7 @@ Vulnerability reports remain private until published. When published, you will be credited as a contributor, and your contribution will reflect the MITRE Credit System. -### What do you consider a responsible disclosure? - -Please report the issue as described above. Please communicate with -the developers about when the fix will be released - usually it's the -next monthly release after the bug was reported. Some issues can take -longer. After the fix is released (releases usually happen on Thursdays) -please feel free to disclose the issue as you see fit. - -### What if I think it's a security issue but the developers disagree? - -Please read the above and try to explain to us why it fits the -description. - -### What if the developers still don't think it's a security issue? - -We'll have to agree to disagree. - -### The bug I submitted was classified as "not a security issue." You don't believe it's real? - -It has nothing to do with the bug being real or its importance to -you. It just means it does not fit our specific definitions for issues -that we will handle in a special way. We fix a lot of non-security bugs -and pull requests are always welcome. - -### But you classified bug #424242 as a security issue, but not this one?! - -Each bug usually has its aspects, if a short discussion does not -yield agreement we'd rather do more fixing and less arguing. - -### Do you pay bounties for security issues? +# Vulnerability Policy -PHP is a volunteer project. We have no money, thus we can't pay bounties. +Our full policy is described at +https://github.com/php/policies/blob/main/security-classification.rst diff --git a/Zend/Optimizer/escape_analysis.c b/Zend/Optimizer/escape_analysis.c index b7c0a5ec4466a..193479bae4b74 100644 --- a/Zend/Optimizer/escape_analysis.c +++ b/Zend/Optimizer/escape_analysis.c @@ -164,10 +164,17 @@ static bool is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, i /* These flags will always cause an exception */ ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT; - if (ce && !ce->parent && !ce->create_object && !ce->constructor && - !ce->destructor && !ce->__get && !ce->__set && - !(ce->ce_flags & forbidden_flags) && - (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + if (ce + && !ce->parent + && !ce->create_object + && ce->default_object_handlers->get_constructor == zend_std_get_constructor + && ce->default_object_handlers->dtor_obj == zend_objects_destroy_object + && !ce->constructor + && !ce->destructor + && !ce->__get + && !ce->__set + && !(ce->ce_flags & forbidden_flags) + && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { return 1; } break; @@ -227,8 +234,15 @@ static bool is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int va /* objects with destructors should escape */ zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1( script, op_array, opline); - if (ce && !ce->create_object && !ce->constructor && - !ce->destructor && !ce->__get && !ce->__set && !ce->parent) { + if (ce + && !ce->create_object + && ce->default_object_handlers->get_constructor == zend_std_get_constructor + && ce->default_object_handlers->dtor_obj == zend_objects_destroy_object + && !ce->constructor + && !ce->destructor + && !ce->__get + && !ce->__set + && !ce->parent) { return 1; } break; diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c index e2c746bbf539c..69238c1458bb2 100644 --- a/Zend/Optimizer/zend_func_info.c +++ b/Zend/Optimizer/zend_func_info.c @@ -61,7 +61,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline, &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]); uint32_t t3 = 0; - uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_EMPTY; + uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY; if (call_info->num_args == 3) { t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline, @@ -77,9 +77,7 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa } if ((t1 & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE)) && (t2 & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) { - if ((t3 & MAY_BE_ANY) != MAY_BE_DOUBLE) { - tmp |= MAY_BE_ARRAY_OF_LONG; - } + tmp |= MAY_BE_ARRAY_OF_LONG; } if (tmp & MAY_BE_ARRAY_OF_ANY) { tmp |= MAY_BE_ARRAY_PACKED; diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index f60f1f09a4b49..34d31a38c40ce 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3776,6 +3776,7 @@ static zend_always_inline zend_result _zend_update_type_info( /* Unset properties will resort back to __get/__set */ if (ce && !ce->create_object + && ce->default_object_handlers->read_property == zend_std_read_property && !ce->__get && !result_may_be_separated(ssa, ssa_op)) { tmp &= ~MAY_BE_RC1; @@ -5069,8 +5070,14 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op const zend_ssa_var_info *var_info = ssa->var_info + ssa_op->op1_use; const zend_class_entry *ce = var_info->ce; - if (var_info->is_instanceof || - !ce || ce->create_object || ce->__get || ce->__set || ce->parent) { + if (var_info->is_instanceof + || !ce + || ce->create_object + || ce->default_object_handlers->write_property != zend_std_write_property + || ce->default_object_handlers->get_property_ptr_ptr != zend_std_get_property_ptr_ptr + || ce->__get + || ce->__set + || ce->parent) { return 1; } @@ -5130,9 +5137,9 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op case ZEND_FETCH_IS: return (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)); case ZEND_ISSET_ISEMPTY_DIM_OBJ: - return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT)); + return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT)); case ZEND_FETCH_DIM_IS: - return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); + return (t1 & MAY_BE_OBJECT) || (t2 & (MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)); case ZEND_CAST: switch (opline->extended_value) { case IS_LONG: diff --git a/Zend/tests/gh13097_a.phpt b/Zend/tests/gh13097_a.phpt new file mode 100644 index 0000000000000..b9ad729f66e4d --- /dev/null +++ b/Zend/tests/gh13097_a.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-13097 (Anonymous class reference in trigger_error / thrown Exception) +--FILE-- + +--EXPECTF-- +Fatal error: class@anonymous%s ...now you don't! in %s on line %d diff --git a/Zend/tests/gh13097_b.phpt b/Zend/tests/gh13097_b.phpt new file mode 100644 index 0000000000000..7473c99d9f499 --- /dev/null +++ b/Zend/tests/gh13097_b.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-13097 (Anonymous class reference in trigger_error / thrown Exception) +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception: class@anonymous%s ...now you don't! in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh13145.phpt b/Zend/tests/gh13145.phpt new file mode 100644 index 0000000000000..801944d164890 --- /dev/null +++ b/Zend/tests/gh13145.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-13145: strtok() misoptimization +--FILE-- + +--EXPECT-- +string(4) "This" +string(2) "is" +string(2) "an" +string(7) "example" +string(6) "string" diff --git a/Zend/tests/multibyte/multibyte_encoding_007.phpt b/Zend/tests/multibyte/multibyte_encoding_007.phpt new file mode 100644 index 0000000000000..5e3323403d692 --- /dev/null +++ b/Zend/tests/multibyte/multibyte_encoding_007.phpt @@ -0,0 +1,15 @@ +--TEST-- +Don't segfault when zend.script_encoding=pass +--EXTENSIONS-- +mbstring +--INI-- +zend.multibyte=1 +zend.script_encoding=pass +internal_encoding=UTF-8 +--FILE-- + +--EXPECT-- +Warning: PHP Startup: INI setting contains invalid encoding "pass" in Unknown on line 0 +Done! diff --git a/Zend/tests/traits/bugs/gh13177.phpt b/Zend/tests/traits/bugs/gh13177.phpt new file mode 100644 index 0000000000000..42ef0ae9d60d7 --- /dev/null +++ b/Zend/tests/traits/bugs/gh13177.phpt @@ -0,0 +1,63 @@ +--TEST-- +GH-13177 (PHP 8.3.2: final private constructor not allowed when used in trait) +--FILE-- +getMethod("__construct"), "\n"; +} + +class Foo5 extends Foo3 { + private function __construct() {} +} + +?> +--EXPECTF-- +Warning: Private methods cannot be final as they are never overridden by other classes in %s on line %d +Method [ final private method __construct ] { + @@ %sgh13177.php 4 - 4 +} + +Method [ final private method __construct ] { + @@ %sgh13177.php 4 - 4 +} + +Method [ final private method __construct ] { + @@ %sgh13177.php 4 - 4 +} + +Method [ final private method __construct ] { + @@ %sgh13177.php 24 - 24 +} + + +Fatal error: Cannot override final method Foo3::__construct() in %s on line %d diff --git a/Zend/zend.h b/Zend/zend.h index 694e419b114f1..dab5bacd533c8 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.2-dev" +#define ZEND_VERSION "4.3.3" #define ZEND_ENGINE_3 diff --git a/Zend/zend_atomic.h b/Zend/zend_atomic.h index 617cde0ec5640..8aab3f11ada00 100644 --- a/Zend/zend_atomic.h +++ b/Zend/zend_atomic.h @@ -23,7 +23,7 @@ ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || (__GNUC__ > (x))) /* Builtins are used to avoid library linkage */ -#if __has_feature(c_atomic) +#if __has_feature(c_atomic) && defined(__clang__) #define HAVE_C11_ATOMICS 1 #elif ZEND_GCC_PREREQ(4, 7) #define HAVE_GNUC_ATOMICS 1 diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index c862c339058cf..b6f61b2bfbf72 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1123,10 +1123,9 @@ ZEND_FUNCTION(get_included_files) ZEND_FUNCTION(trigger_error) { zend_long error_type = E_USER_NOTICE; - char *message; - size_t message_len; + zend_string *message; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &message, &message_len, &error_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &message, &error_type) == FAILURE) { RETURN_THROWS(); } @@ -1143,7 +1142,7 @@ ZEND_FUNCTION(trigger_error) break; } - zend_error((int)error_type, "%s", message); + zend_error_zstr_at(error_type, zend_get_executed_filename_ex(), zend_get_executed_lineno(), message); // TODO Change to void RETURN_TRUE; } diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 7dcf2ad65b7f6..d37648cfa92f6 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -951,9 +951,10 @@ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *ex, int severit file = zval_get_string(GET_PROPERTY_SILENT(&exception, ZEND_STR_FILE)); line = zval_get_long(GET_PROPERTY_SILENT(&exception, ZEND_STR_LINE)); + ZVAL_STR(&tmp, str); zend_error_va(severity | E_DONT_BAIL, (file && ZSTR_LEN(file) > 0) ? file : NULL, line, - "Uncaught %s\n thrown", ZSTR_VAL(str)); + "Uncaught %Z\n thrown", &tmp); zend_string_release_ex(str, 0); zend_string_release_ex(file, 0); diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index b92ead02eb4a0..4051dd82f8a85 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -1773,7 +1773,8 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe } static void zend_get_gc_buffer_release(void); -static void zend_gc_root_tmpvars(void); +static void zend_gc_check_root_tmpvars(void); +static void zend_gc_remove_root_tmpvars(void); ZEND_API int zend_gc_collect_cycles(void) { @@ -1782,6 +1783,9 @@ ZEND_API int zend_gc_collect_cycles(void) bool did_rerun_gc = 0; zend_hrtime_t start_time = zend_hrtime(); + if (GC_G(num_roots) && GC_G(gc_active)) { + zend_gc_remove_root_tmpvars(); + } rerun_gc: if (GC_G(num_roots)) { @@ -1986,7 +1990,7 @@ ZEND_API int zend_gc_collect_cycles(void) finish: zend_get_gc_buffer_release(); - zend_gc_root_tmpvars(); + zend_gc_check_root_tmpvars(); GC_G(collector_time) += zend_hrtime() - start_time; return total_count; } @@ -2033,7 +2037,7 @@ static void zend_get_gc_buffer_release(void) { * cycles. However, there are some rare exceptions where this is possible, in which case we rely * on the producing code to root the value. If a GC run occurs between the rooting and consumption * of the value, we would end up leaking it. To avoid this, root all live TMPVAR values here. */ -static void zend_gc_root_tmpvars(void) { +static void zend_gc_check_root_tmpvars(void) { zend_execute_data *ex = EG(current_execute_data); for (; ex; ex = ex->prev_execute_data) { zend_function *func = ex->func; @@ -2063,6 +2067,36 @@ static void zend_gc_root_tmpvars(void) { } } +static void zend_gc_remove_root_tmpvars(void) { + zend_execute_data *ex = EG(current_execute_data); + for (; ex; ex = ex->prev_execute_data) { + zend_function *func = ex->func; + if (!func || !ZEND_USER_CODE(func->type)) { + continue; + } + + uint32_t op_num = ex->opline - ex->func->op_array.opcodes; + for (uint32_t i = 0; i < func->op_array.last_live_range; i++) { + const zend_live_range *range = &func->op_array.live_range[i]; + if (range->start > op_num) { + break; + } + if (range->end <= op_num) { + continue; + } + + uint32_t kind = range->var & ZEND_LIVE_MASK; + if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) { + uint32_t var_num = range->var & ~ZEND_LIVE_MASK; + zval *var = ZEND_CALL_VAR(ex, var_num); + if (Z_REFCOUNTED_P(var)) { + GC_REMOVE_FROM_BUFFER(Z_COUNTED_P(var)); + } + } + } + } +} + #if GC_BENCH void gc_bench_print(void) { diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 6362090ada908..ea8cc219a23a3 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1949,10 +1949,6 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ zend_function *new_fn; bool check_inheritance = false; - if ((fn->common.fn_flags & (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) == (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) { - zend_error(E_COMPILE_WARNING, "Private methods cannot be final as they are never overridden by other classes"); - } - if ((existing_fn = zend_hash_find_ptr(&ce->function_table, key)) != NULL) { /* if it is the same function with the same visibility and has not been assigned a class scope yet, regardless * of where it is coming from there is no conflict and we do not need to add it again */ @@ -2033,6 +2029,17 @@ static void zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce) /* } /* }}} */ +static void zend_traits_check_private_final_inheritance(uint32_t original_fn_flags, zend_function *fn_copy, zend_string *name) +{ + /* If the function was originally already private+final, then it will have already been warned about. + * If the function became private+final only after applying modifiers, we need to emit the same warning. */ + if ((original_fn_flags & (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) != (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL) + && (fn_copy->common.fn_flags & (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)) == (ZEND_ACC_PRIVATE | ZEND_ACC_FINAL) + && !zend_string_equals_literal_ci(name, ZEND_CONSTRUCTOR_FUNC_NAME)) { + zend_error(E_COMPILE_WARNING, "Private methods cannot be final as they are never overridden by other classes"); + } +} + static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, zend_class_entry *ce, HashTable *exclude_table, zend_class_entry **aliases) /* {{{ */ { zend_trait_alias *alias, **alias_ptr; @@ -2058,6 +2065,8 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z fn_copy.common.fn_flags = alias->modifiers | fn->common.fn_flags; } + zend_traits_check_private_final_inheritance(fn->common.fn_flags, &fn_copy, alias->alias); + lcname = zend_string_tolower(alias->alias); zend_add_trait_method(ce, alias->alias, lcname, &fn_copy); zend_string_release_ex(lcname, 0); @@ -2095,6 +2104,8 @@ static void zend_traits_copy_functions(zend_string *fnname, zend_function *fn, z } } + zend_traits_check_private_final_inheritance(fn->common.fn_flags, &fn_copy, fnname); + zend_add_trait_method(ce, fn->common.function_name, fnname, &fn_copy); } } diff --git a/Zend/zend_max_execution_timer.c b/Zend/zend_max_execution_timer.c index 480631dcb169a..48a4d1bd66415 100644 --- a/Zend/zend_max_execution_timer.c +++ b/Zend/zend_max_execution_timer.c @@ -35,6 +35,12 @@ ZEND_API void zend_max_execution_timer_init(void) /* {{{ */ { + pid_t pid = getpid(); + + if (EG(pid) == pid) { + return; + } + struct sigevent sev; sev.sigev_notify = SIGEV_THREAD_ID; sev.sigev_value.sival_ptr = &EG(max_execution_timer_timer); @@ -48,9 +54,9 @@ ZEND_API void zend_max_execution_timer_init(void) /* {{{ */ EG(pid) = getpid(); -# ifdef MAX_EXECUTION_TIMERS_DEBUG +# ifdef MAX_EXECUTION_TIMERS_DEBUG fprintf(stderr, "Timer %#jx created on thread %d\n", (uintmax_t) EG(max_execution_timer_timer), sev.sigev_notify_thread_id); -# endif +# endif sigaction(sev.sigev_signo, NULL, &EG(oldact)); } diff --git a/Zend/zend_string.c b/Zend/zend_string.c index 549270690a8a3..91fa421e9c68e 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -374,6 +374,7 @@ ZEND_API void zend_interned_strings_switch_storage(bool request) } } +#if defined(__GNUC__) && (defined(__i386__) || (defined(__x86_64__) && !defined(__ILP32__))) /* Even if we don't build with valgrind support, include the symbol so that valgrind available * only at runtime will not result in false positives. */ #ifndef I_REPLACE_SONAME_FNNAME_ZU @@ -381,29 +382,20 @@ ZEND_API void zend_interned_strings_switch_storage(bool request) #endif /* See GH-9068 */ -#if defined(__GNUC__) && (__GNUC__ >= 11 || defined(__clang__)) && __has_attribute(no_caller_saved_registers) -# define NO_CALLER_SAVED_REGISTERS __attribute__((no_caller_saved_registers)) -# ifndef __clang__ -# pragma GCC push_options -# pragma GCC target ("general-regs-only") -# define POP_OPTIONS -# endif +#if __has_attribute(noipa) +# define NOIPA __attribute__((noipa)) #else -# define NO_CALLER_SAVED_REGISTERS +# define NOIPA #endif -ZEND_API bool ZEND_FASTCALL NO_CALLER_SAVED_REGISTERS I_REPLACE_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(const zend_string *s1, const zend_string *s2) +ZEND_API bool ZEND_FASTCALL I_REPLACE_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(const zend_string *s1, const zend_string *s2) { return !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)); } - -#ifdef POP_OPTIONS -# pragma GCC pop_options -# undef POP_OPTIONS #endif #if defined(__GNUC__) && defined(__i386__) -ZEND_API bool ZEND_FASTCALL zend_string_equal_val(const zend_string *s1, const zend_string *s2) +ZEND_API zend_never_inline NOIPA bool ZEND_FASTCALL zend_string_equal_val(const zend_string *s1, const zend_string *s2) { const char *ptr = ZSTR_VAL(s1); size_t delta = (const char*)s2 - (const char*)s1; @@ -441,7 +433,7 @@ ZEND_API bool ZEND_FASTCALL zend_string_equal_val(const zend_string *s1, const z } #elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__) -ZEND_API bool ZEND_FASTCALL zend_string_equal_val(const zend_string *s1, const zend_string *s2) +ZEND_API zend_never_inline NOIPA bool ZEND_FASTCALL zend_string_equal_val(const zend_string *s1, const zend_string *s2) { const char *ptr = ZSTR_VAL(s1); size_t delta = (const char*)s2 - (const char*)s1; diff --git a/build/Makefile.global b/build/Makefile.global index dee5fa5ecde73..b11ce20aae87a 100644 --- a/build/Makefile.global +++ b/build/Makefile.global @@ -16,8 +16,8 @@ build-modules: $(PHP_MODULES) $(PHP_ZEND_EX) build-binaries: $(PHP_BINARIES) libphp.la: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) - $(LIBTOOL) --mode=link $(CC) $(LIBPHP_CFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) -rpath $(phptempdir) $(EXTRA_LDFLAGS) $(LDFLAGS) $(PHP_RPATHS) $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) -o $@ - -@$(LIBTOOL) --silent --mode=install cp $@ $(phptempdir)/$@ >/dev/null 2>&1 + $(LIBTOOL) --tag=CC --mode=link $(CC) $(LIBPHP_CFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) -rpath $(phptempdir) $(EXTRA_LDFLAGS) $(LDFLAGS) $(PHP_RPATHS) $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) -o $@ + -@$(LIBTOOL) --silent --tag=CC --mode=install cp $@ $(phptempdir)/$@ >/dev/null 2>&1 libs/libphp.bundle: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(CC) $(MH_BUNDLE_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) $(EXTRA_LDFLAGS) $(PHP_GLOBAL_OBJS:.lo=.o) $(PHP_SAPI_OBJS:.lo=.o) $(PHP_FRAMEWORKS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) -o $@ && cp $@ libs/libphp.so diff --git a/build/gen_stub.php b/build/gen_stub.php index a4818dd33c865..b3f3fcfba63db 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -4925,7 +4925,7 @@ function initPhpParser() { } $isInitialized = true; - $version = "5.0.0alpha3"; + $version = "5.0.0"; $phpParserDir = __DIR__ . "/PHP-Parser-$version"; if (!is_dir($phpParserDir)) { installPhpParser($version, $phpParserDir); diff --git a/build/php.m4 b/build/php.m4 index 53ba394231709..25d820c2383c0 100644 --- a/build/php.m4 +++ b/build/php.m4 @@ -786,10 +786,10 @@ dnl dnl PHP_BUILD_PROGRAM dnl AC_DEFUN([PHP_BUILD_PROGRAM],[ - php_c_pre='$(LIBTOOL) --mode=compile $(CC)' + php_c_pre='$(LIBTOOL) --tag=CC --mode=compile $(CC)' php_c_meta='$(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS)' php_c_post= - php_cxx_pre='$(LIBTOOL) --mode=compile $(CXX)' + php_cxx_pre='$(LIBTOOL) --tag=CXX --mode=compile $(CXX)' php_cxx_meta='$(COMMON_FLAGS) $(CXXFLAGS_CLEAN) $(EXTRA_CXXFLAGS)' php_cxx_post= php_lo=lo @@ -799,10 +799,10 @@ AC_DEFUN([PHP_BUILD_PROGRAM],[ no) pic_setting='-prefer-non-pic';; esac - shared_c_pre='$(LIBTOOL) --mode=compile $(CC)' + shared_c_pre='$(LIBTOOL) --tag=CC --mode=compile $(CC)' shared_c_meta='$(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) '$pic_setting shared_c_post= - shared_cxx_pre='$(LIBTOOL) --mode=compile $(CXX)' + shared_cxx_pre='$(LIBTOOL) --tag=CXX --mode=compile $(CXX)' shared_cxx_meta='$(COMMON_FLAGS) $(CXXFLAGS_CLEAN) $(EXTRA_CXXFLAGS) '$pic_setting shared_cxx_post= shared_lo=lo @@ -832,10 +832,10 @@ AC_DEFUN([PHP_SHARED_MODULE],[ PHP_SUBST($2) cat >>Makefile.objects<multi = curl_multi_init(); + mh->multi = multi; zend_llist_init(&mh->easyh, sizeof(zval), _php_curl_multi_cleanup_list, 0); } diff --git a/ext/dom/tests/DOMEntityReference_predefined_free.phpt b/ext/dom/tests/DOMEntityReference_predefined_free.phpt new file mode 100644 index 0000000000000..4b971d83703ed --- /dev/null +++ b/ext/dom/tests/DOMEntityReference_predefined_free.phpt @@ -0,0 +1,46 @@ +--TEST-- +Freeing of a predefined DOMEntityReference +--EXTENSIONS-- +dom +--FILE-- + +--EXPECT-- +object(DOMEntityReference)#1 (17) { + ["nodeName"]=> + string(3) "amp" + ["nodeValue"]=> + NULL + ["nodeType"]=> + int(5) + ["parentNode"]=> + NULL + ["parentElement"]=> + NULL + ["childNodes"]=> + string(22) "(object value omitted)" + ["firstChild"]=> + string(22) "(object value omitted)" + ["lastChild"]=> + string(22) "(object value omitted)" + ["previousSibling"]=> + NULL + ["nextSibling"]=> + NULL + ["attributes"]=> + NULL + ["isConnected"]=> + bool(false) + ["namespaceURI"]=> + NULL + ["prefix"]=> + string(0) "" + ["localName"]=> + NULL + ["baseURI"]=> + NULL + ["textContent"]=> + string(0) "" +} diff --git a/ext/dom/tests/delayed_freeing/entity_declaration.phpt b/ext/dom/tests/delayed_freeing/entity_declaration.phpt index 3e082611c3583..c049be675be38 100644 --- a/ext/dom/tests/delayed_freeing/entity_declaration.phpt +++ b/ext/dom/tests/delayed_freeing/entity_declaration.phpt @@ -9,16 +9,30 @@ $doc->loadXML(<<<'XML' + ]> XML); -$entity = $doc->doctype->entities[0]; -var_dump($entity->nodeName, $entity->parentNode->nodeName); +$ref1 = $doc->createEntityReference("test"); +$ref2 = $doc->createEntityReference("myimage"); +$entity1 = $doc->doctype->entities[0]; +$entity2 = $doc->doctype->entities[1]; +if (strcmp($entity1->nodeName, $entity2->nodeName) < 0) { + // Entity ordering depends on the addresses + [$entity1, $entity2] = [$entity2, $entity1]; +} +var_dump($entity1->nodeName, $entity1->parentNode->nodeName); +var_dump($entity2->nodeName, $entity2->parentNode->nodeName); $doc->removeChild($doc->doctype); -var_dump($entity->nodeName, $entity->parentNode); +var_dump($entity1->nodeName, $entity1->parentNode); +var_dump($entity2->nodeName, $entity2->parentNode); ?> --EXPECT-- string(4) "test" string(5) "books" +string(7) "myimage" +string(5) "books" string(4) "test" NULL +string(7) "myimage" +NULL diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 0e4ef64cdfdd2..7379798d861bc 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -2677,8 +2677,8 @@ static gdFontPtr php_find_gd_font(zend_object *font_obj, zend_long font_int) */ static void php_imagefontsize(INTERNAL_FUNCTION_PARAMETERS, int arg) { - zend_object *font_obj; - zend_long font_int; + zend_object *font_obj = NULL; + zend_long font_int = 0; gdFontPtr font; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -2745,9 +2745,8 @@ static void php_imagechar(INTERNAL_FUNCTION_PARAMETERS, int mode) gdImagePtr im; int ch = 0, col, x, y, i, l = 0; unsigned char *str = NULL; - zend_object *font_obj; - zend_long font_int; - gdFontPtr font; + zend_object *font_obj = NULL; + zend_long font_int = 0; ZEND_PARSE_PARAMETERS_START(6, 6) Z_PARAM_OBJECT_OF_CLASS(IM, gd_image_ce) @@ -2772,7 +2771,7 @@ static void php_imagechar(INTERNAL_FUNCTION_PARAMETERS, int mode) y = Y; x = X; - font = php_find_gd_font(font_obj, font_int); + gdFontPtr font = php_find_gd_font(font_obj, font_int); switch (mode) { case 0: diff --git a/ext/gd/libgd/gd_rotate.c b/ext/gd/libgd/gd_rotate.c index cc89652f03f79..53c6c9470db79 100644 --- a/ext/gd/libgd/gd_rotate.c +++ b/ext/gd/libgd/gd_rotate.c @@ -216,7 +216,7 @@ gdImagePtr gdImageRotate90 (gdImagePtr src, int ignoretransparent) if (dst != NULL) { int old_blendmode = dst->alphaBlendingFlag; dst->alphaBlendingFlag = 0; - + dst->saveAlphaFlag = 1; dst->transparent = src->transparent; gdImagePaletteCopy (dst, src); @@ -263,7 +263,7 @@ gdImagePtr gdImageRotate180 (gdImagePtr src, int ignoretransparent) if (dst != NULL) { int old_blendmode = dst->alphaBlendingFlag; dst->alphaBlendingFlag = 0; - + dst->saveAlphaFlag = 1; dst->transparent = src->transparent; gdImagePaletteCopy (dst, src); @@ -311,7 +311,7 @@ gdImagePtr gdImageRotate270 (gdImagePtr src, int ignoretransparent) if (dst != NULL) { int old_blendmode = dst->alphaBlendingFlag; dst->alphaBlendingFlag = 0; - + dst->saveAlphaFlag = 1; dst->transparent = src->transparent; gdImagePaletteCopy (dst, src); diff --git a/ext/gd/libgd/gdft.c b/ext/gd/libgd/gdft.c index 554a656e625c9..6876ca6f6b428 100644 --- a/ext/gd/libgd/gdft.c +++ b/ext/gd/libgd/gdft.c @@ -401,7 +401,17 @@ static void *fontFetch (char **error, void *key) #ifdef NETWARE if (*name == '/' || (name[0] != 0 && strstr(name, ":/"))) { #else - if (*name == '/' || (name[0] != 0 && name[1] == ':' && (name[2] == '/' || name[2] == '\\'))) { + /* Actual length doesn't matter, just the minimum does up to length 2. */ + unsigned int min_length = 0; + if (name[0] != '\0') { + if (name[1] != '\0') { + min_length = 2; + } else { + min_length = 1; + } + } + ZEND_IGNORE_VALUE(min_length); /* On Posix systems this may be unused */ + if (IS_ABSOLUTE_PATH(name, min_length)) { #endif snprintf(fullname, sizeof(fullname) - 1, "%s", name); if (access(fullname, R_OK) == 0) { diff --git a/ext/gd/tests/gh10344.phpt b/ext/gd/tests/gh10344.phpt new file mode 100644 index 0000000000000..829f8f9c35cde --- /dev/null +++ b/ext/gd/tests/gh10344.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-10344 (imagettfbbox(): Could not find/open font UNC path) +--EXTENSIONS-- +gd +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(8) diff --git a/ext/gd/tests/gh10614.phpt b/ext/gd/tests/gh10614.phpt new file mode 100644 index 0000000000000..c0689141837cd --- /dev/null +++ b/ext/gd/tests/gh10614.phpt @@ -0,0 +1,93 @@ +--TEST-- +GH-10614 (imagerotate will turn the picture all black, when rotated 90) +--EXTENSIONS-- +gd +--SKIPIF-- +=')) die("skip test requires GD 2.3.4 or older"); +?> +--FILE-- + +--EXPECT-- +--- Angle 0 --- +string(1) "0" +string(1) "0" +string(1) "0" +string(1) "0" +string(1) "0" +string(1) "0" +string(1) "0" +string(1) "0" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +--- Angle 90 --- +string(8) "7f000000" +string(8) "7f000000" +string(1) "0" +string(1) "0" +string(8) "7f000000" +string(8) "7f000000" +string(1) "0" +string(1) "0" +string(8) "7f000000" +string(8) "7f000000" +string(1) "0" +string(1) "0" +string(8) "7f000000" +string(8) "7f000000" +string(1) "0" +string(1) "0" +--- Angle 180 --- +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(8) "7f000000" +string(1) "0" +string(1) "0" +string(1) "0" +string(1) "0" +string(1) "0" +string(1) "0" +string(1) "0" +string(1) "0" +--- Angle 270 --- +string(1) "0" +string(1) "0" +string(8) "7f000000" +string(8) "7f000000" +string(1) "0" +string(1) "0" +string(8) "7f000000" +string(8) "7f000000" +string(1) "0" +string(1) "0" +string(8) "7f000000" +string(8) "7f000000" +string(1) "0" +string(1) "0" +string(8) "7f000000" +string(8) "7f000000" diff --git a/ext/gd/tests/gh10614.png b/ext/gd/tests/gh10614.png new file mode 100644 index 0000000000000..baf1ea92fc9ce Binary files /dev/null and b/ext/gd/tests/gh10614.png differ diff --git a/ext/gd/tests/gh13082.gdf b/ext/gd/tests/gh13082.gdf new file mode 100644 index 0000000000000..44669f3d12b92 Binary files /dev/null and b/ext/gd/tests/gh13082.gdf differ diff --git a/ext/gd/tests/gh13082.phpt b/ext/gd/tests/gh13082.phpt new file mode 100644 index 0000000000000..edfca9d850fdc --- /dev/null +++ b/ext/gd/tests/gh13082.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-13082 - imagefontwidth/height unexpectedly throwing an exception on a valid GdFont object. +--EXTENSIONS-- +gd +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(12) +int(20) diff --git a/ext/intl/dateformat/dateformat_create.cpp b/ext/intl/dateformat/dateformat_create.cpp index 399740dbca227..dbb2e8a6f6229 100644 --- a/ext/intl/dateformat/dateformat_create.cpp +++ b/ext/intl/dateformat/dateformat_create.cpp @@ -112,7 +112,7 @@ static zend_result datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS, zend_error_handlin } locale = Locale::createFromName(locale_str); /* get*Name accessors being set does not preclude being bogus */ - if (locale.isBogus() || ((locale_len == 1 && locale_str[0] != 'C') && strlen(locale.getISO3Language()) == 0)) { + if (locale.isBogus() || ((locale_len == 1 && locale_str[0] != 'C') || (locale_len > 1 && strlen(locale.getISO3Language()) == 0))) { goto error; } diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index e8654abeed700..299dd1e11de5f 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -207,12 +207,36 @@ static void php_libxml_node_free(xmlNodePtr node) * dtd is attached to the document. This works around the issue by inspecting the parent directly. */ case XML_ENTITY_DECL: { xmlEntityPtr entity = (xmlEntityPtr) node; - php_libxml_unlink_entity_decl(entity); - if (entity->orig != NULL) { - xmlFree((char *) entity->orig); - entity->orig = NULL; + if (entity->etype != XML_INTERNAL_PREDEFINED_ENTITY) { + php_libxml_unlink_entity_decl(entity); +#if LIBXML_VERSION >= 21200 + xmlFreeEntity(entity); +#else + if (entity->children != NULL && entity->owner && entity == (xmlEntityPtr) entity->children->parent) { + xmlFreeNodeList(entity->children); + } + xmlDictPtr dict = entity->doc != NULL ? entity->doc->dict : NULL; + if (dict == NULL || !xmlDictOwns(dict, entity->name)) { + xmlFree((xmlChar *) entity->name); + } + if (dict == NULL || !xmlDictOwns(dict, entity->ExternalID)) { + xmlFree((xmlChar *) entity->ExternalID); + } + if (dict == NULL || !xmlDictOwns(dict, entity->SystemID)) { + xmlFree((xmlChar *) entity->SystemID); + } + if (dict == NULL || !xmlDictOwns(dict, entity->URI)) { + xmlFree((xmlChar *) entity->URI); + } + if (dict == NULL || !xmlDictOwns(dict, entity->content)) { + xmlFree(entity->content); + } + if (dict == NULL || !xmlDictOwns(dict, entity->orig)) { + xmlFree(entity->orig); + } + xmlFree(entity); +#endif } - xmlFreeNode(node); break; } case XML_NOTATION_NODE: { @@ -1386,6 +1410,15 @@ PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node) case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: break; + case XML_ENTITY_REF_NODE: + /* Entity reference nodes are special: their children point to entity declarations, + * but they don't own the declarations and therefore shouldn't free the children. + * Moreover, there can be more than one reference node for a single entity declarations. */ + php_libxml_unregister_node(node); + if (node->parent == NULL) { + php_libxml_node_free(node); + } + break; default: if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) { php_libxml_node_free_list((xmlNodePtr) node->children); diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index ad15dd6768e03..cf4c1c32c5915 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -291,8 +291,7 @@ static size_t count_commas(const char *p, const char *end) { * Emits a ValueError in function context and a warning in INI context, in INI context arg_num must be 0. */ static zend_result php_mb_parse_encoding_list(const char *value, size_t value_length, - const mbfl_encoding ***return_list, size_t *return_size, bool persistent, uint32_t arg_num, - bool allow_pass_encoding) + const mbfl_encoding ***return_list, size_t *return_size, bool persistent, uint32_t arg_num) { if (value == NULL || value_length == 0) { *return_list = NULL; @@ -345,8 +344,7 @@ static zend_result php_mb_parse_encoding_list(const char *value, size_t value_le } } } else { - const mbfl_encoding *encoding = - allow_pass_encoding ? php_mb_get_encoding_or_pass(p1) : mbfl_name2encoding(p1); + const mbfl_encoding *encoding = mbfl_name2encoding(p1); if (!encoding) { /* Called from an INI setting modification */ if (arg_num == 0) { @@ -452,8 +450,12 @@ static const zend_encoding *php_mb_zend_encoding_detector(const unsigned char *a list = (const zend_encoding**)MBSTRG(current_detect_order_list); list_size = MBSTRG(current_detect_order_list_size); } - - return (const zend_encoding*)mb_guess_encoding((unsigned char*)arg_string, arg_length, (const mbfl_encoding **)list, list_size, false, false); + if (list_size == 1 && ((mbfl_encoding*)*list) == &mbfl_encoding_pass) { + /* Emulate behavior of previous implementation; it would never return "pass" + * from an encoding auto-detection operation */ + return NULL; + } + return (const zend_encoding*)mb_guess_encoding((unsigned char*)arg_string, arg_length, (const mbfl_encoding**)list, list_size, false, false); } static size_t php_mb_zend_encoding_converter(unsigned char **to, size_t *to_length, const unsigned char *from, size_t from_length, const zend_encoding *encoding_to, const zend_encoding *encoding_from) @@ -474,7 +476,7 @@ static zend_result php_mb_zend_encoding_list_parser(const char *encoding_list, s return php_mb_parse_encoding_list( encoding_list, encoding_list_len, (const mbfl_encoding ***)return_list, return_size, - persistent, /* arg_num */ 0, /* allow_pass_encoding */ 1); + persistent, /* arg_num */ 0); } static const zend_encoding *php_mb_zend_internal_encoding_getter(void) @@ -712,7 +714,7 @@ static PHP_INI_MH(OnUpdate_mbstring_detect_order) return SUCCESS; } - if (FAILURE == php_mb_parse_encoding_list(ZSTR_VAL(new_value), ZSTR_LEN(new_value), &list, &size, /* persistent */ 1, /* arg_num */ 0, /* allow_pass_encoding */ 0) || size == 0) { + if (FAILURE == php_mb_parse_encoding_list(ZSTR_VAL(new_value), ZSTR_LEN(new_value), &list, &size, /* persistent */ 1, /* arg_num */ 0) || size == 0) { return FAILURE; } @@ -728,7 +730,11 @@ static PHP_INI_MH(OnUpdate_mbstring_detect_order) static int _php_mb_ini_mbstring_http_input_set(const char *new_value, size_t new_value_length) { const mbfl_encoding **list; size_t size; - if (FAILURE == php_mb_parse_encoding_list(new_value, new_value_length, &list, &size, /* persistent */ 1, /* arg_num */ 0, /* allow_pass_encoding */ 1) || size == 0) { + if (new_value_length == 4 && strncmp(new_value, "pass", 4) == 0) { + list = (const mbfl_encoding**)pecalloc(1, sizeof(mbfl_encoding*), 1); + *list = &mbfl_encoding_pass; + size = 1; + } else if (FAILURE == php_mb_parse_encoding_list(new_value, new_value_length, &list, &size, /* persistent */ 1, /* arg_num */ 0) || size == 0) { return FAILURE; } if (MBSTRG(http_input_list)) { @@ -1383,7 +1389,7 @@ PHP_FUNCTION(mb_detect_order) RETURN_THROWS(); } } else { - if (FAILURE == php_mb_parse_encoding_list(ZSTR_VAL(order_str), ZSTR_LEN(order_str), &list, &size, /* persistent */ 0, /* arg_num */ 1, /* allow_pass_encoding */ 0)) { + if (FAILURE == php_mb_parse_encoding_list(ZSTR_VAL(order_str), ZSTR_LEN(order_str), &list, &size, /* persistent */ 0, /* arg_num */ 1)) { RETURN_THROWS(); } } @@ -2799,7 +2805,7 @@ PHP_FUNCTION(mb_convert_encoding) } else if (from_encodings_str) { if (php_mb_parse_encoding_list(ZSTR_VAL(from_encodings_str), ZSTR_LEN(from_encodings_str), &from_encodings, &num_from_encodings, - /* persistent */ 0, /* arg_num */ 3, /* allow_pass_encoding */ 0) == FAILURE) { + /* persistent */ 0, /* arg_num */ 3) == FAILURE) { RETURN_THROWS(); } free_from_encodings = true; @@ -3163,7 +3169,7 @@ PHP_FUNCTION(mb_detect_encoding) RETURN_THROWS(); } } else if (encoding_str) { - if (FAILURE == php_mb_parse_encoding_list(ZSTR_VAL(encoding_str), ZSTR_LEN(encoding_str), &elist, &size, /* persistent */ 0, /* arg_num */ 2, /* allow_pass_encoding */ 0)) { + if (FAILURE == php_mb_parse_encoding_list(ZSTR_VAL(encoding_str), ZSTR_LEN(encoding_str), &elist, &size, /* persistent */ 0, /* arg_num */ 2)) { RETURN_THROWS(); } } else { @@ -3564,7 +3570,7 @@ PHP_FUNCTION(mb_convert_variables) RETURN_THROWS(); } } else { - if (php_mb_parse_encoding_list(ZSTR_VAL(from_enc_str), ZSTR_LEN(from_enc_str), &elist, &elistsz, /* persistent */ 0, /* arg_num */ 2, /* allow_pass_encoding */ 0) == FAILURE) { + if (php_mb_parse_encoding_list(ZSTR_VAL(from_enc_str), ZSTR_LEN(from_enc_str), &elist, &elistsz, /* persistent */ 0, /* arg_num */ 2) == FAILURE) { RETURN_THROWS(); } } diff --git a/ext/mbstring/tests/gh13123.phpt b/ext/mbstring/tests/gh13123.phpt new file mode 100644 index 0000000000000..3944751d2dea4 --- /dev/null +++ b/ext/mbstring/tests/gh13123.phpt @@ -0,0 +1,24 @@ +--TEST-- +Segfault in mb_fast_convert() when mbstring.encoding_translation is enabled (GH-13123) +--EXTENSIONS-- +mbstring +--POST_RAW-- +Content-Type: multipart/form-data, boundary=Blah + +--Blah +Content-Disposition: form-data; name="file"; filename="file.txt" +Content-Type: text/plain + +foo +--Blah + +--INI-- +error_reporting=E_ALL&~E_DEPRECATED +mbstring.encoding_translation=On +mbstring.http_input=pass +--FILE-- + +--EXPECT-- +Done! diff --git a/ext/mysqli/tests/gh12107.phpt b/ext/mysqli/tests/gh12107.phpt new file mode 100644 index 0000000000000..ca5dd4ba8f142 --- /dev/null +++ b/ext/mysqli/tests/gh12107.phpt @@ -0,0 +1,59 @@ +--TEST-- +GH-12107 (When running a stored procedure (that returns a result set) twice, PHP crashes) +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--FILE-- +query($sql); + +echo "Start or run 1\n"; +$stmt = $mysqli->prepare("call `gh12107`()"); +$stmt->execute(); +$stmt->bind_result($output); +var_dump($stmt->fetch()); +var_dump($output); +unset($output); +echo "End of run 1\n"; + +echo "Start or run 2\n"; +$stmt->execute(); +$stmt->bind_result($output); +var_dump($stmt->fetch()); +var_dump($output); +echo "End of run 2\n"; + +?> +--CLEAN-- + +--EXPECT-- +Start or run 1 +bool(true) +string(11) "hello world" +End of run 1 +Start or run 2 +bool(true) +string(11) "hello world" +End of run 2 diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index dad5de3bb90a2..87d0ba738a67b 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -652,8 +652,11 @@ MYSQLND_METHOD(mysqlnd_stmt, send_execute)(MYSQLND_STMT * const s, const enum_my Executed, but the user hasn't started to fetch This will clean also the metadata, but after the EXECUTE call we will have it again. + stmt->result may be freed and nullified by free_stmt_result, transitively called from flush. */ - stmt->result->m.free_result_buffers(stmt->result); + if (stmt->result) { + stmt->result->m.free_result_buffers(stmt->result); + } stmt->state = MYSQLND_STMT_PREPARED; } else if (stmt->state < MYSQLND_STMT_PREPARED) { diff --git a/ext/oci8/LICENSE b/ext/oci8/LICENSE index 47a594e38a4d2..0815d7eb79119 100644 --- a/ext/oci8/LICENSE +++ b/ext/oci8/LICENSE @@ -1,6 +1,6 @@ -------------------------------------------------------------------- The PHP License, version 3.01 -Copyright (c) 1999 - 2023 The PHP Group. All rights reserved. +Copyright (c) 1999 - 2024 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index f3b3e7edb6e48..bd36eceb65d0b 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -5054,7 +5054,7 @@ ZEND_EXT_API int zend_jit_startup(void *buf, size_t size, bool reattached) ZEND_EXT_API void zend_jit_shutdown(void) { - if (JIT_G(debug) & ZEND_JIT_DEBUG_SIZE) { + if (JIT_G(debug) & ZEND_JIT_DEBUG_SIZE && dasm_ptr != NULL) { fprintf(stderr, "\nJIT memory usage: %td\n", (ptrdiff_t)((char*)*dasm_ptr - (char*)dasm_buf)); } diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 814da32123066..e4d7a745cba2d 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -4443,8 +4443,14 @@ static int zend_jit_math_long_long(dasm_State **Dst, } else { zend_reg tmp_reg; - if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) { - tmp_reg = ZREG_R1; + if (Z_MODE(res_addr) == IS_MEM_ZVAL) { + if (Z_REG(res_addr) != ZREG_R0 && result_reg != ZREG_R0) { + tmp_reg = ZREG_R0; + } else if (Z_REG(res_addr) != ZREG_R1 && result_reg != ZREG_R1) { + tmp_reg = ZREG_R1; + } else { + tmp_reg = ZREG_R2; + } } else if (result_reg != ZREG_R0) { tmp_reg = ZREG_R0; } else { @@ -5312,8 +5318,16 @@ static int zend_jit_long_math_helper(dasm_State **Dst, } else { zend_reg tmp_reg; - if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R0) { - tmp_reg = ZREG_R1; + if (Z_MODE(res_addr) == IS_MEM_ZVAL) { + if (Z_REG(res_addr) != ZREG_R0 && result_reg != ZREG_R0) { + tmp_reg = ZREG_R0; + } else if (Z_REG(res_addr) != ZREG_R1 && result_reg != ZREG_R1) { + tmp_reg = ZREG_R1; + } else { + tmp_reg = ZREG_R2; + } + } else if (Z_MODE(res_addr) == IS_MEM_ZVAL && Z_REG(res_addr) == ZREG_R1) { + tmp_reg = ZREG_R0; } else if (result_reg != ZREG_R0) { tmp_reg = ZREG_R0; } else { diff --git a/ext/opcache/tests/jit/gh12481.phpt b/ext/opcache/tests/jit/gh12481.phpt new file mode 100644 index 0000000000000..7c13db6f1210e --- /dev/null +++ b/ext/opcache/tests/jit/gh12481.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-12481: PHP crash on Windows 64-bit with JIT enabled +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--SKIPIF-- + +--FILE-- + +DONE +--EXPECTF-- +DONE diff --git a/ext/opcache/tests/jit/gh13232.phpt b/ext/opcache/tests/jit/gh13232.phpt new file mode 100644 index 0000000000000..aad5a7913f8a6 --- /dev/null +++ b/ext/opcache/tests/jit/gh13232.phpt @@ -0,0 +1,13 @@ +--TEST-- +GH-13232 (Segmentation fault will be reported when JIT is off but JIT_debug is still on) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=disable +opcache.jit_debug=0xfffff +--FILE-- + +--EXPECT-- +Done diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 9698b54a21085..6f85e9852fba6 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -61,7 +61,7 @@ #include #endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L && !defined(OPENSSL_NO_ENGINE) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)) && !defined(OPENSSL_NO_ENGINE) #include #endif diff --git a/ext/pdo_firebird/firebird_statement.c b/ext/pdo_firebird/firebird_statement.c index 4ad51ab483d96..1fe894cd631df 100644 --- a/ext/pdo_firebird/firebird_statement.c +++ b/ext/pdo_firebird/firebird_statement.c @@ -51,6 +51,11 @@ static zend_always_inline double get_double_from_sqldata(const ISC_SCHAR *sqldat READ_AND_RETURN_USING_MEMCPY(double, sqldata); } +static zend_always_inline float get_float_from_sqldata(const ISC_SCHAR *sqldata) +{ + READ_AND_RETURN_USING_MEMCPY(float, sqldata); +} + static zend_always_inline ISC_TIMESTAMP get_isc_timestamp_from_sqldata(const ISC_SCHAR *sqldata) { READ_AND_RETURN_USING_MEMCPY(ISC_TIMESTAMP, sqldata); @@ -459,11 +464,11 @@ static int firebird_stmt_get_col( break; case SQL_FLOAT: /* TODO: Why is this not returned as the native type? */ - ZVAL_STR(result, zend_strpprintf(0, "%F", *(float*)var->sqldata)); + ZVAL_STR(result, zend_strpprintf_unchecked(0, "%.8H", get_float_from_sqldata(var->sqldata))); break; case SQL_DOUBLE: /* TODO: Why is this not returned as the native type? */ - ZVAL_STR(result, zend_strpprintf(0, "%F", get_double_from_sqldata(var->sqldata))); + ZVAL_STR(result, zend_strpprintf_unchecked(0, "%.16H", get_double_from_sqldata(var->sqldata))); break; #ifdef SQL_BOOLEAN case SQL_BOOLEAN: diff --git a/ext/pdo_firebird/tests/gh10908.phpt b/ext/pdo_firebird/tests/gh10908.phpt index a1e8271ffd19b..bcd3bb1e20024 100644 --- a/ext/pdo_firebird/tests/gh10908.phpt +++ b/ext/pdo_firebird/tests/gh10908.phpt @@ -79,8 +79,8 @@ Array Array ( - [DBL] => 1.000000 - [0] => 1.000000 + [DBL] => 1 + [0] => 1 ) Array @@ -103,10 +103,10 @@ Array [1] => ABC [NUM] => 12.340 [2] => 12.340 - [DBL] => 1.000000 - [3] => 1.000000 - [FLT] => 2.000000 - [4] => 2.000000 + [DBL] => 1 + [3] => 1 + [FLT] => 2 + [4] => 2 [TS] => 2023-03-24 17:39:00 [5] => 2023-03-24 17:39:00 [MYDATE] => 2023-03-24 diff --git a/ext/pdo_firebird/tests/gh13119.phpt b/ext/pdo_firebird/tests/gh13119.phpt new file mode 100644 index 0000000000000..29524e3d3bf95 --- /dev/null +++ b/ext/pdo_firebird/tests/gh13119.phpt @@ -0,0 +1,77 @@ +--TEST-- +GH-13119 (float, double value is incorrect) +--EXTENSIONS-- +pdo_firebird +--SKIPIF-- + +--XLEAK-- +A bug in firebird causes a memory leak when calling `isc_attach_database()`. +See https://github.com/FirebirdSQL/firebird/issues/7849 +--FILE-- +exec('CREATE TABLE gh13119 (f_val FLOAT, d_val DOUBLE PRECISION)'); + +$dbh->exec('INSERT INTO gh13119 VALUES (0.1, 0.1)'); +$dbh->exec('INSERT INTO gh13119 VALUES (0.0000000000000001, 0.0000000000000001)'); +$dbh->exec('INSERT INTO gh13119 VALUES (12.000000, 12.00000000000000)'); +$dbh->exec('INSERT INTO gh13119 VALUES (12.000001, 12.00000000000001)'); +$dbh->exec('INSERT INTO gh13119 VALUES (12.345678, 12.34567890123456)'); +$dbh->exec('INSERT INTO gh13119 VALUES (0.0000000000000000012345678, 0.000000000000000001234567890123456)'); + +$stmt = $dbh->query('select * from gh13119'); +var_dump($stmt->fetchAll(PDO::FETCH_ASSOC)); +?> +--CLEAN-- +exec('DROP TABLE gh13119'); +unset($dbh); +?> +--EXPECT-- +array(6) { + [0]=> + array(2) { + ["F_VAL"]=> + string(3) "0.1" + ["D_VAL"]=> + string(3) "0.1" + } + [1]=> + array(2) { + ["F_VAL"]=> + string(7) "1.0E-16" + ["D_VAL"]=> + string(7) "1.0E-16" + } + [2]=> + array(2) { + ["F_VAL"]=> + string(2) "12" + ["D_VAL"]=> + string(2) "12" + } + [3]=> + array(2) { + ["F_VAL"]=> + string(9) "12.000001" + ["D_VAL"]=> + string(17) "12.00000000000001" + } + [4]=> + array(2) { + ["F_VAL"]=> + string(9) "12.345678" + ["D_VAL"]=> + string(17) "12.34567890123456" + } + [5]=> + array(2) { + ["F_VAL"]=> + string(13) "1.2345678E-18" + ["D_VAL"]=> + string(21) "1.234567890123456E-18" + } +} diff --git a/ext/phar/phar.1.in b/ext/phar/phar.1.in index bf4fd964d6386..f0046681d32e5 100644 --- a/ext/phar/phar.1.in +++ b/ext/phar/phar.1.in @@ -1,4 +1,4 @@ -.TH PHAR 1 "2023" "The PHP Group" "User Commands" +.TH PHAR 1 "2024" "The PHP Group" "User Commands" .SH NAME phar, phar.phar \- PHAR (PHP archive) command line tool .SH SYNOPSIS diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 0ab5d096836c4..2225d21d187a9 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -608,7 +608,8 @@ PHP_METHOD(Phar, webPhar) if ((sapi_mod_name_len == sizeof("cgi-fcgi") - 1 && !strncmp(sapi_module.name, "cgi-fcgi", sizeof("cgi-fcgi") - 1)) || (sapi_mod_name_len == sizeof("fpm-fcgi") - 1 && !strncmp(sapi_module.name, "fpm-fcgi", sizeof("fpm-fcgi") - 1)) - || (sapi_mod_name_len == sizeof("cgi") - 1 && !strncmp(sapi_module.name, "cgi", sizeof("cgi") - 1))) { + || (sapi_mod_name_len == sizeof("cgi") - 1 && !strncmp(sapi_module.name, "cgi", sizeof("cgi") - 1)) + || (sapi_mod_name_len == sizeof("litespeed") - 1 && !strncmp(sapi_module.name, "litespeed", sizeof("litespeed") - 1))) { if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) != IS_UNDEF) { HashTable *_server = Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]); diff --git a/ext/phar/tests/bug77432.phpt b/ext/phar/tests/bug77432.phpt index 12480856b3952..b3b927a5439df 100644 --- a/ext/phar/tests/bug77432.phpt +++ b/ext/phar/tests/bug77432.phpt @@ -1,14 +1,10 @@ --TEST-- Bug #77432 (Segmentation fault on including phar file) ---SKIPIF-- - --EXTENSIONS-- phar --INI-- +opcache.enable=0 +error_reporting=-1 phar.readonly=0 --FILE-- extractTo(__DIR__ . '/out_gh13037/'); +echo file_get_contents(__DIR__ . '/out_gh13037/test'); +?> +--CLEAN-- + +--EXPECT-- +hello diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 90b36d94e5b8d..a7b4678660843 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -386,8 +386,6 @@ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alia entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp); entry.flags = PHAR_ENT_PERM_DEF_FILE; entry.header_offset = PHAR_GET_32(zipentry.offset); - entry.offset = entry.offset_abs = PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + PHAR_GET_16(zipentry.filename_len) + - PHAR_GET_16(zipentry.extra_len); if (PHAR_GET_16(zipentry.flags) & PHAR_ZIP_FLAG_ENCRYPTED) { PHAR_ZIP_FAIL("Cannot process encrypted zip files"); @@ -417,6 +415,42 @@ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alia entry.is_dir = 0; } + phar_zip_file_header local; /* Warning: only filled in when the entry is not a directory! */ + if (!entry.is_dir) { + /* A file has a central directory entry, and a local file header. Both of these contain the filename + * and the extra field data. However, at least the extra field data does not have to match between the two! + * This happens for example for the "Extended Timestamp extra field" where the local header has 2 extra fields + * in comparison to the central header. So we have to use the local header to find the right offset to the file + * contents, otherwise we're reading some garbage bytes before reading the actual file contents. */ + zend_off_t current_central_dir_pos = php_stream_tell(fp); + + php_stream_seek(fp, entry.header_offset, SEEK_SET); + if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) { + pefree(entry.filename, entry.is_persistent); + PHAR_ZIP_FAIL("phar error: internal corruption (cannot read local file header)"); + } + php_stream_seek(fp, current_central_dir_pos, SEEK_SET); + + /* verify local header + * Note: normally I'd check the crc32, and file sizes too here, but that breaks tests zip/bug48791.phpt & zip/odt.phpt, + * suggesting that something may be wrong with those files or the assumption doesn't hold. Anyway, the other checks + * _are_ performed for the alias file as was done in the past too. */ + if (entry.filename_len != PHAR_GET_16(local.filename_len)) { + pefree(entry.filename, entry.is_persistent); + PHAR_ZIP_FAIL("phar error: internal corruption (local file header does not match central directory)"); + } + + entry.offset = entry.offset_abs = entry.header_offset + + sizeof(phar_zip_file_header) + + entry.filename_len + + PHAR_GET_16(local.extra_len); + } else { + entry.offset = entry.offset_abs = entry.header_offset + + sizeof(phar_zip_file_header) + + entry.filename_len + + PHAR_GET_16(zipentry.extra_len); + } + if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) { size_t read; php_stream *sigfile; @@ -444,7 +478,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alia if (metadata) { php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len)); } - php_stream_seek(fp, sizeof(phar_zip_file_header) + entry.header_offset + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET); + php_stream_seek(fp, entry.offset, SEEK_SET); sig = (char *) emalloc(entry.uncompressed_filesize); read = php_stream_read(fp, sig, entry.uncompressed_filesize); if (read != entry.uncompressed_filesize || read <= 8) { @@ -562,28 +596,17 @@ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alia if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) { php_stream_filter *filter; - zend_off_t saveloc; - /* verify local file header */ - phar_zip_file_header local; /* archive alias found */ - saveloc = php_stream_tell(fp); - php_stream_seek(fp, PHAR_GET_32(zipentry.offset), SEEK_SET); - - if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) { - pefree(entry.filename, entry.is_persistent); - PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (cannot read local file header for alias)"); - } /* verify local header */ - if (entry.filename_len != PHAR_GET_16(local.filename_len) || entry.crc32 != PHAR_GET_32(local.crc32) || entry.uncompressed_filesize != PHAR_GET_32(local.uncompsize) || entry.compressed_filesize != PHAR_GET_32(local.compsize)) { + ZEND_ASSERT(!entry.is_dir); + if (entry.crc32 != PHAR_GET_32(local.crc32) || entry.uncompressed_filesize != PHAR_GET_32(local.uncompsize) || entry.compressed_filesize != PHAR_GET_32(local.compsize)) { pefree(entry.filename, entry.is_persistent); PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)"); } - /* construct actual offset to file start - local extra_len can be different from central extra_len */ - entry.offset = entry.offset_abs = - sizeof(local) + entry.header_offset + PHAR_GET_16(local.filename_len) + PHAR_GET_16(local.extra_len); + zend_off_t restore_pos = php_stream_tell(fp); php_stream_seek(fp, entry.offset, SEEK_SET); /* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */ fp->writepos = 0; @@ -679,7 +702,7 @@ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alia } /* return to central directory parsing */ - php_stream_seek(fp, saveloc, SEEK_SET); + php_stream_seek(fp, restore_pos, SEEK_SET); } phar_set_inode(&entry); diff --git a/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt b/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt index e6fd2d7672343..2cfe68a467891 100644 --- a/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt +++ b/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt @@ -43,6 +43,18 @@ try { echo $e->getMessage(), PHP_EOL; } +try { + var_dump(randomizer()->pickArrayKeys(range(1, 1234), 1)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->pickArrayKeys(range(1, 1234), 10)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + try { var_dump(randomizer()->shuffleBytes('foobar')); } catch (Random\BrokenRandomEngineError $e) { @@ -70,3 +82,5 @@ Failed to generate an acceptable random number in 50 attempts Failed to generate an acceptable random number in 50 attempts Failed to generate an acceptable random number in 50 attempts Failed to generate an acceptable random number in 50 attempts +Failed to generate an acceptable random number in 50 attempts +Failed to generate an acceptable random number in 50 attempts 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 13ad0637fc33d..6aec7180b1ee5 100644 --- a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt +++ b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt @@ -43,6 +43,18 @@ try { echo $e->getMessage(), PHP_EOL; } +try { + var_dump(randomizer()->pickArrayKeys(range(1, 1234), 1)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->pickArrayKeys(range(1, 1234), 10)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + try { var_dump(randomizer()->shuffleBytes('foobar')); } catch (Random\BrokenRandomEngineError $e) { @@ -70,3 +82,5 @@ A random engine must return a non-empty string A random engine must return a non-empty string A random engine must return a non-empty string A random engine must return a non-empty string +A random engine must return a non-empty string +A random engine must return a non-empty string diff --git a/ext/random/tests/03_randomizer/engine_unsafe_nul.phpt b/ext/random/tests/03_randomizer/engine_unsafe_nul.phpt new file mode 100644 index 0000000000000..ff53b83b207a6 --- /dev/null +++ b/ext/random/tests/03_randomizer/engine_unsafe_nul.phpt @@ -0,0 +1,336 @@ +--TEST-- +Random: Randomizer: Nul engines are correctly handled +--FILE-- +getInt(0, 1234)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->nextInt()); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(bin2hex(randomizer()->getBytes(1))); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->shuffleArray(range(1, 123))); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->pickArrayKeys(range(1, 123), 1)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->pickArrayKeys(range(1, 123), 10)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->shuffleBytes('foobar')); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->getBytesFromString('123', 10)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->getBytesFromString(str_repeat('a', 500), 10)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +int(0) +int(0) +string(2) "00" +array(123) { + [0]=> + int(2) + [1]=> + int(3) + [2]=> + int(4) + [3]=> + int(5) + [4]=> + int(6) + [5]=> + int(7) + [6]=> + int(8) + [7]=> + int(9) + [8]=> + int(10) + [9]=> + int(11) + [10]=> + int(12) + [11]=> + int(13) + [12]=> + int(14) + [13]=> + int(15) + [14]=> + int(16) + [15]=> + int(17) + [16]=> + int(18) + [17]=> + int(19) + [18]=> + int(20) + [19]=> + int(21) + [20]=> + int(22) + [21]=> + int(23) + [22]=> + int(24) + [23]=> + int(25) + [24]=> + int(26) + [25]=> + int(27) + [26]=> + int(28) + [27]=> + int(29) + [28]=> + int(30) + [29]=> + int(31) + [30]=> + int(32) + [31]=> + int(33) + [32]=> + int(34) + [33]=> + int(35) + [34]=> + int(36) + [35]=> + int(37) + [36]=> + int(38) + [37]=> + int(39) + [38]=> + int(40) + [39]=> + int(41) + [40]=> + int(42) + [41]=> + int(43) + [42]=> + int(44) + [43]=> + int(45) + [44]=> + int(46) + [45]=> + int(47) + [46]=> + int(48) + [47]=> + int(49) + [48]=> + int(50) + [49]=> + int(51) + [50]=> + int(52) + [51]=> + int(53) + [52]=> + int(54) + [53]=> + int(55) + [54]=> + int(56) + [55]=> + int(57) + [56]=> + int(58) + [57]=> + int(59) + [58]=> + int(60) + [59]=> + int(61) + [60]=> + int(62) + [61]=> + int(63) + [62]=> + int(64) + [63]=> + int(65) + [64]=> + int(66) + [65]=> + int(67) + [66]=> + int(68) + [67]=> + int(69) + [68]=> + int(70) + [69]=> + int(71) + [70]=> + int(72) + [71]=> + int(73) + [72]=> + int(74) + [73]=> + int(75) + [74]=> + int(76) + [75]=> + int(77) + [76]=> + int(78) + [77]=> + int(79) + [78]=> + int(80) + [79]=> + int(81) + [80]=> + int(82) + [81]=> + int(83) + [82]=> + int(84) + [83]=> + int(85) + [84]=> + int(86) + [85]=> + int(87) + [86]=> + int(88) + [87]=> + int(89) + [88]=> + int(90) + [89]=> + int(91) + [90]=> + int(92) + [91]=> + int(93) + [92]=> + int(94) + [93]=> + int(95) + [94]=> + int(96) + [95]=> + int(97) + [96]=> + int(98) + [97]=> + int(99) + [98]=> + int(100) + [99]=> + int(101) + [100]=> + int(102) + [101]=> + int(103) + [102]=> + int(104) + [103]=> + int(105) + [104]=> + int(106) + [105]=> + int(107) + [106]=> + int(108) + [107]=> + int(109) + [108]=> + int(110) + [109]=> + int(111) + [110]=> + int(112) + [111]=> + int(113) + [112]=> + int(114) + [113]=> + int(115) + [114]=> + int(116) + [115]=> + int(117) + [116]=> + int(118) + [117]=> + int(119) + [118]=> + int(120) + [119]=> + int(121) + [120]=> + int(122) + [121]=> + int(123) + [122]=> + int(1) +} +array(1) { + [0]=> + int(0) +} +Failed to generate an acceptable random number in 50 attempts +string(6) "oobarf" +string(10) "1111111111" +string(10) "aaaaaaaaaa" diff --git a/ext/session/session.c b/ext/session/session.c index 3ec9315d882d4..0c869b7e57d82 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -258,18 +258,28 @@ static zend_string *php_session_encode(void) /* {{{ */ } /* }}} */ +static ZEND_COLD void php_session_cancel_decode(void) +{ + php_session_destroy(); + php_session_track_init(); + php_error_docref(NULL, E_WARNING, "Failed to decode session object. Session has been destroyed"); +} + static zend_result php_session_decode(zend_string *data) /* {{{ */ { if (!PS(serializer)) { php_error_docref(NULL, E_WARNING, "Unknown session.serialize_handler. Failed to decode session object"); return FAILURE; } - if (PS(serializer)->decode(ZSTR_VAL(data), ZSTR_LEN(data)) == FAILURE) { - php_session_destroy(); - php_session_track_init(); - php_error_docref(NULL, E_WARNING, "Failed to decode session object. Session has been destroyed"); - return FAILURE; - } + zend_try { + if (PS(serializer)->decode(ZSTR_VAL(data), ZSTR_LEN(data)) == FAILURE) { + php_session_cancel_decode(); + return FAILURE; + } + } zend_catch { + php_session_cancel_decode(); + zend_bailout(); + } zend_end_try(); return SUCCESS; } /* }}} */ diff --git a/ext/session/tests/gh12504.phpt b/ext/session/tests/gh12504.phpt new file mode 100644 index 0000000000000..eb19424eb5005 --- /dev/null +++ b/ext/session/tests/gh12504.phpt @@ -0,0 +1,62 @@ +--TEST-- +GH-12504 (Corrupted session written when there's a fatal error in autoloader) +--EXTENSIONS-- +session +--FILE-- + +--EXPECTF-- +Fatal error: Default value for property of type int may not be null. Use the nullable type ?int to allow null default value in %s on line %d + +Warning: Unknown: Failed to decode session object. Session has been destroyed in Unknown on line 0 +In shutdown function +array(0) { +} diff --git a/ext/standard/array.c b/ext/standard/array.c index eee260f224319..388b15a0879bb 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2924,8 +2924,8 @@ PHP_FUNCTION(range) /* If the range is given as strings, generate an array of characters. */ if (start_type >= IS_STRING || end_type >= IS_STRING) { - /* If one of the inputs is NOT a string */ - if (UNEXPECTED(start_type + end_type < 2*IS_STRING)) { + /* If one of the inputs is NOT a string nor single-byte string */ + if (UNEXPECTED(start_type < IS_STRING || end_type < IS_STRING)) { if (start_type < IS_STRING) { if (end_type != IS_ARRAY) { php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a single byte string if" @@ -6102,6 +6102,9 @@ PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status * * specific offset using linear scan. */ i = 0; randval = algo->range(status, 0, num_avail - 1); + if (EG(exception)) { + return false; + } ZEND_HASH_FOREACH_KEY(ht, num_key, string_key) { if (i == randval) { if (string_key) { @@ -6122,6 +6125,9 @@ PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status * if (HT_IS_PACKED(ht)) { do { randval = algo->range(status, 0, ht->nNumUsed - 1); + if (EG(exception)) { + return false; + } zv = &ht->arPacked[randval]; if (!Z_ISUNDEF_P(zv)) { ZVAL_LONG(retval, randval); @@ -6131,6 +6137,9 @@ PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status * } else { do { randval = algo->range(status, 0, ht->nNumUsed - 1); + if (EG(exception)) { + return false; + } b = &ht->arData[randval]; if (!Z_ISUNDEF(b->val)) { if (b->key) { @@ -6163,11 +6172,24 @@ PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status * zend_bitset_clear(bitset, bitset_len); i = num_req; + int failures = 0; while (i) { randval = algo->range(status, 0, num_avail - 1); - if (!zend_bitset_in(bitset, randval)) { + if (EG(exception)) { + goto fail; + } + if (zend_bitset_in(bitset, randval)) { + if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) { + if (!silent) { + zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS); + } + + goto fail; + } + } else { zend_bitset_incl(bitset, randval); i--; + failures = 0; } } @@ -6191,6 +6213,11 @@ PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status * free_alloca(bitset, use_heap); return true; + + fail: + free_alloca(bitset, use_heap); + + return false; } /* }}} */ diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 66b458897adb4..be9b024258ee8 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -2337,7 +2337,6 @@ function implode(string|array $separator, ?array $array = null): string {} function join(string|array $separator, ?array $array = null): string {} /** - * @compile-time-eval * @refcount 1 */ function strtok(string $string, ?string $token = null): string|false {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 39500b2b582dc..99e08feddf590 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 487cee0751d47b18bf0a8fbdb050313783f1b369 */ + * Stub hash: 7389d094a842a2289cd32cb37386e5e40ea7e031 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -3087,7 +3087,7 @@ static const zend_function_entry ext_functions[] = { ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(explode, arginfo_explode) ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(implode, arginfo_implode) ZEND_FALIAS(join, implode, arginfo_join) - ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strtok, arginfo_strtok) + ZEND_FE(strtok, arginfo_strtok) ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strtoupper, arginfo_strtoupper) ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(strtolower, arginfo_strtolower) ZEND_FE(str_increment, arginfo_str_increment) diff --git a/ext/standard/tests/array/range/gh13094.phpt b/ext/standard/tests/array/range/gh13094.phpt new file mode 100644 index 0000000000000..2e70adb65da72 --- /dev/null +++ b/ext/standard/tests/array/range/gh13094.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-13094 (range(9.9, '0') causes segmentation fault) +--FILE-- + +--EXPECT-- +array(10) { + [0]=> + float(9.9) + [1]=> + float(8.9) + [2]=> + float(7.9) + [3]=> + float(6.9) + [4]=> + float(5.9) + [5]=> + float(4.9) + [6]=> + float(3.9000000000000004) + [7]=> + float(2.9000000000000004) + [8]=> + float(1.9000000000000004) + [9]=> + float(0.9000000000000004) +} diff --git a/ext/standard/tests/file/gh13136.phpt b/ext/standard/tests/file/gh13136.phpt new file mode 100644 index 0000000000000..a90c07c91c280 --- /dev/null +++ b/ext/standard/tests/file/gh13136.phpt @@ -0,0 +1,55 @@ +--TEST-- +GH-13071 (Copying large files using mmap-able source streams may exhaust available memory and fail) +--FILE-- +trim_path($path); + $this->file = fopen($path, $mode); + return true; + } + + public function stream_close() { + fclose($this->file); + return true; + } + + public function stream_write($data) { + self::$writes++; + return fwrite($this->file, $data); + } + + public function url_stat($path, $flags) { + return false; + } + + private function trim_path(string $path): string { + return substr($path, strlen("up://")); + } +} + +file_put_contents(__DIR__ . "/gh13071.tmp", str_repeat("a", 1024 * 1024 * 8)); + +stream_wrapper_register("up", CustomStream::class, STREAM_IS_URL); + +$old_limit = ini_get("memory_limit"); +ini_set("memory_limit", memory_get_usage(true) + 5 * 1024 * 1024); +copy(__DIR__ . "/gh13071.tmp", "up://" . __DIR__ . "/gh13071.out.tmp"); +ini_set("memory_limit", $old_limit); + +echo "Done ", CustomStream::$writes, " writes\n"; + +?> +--CLEAN-- + +--EXPECT-- +Done 1024 writes diff --git a/ext/standard/tests/file/userstreams_006.phpt b/ext/standard/tests/file/userstreams_006.phpt index a432937dac29f..e5d341379f6d1 100644 --- a/ext/standard/tests/file/userstreams_006.phpt +++ b/ext/standard/tests/file/userstreams_006.phpt @@ -34,5 +34,6 @@ bool(true) option: 3, 2, 50 int(-1) int(8192) -size: 70 +size: 42 +size: 28 int(70) diff --git a/ext/standard/tests/streams/set_file_buffer.phpt b/ext/standard/tests/streams/set_file_buffer.phpt index ef808a24fd002..c39ba56cf6cfb 100644 --- a/ext/standard/tests/streams/set_file_buffer.phpt +++ b/ext/standard/tests/streams/set_file_buffer.phpt @@ -39,4 +39,5 @@ option: %d, %d, %d int(%i) int(%d) size: %d +size: 28 int(%d) diff --git a/ext/standard/tests/streams/stream_set_chunk_size.phpt b/ext/standard/tests/streams/stream_set_chunk_size.phpt index 77d9bac00ea4f..8bb5b46b7f94a 100644 --- a/ext/standard/tests/streams/stream_set_chunk_size.phpt +++ b/ext/standard/tests/streams/stream_set_chunk_size.phpt @@ -35,7 +35,7 @@ echo "should return previous chunk size (8192)\n"; var_dump(stream_set_chunk_size($f, 1)); echo "should be read without buffer (\$count == 10000)\n"; var_dump(strlen(fread($f, 10000))); -echo "should have no effect on writes\n"; +echo "should elicit 3 writes\n"; var_dump(fwrite($f, str_repeat('b', 3))); echo "should return previous chunk size (1)\n"; @@ -46,7 +46,7 @@ echo "should elicit one read of size 100 (chunk size)\n"; var_dump(strlen(fread($f, 50))); echo "should elicit no read because there is sufficient cached data\n"; var_dump(strlen(fread($f, 50))); -echo "should have no effect on writes\n"; +echo "should elicit 3 writes\n"; var_dump(strlen(fwrite($f, str_repeat('b', 250)))); echo "\nerror conditions\n"; @@ -68,8 +68,10 @@ int(8192) should be read without buffer ($count == 10000) read with size: 10000 int(10000) -should have no effect on writes -write with size: 3 +should elicit 3 writes +write with size: 1 +write with size: 1 +write with size: 1 int(3) should return previous chunk size (1) int(1) @@ -81,8 +83,10 @@ read with size: 100 int(50) should elicit no read because there is sufficient cached data int(50) -should have no effect on writes -write with size: 250 +should elicit 3 writes +write with size: 100 +write with size: 100 +write with size: 50 int(3) error conditions diff --git a/main/main.c b/main/main.c index fa296eed3074f..fb68bd1b3dd39 100644 --- a/main/main.c +++ b/main/main.c @@ -1361,19 +1361,25 @@ static ZEND_COLD void php_error_cb(int orig_type, zend_string *error_filename, c php_printf("%s
\n%s: %s in %s on line %" PRIu32 "
\n%s", STR_PRINT(prepend_string), error_type_str, ZSTR_VAL(buf), ZSTR_VAL(error_filename), error_lineno, STR_PRINT(append_string)); zend_string_free(buf); } else { - php_printf("%s
\n%s: %s in %s on line %" PRIu32 "
\n%s", STR_PRINT(prepend_string), error_type_str, ZSTR_VAL(message), ZSTR_VAL(error_filename), error_lineno, STR_PRINT(append_string)); + zval tmp; + ZVAL_STR(&tmp, message); + php_printf_unchecked("%s
\n%s: %Z in %s on line %" PRIu32 "
\n%s", STR_PRINT(prepend_string), error_type_str, &tmp, ZSTR_VAL(error_filename), error_lineno, STR_PRINT(append_string)); } } else { /* Write CLI/CGI errors to stderr if display_errors = "stderr" */ if ((!strcmp(sapi_module.name, "cli") || !strcmp(sapi_module.name, "cgi") || !strcmp(sapi_module.name, "phpdbg")) && PG(display_errors) == PHP_DISPLAY_ERRORS_STDERR ) { - fprintf(stderr, "%s: %s in %s on line %" PRIu32 "\n", error_type_str, ZSTR_VAL(message), ZSTR_VAL(error_filename), error_lineno); + fprintf(stderr, "%s: ", error_type_str); + fwrite(ZSTR_VAL(message), sizeof(char), ZSTR_LEN(message), stderr); + fprintf(stderr, " in %s on line %" PRIu32 "\n", ZSTR_VAL(error_filename), error_lineno); #ifdef PHP_WIN32 fflush(stderr); #endif } else { - php_printf("%s\n%s: %s in %s on line %" PRIu32 "\n%s", STR_PRINT(prepend_string), error_type_str, ZSTR_VAL(message), ZSTR_VAL(error_filename), error_lineno, STR_PRINT(append_string)); + zval tmp; + ZVAL_STR(&tmp, message); + php_printf_unchecked("%s\n%s: %Z in %s on line %" PRIu32 "\n%s", STR_PRINT(prepend_string), error_type_str, &tmp, ZSTR_VAL(error_filename), error_lineno, STR_PRINT(append_string)); } } } diff --git a/main/php_version.h b/main/php_version.h index f6633fe34519b..882d64be43ad3 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 3 -#define PHP_RELEASE_VERSION 2 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.2-dev" -#define PHP_VERSION_ID 80302 +#define PHP_RELEASE_VERSION 3 +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.3.3" +#define PHP_VERSION_ID 80303 diff --git a/main/streams/streams.c b/main/streams/streams.c index 3f7a16c51101c..9f79821f0c9ff 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1153,8 +1153,15 @@ static ssize_t _php_stream_write_buffer(php_stream *stream, const char *buf, siz bool old_eof = stream->eof; + /* See GH-13071: userspace stream is subject to the memory limit. */ + size_t chunk_size = count; + if (php_stream_is(stream, PHP_STREAM_IS_USERSPACE)) { + /* If the stream is unbuffered, we can only write one byte at a time. */ + chunk_size = stream->chunk_size; + } + while (count > 0) { - ssize_t justwrote = stream->ops->write(stream, buf, count); + ssize_t justwrote = stream->ops->write(stream, buf, MIN(chunk_size, count)); if (justwrote <= 0) { /* If we already successfully wrote some bytes and a write error occurred * later, report the successfully written bytes. */ diff --git a/run-tests.php b/run-tests.php index 16b74e5804a66..71a8fdc80fe5f 100755 --- a/run-tests.php +++ b/run-tests.php @@ -4138,7 +4138,7 @@ public function calculateCommonSubsequence(array $from, array $to): array if ($cFrom === 1) { foreach ($to as $toV) { if (($this->isEqual)($from[0], $toV)) { - return [$from[0]]; + return [$toV]; } } diff --git a/sapi/cgi/config9.m4 b/sapi/cgi/config9.m4 index 04bd70b269ee6..f9d7b1b3943c7 100644 --- a/sapi/cgi/config9.m4 +++ b/sapi/cgi/config9.m4 @@ -46,16 +46,16 @@ if test "$PHP_CGI" != "no"; then case $host_alias in *aix*) if test "$php_sapi_module" = "shared"; then - BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" else - BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" fi ;; *darwin*) BUILD_CGI="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FASTCGI_OBJS:.lo=.o) \$(PHP_CGI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" ;; *) - BUILD_CGI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FASTCGI_OBJS:.lo=.o) \$(PHP_CGI_OBJS:.lo=.o) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" + BUILD_CGI="\$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FASTCGI_OBJS:.lo=.o) \$(PHP_CGI_OBJS:.lo=.o) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)" ;; esac diff --git a/sapi/cli/config.m4 b/sapi/cli/config.m4 index d17d531683605..1b8e67f0b394e 100644 --- a/sapi/cli/config.m4 +++ b/sapi/cli/config.m4 @@ -33,16 +33,16 @@ if test "$PHP_CLI" != "no"; then case $host_alias in *aix*) if test "$php_sapi_module" = "shared"; then - BUILD_CLI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" + BUILD_CLI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" else - BUILD_CLI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" + BUILD_CLI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" fi ;; *darwin*) BUILD_CLI="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_CLI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" ;; *) - BUILD_CLI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_CLI_OBJS:.lo=.o) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" + BUILD_CLI="\$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_CLI_OBJS:.lo=.o) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)" ;; esac diff --git a/sapi/cli/php.1.in b/sapi/cli/php.1.in index 4054cda49330d..a49e9cac13fb5 100644 --- a/sapi/cli/php.1.in +++ b/sapi/cli/php.1.in @@ -1,4 +1,4 @@ -.TH @program_prefix@php 1 "2023" "The PHP Group" "Scripting Language" +.TH @program_prefix@php 1 "2024" "The PHP Group" "Scripting Language" .SH NAME @program_prefix@php \- PHP Command Line Interface 'CLI' .P diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index 77dc32c56a908..729fb718014e4 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -719,13 +719,13 @@ if test "$PHP_FPM" != "no"; then case $host_alias in *aix*) - BUILD_FPM="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" + BUILD_FPM="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FASTCGI_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" ;; *darwin*) BUILD_FPM="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FASTCGI_OBJS:.lo=.o) \$(PHP_FPM_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" ;; *) - BUILD_FPM="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FASTCGI_OBJS:.lo=.o) \$(PHP_FPM_OBJS:.lo=.o) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" + BUILD_FPM="\$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FASTCGI_OBJS:.lo=.o) \$(PHP_FPM_OBJS:.lo=.o) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)" ;; esac diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c index b3ae2f69cc556..94a9ca6c7e604 100644 --- a/sapi/fpm/fpm/fpm_main.c +++ b/sapi/fpm/fpm/fpm_main.c @@ -1165,7 +1165,7 @@ static void init_request_info(void) size_t decoded_path_info_len = 0; if (strchr(path_info, '%')) { decoded_path_info = estrdup(path_info); - decoded_path_info_len = php_url_decode(decoded_path_info, strlen(path_info)); + decoded_path_info_len = php_raw_url_decode(decoded_path_info, strlen(path_info)); } size_t snlen = strlen(env_script_name); size_t env_script_file_info_start = 0; diff --git a/sapi/fpm/php-fpm.8.in b/sapi/fpm/php-fpm.8.in index e8bbfa7aafba2..821fd7390810e 100644 --- a/sapi/fpm/php-fpm.8.in +++ b/sapi/fpm/php-fpm.8.in @@ -1,4 +1,4 @@ -.TH PHP-FPM 8 "2023" "The PHP Group" "Scripting Language" +.TH PHP-FPM 8 "2024" "The PHP Group" "Scripting Language" .SH NAME .TP 15 php-fpm \- PHP FastCGI Process Manager 'PHP-FPM' diff --git a/sapi/fpm/tests/fcgi-env-pif-apache-pp-sn-strip-encoded-plus.phpt b/sapi/fpm/tests/fcgi-env-pif-apache-pp-sn-strip-encoded-plus.phpt new file mode 100644 index 0000000000000..4bef11ec668f0 --- /dev/null +++ b/sapi/fpm/tests/fcgi-env-pif-apache-pp-sn-strip-encoded-plus.phpt @@ -0,0 +1,54 @@ +--TEST-- +FPM: FastCGI env var path info fix for Apache ProxyPass SCRIPT_NAME encoded path and plush sign (GH-12996) +--SKIPIF-- + +--FILE-- +createSourceFileAndScriptName(); +$tester->start(); +$tester->expectLogStartNotices(); +$tester + ->request( + uri: $scriptName . '/1%202', + scriptFilename: "proxy:fcgi://" . $tester->getAddr() . $sourceFilePath . '/1%20+2', + scriptName: $scriptName . '/1 +2' + ) + ->expectBody([$scriptName, $scriptName . '/1 +2', $sourceFilePath, '/1%20+2', $scriptName . '/1%20+2']); +$tester->terminate(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fuzzer/Makefile.frag b/sapi/fuzzer/Makefile.frag index 34ec210a44111..9608e29d48910 100644 --- a/sapi/fuzzer/Makefile.frag +++ b/sapi/fuzzer/Makefile.frag @@ -1,6 +1,6 @@ fuzzer: $(PHP_FUZZER_BINARIES) -FUZZER_BUILD = $(LIBTOOL) --mode=link $(FUZZING_CC) -export-dynamic $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(EXTRA_LDFLAGS_PROGRAM) $(LDFLAGS) $(PHP_RPATHS) $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) $(FUZZING_LIB) +FUZZER_BUILD = $(LIBTOOL) --tag=CC --mode=link $(FUZZING_CC) -export-dynamic $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(EXTRA_LDFLAGS_PROGRAM) $(LDFLAGS) $(PHP_RPATHS) $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) $(FUZZING_LIB) $(SAPI_FUZZER_PATH)/php-fuzz-parser: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_PARSER_OBJS) $(FUZZER_BUILD) $(PHP_FUZZER_PARSER_OBJS) -o $@ diff --git a/sapi/fuzzer/fuzzer-sapi.c b/sapi/fuzzer/fuzzer-sapi.c index d0a9b553aa856..2a887a8813d81 100644 --- a/sapi/fuzzer/fuzzer-sapi.c +++ b/sapi/fuzzer/fuzzer-sapi.c @@ -283,7 +283,9 @@ int fuzzer_do_request_from_buffer( CG(compiled_filename) = NULL; /* ??? */ if (before_shutdown) { - before_shutdown(); + zend_try { + before_shutdown(); + } zend_end_try(); } fuzzer_request_shutdown(); diff --git a/sapi/litespeed/config.m4 b/sapi/litespeed/config.m4 index 89074053852c3..f18697203cb2f 100644 --- a/sapi/litespeed/config.m4 +++ b/sapi/litespeed/config.m4 @@ -15,10 +15,10 @@ if test "$PHP_LITESPEED" != "no"; then ;; *cygwin*) SAPI_LITESPEED_PATH=sapi/litespeed/php.exe - BUILD_LITESPEED="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_LITESPEED_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)" + BUILD_LITESPEED="\$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_LITESPEED_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)" ;; *) - BUILD_LITESPEED="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_LITESPEED_OBJS:.lo=.o) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)" + BUILD_LITESPEED="\$(LIBTOOL) --tag=CC --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_LITESPEED_OBJS:.lo=.o) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)" ;; esac diff --git a/sapi/phpdbg/config.m4 b/sapi/phpdbg/config.m4 index 10dd2029b4c32..e9357f4ae74db 100644 --- a/sapi/phpdbg/config.m4 +++ b/sapi/phpdbg/config.m4 @@ -85,7 +85,7 @@ if test "$BUILD_PHPDBG" = "" && test "$PHP_PHPDBG" != "no"; then BUILD_BINARY="sapi/phpdbg/phpdbg" BUILD_SHARED="sapi/phpdbg/libphpdbg.la" - BUILD_PHPDBG="\$(LIBTOOL) --mode=link \ + BUILD_PHPDBG="\$(LIBTOOL) --tag=CC --mode=link \ \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \ \$(PHP_GLOBAL_OBJS:.lo=.o) \ \$(PHP_BINARY_OBJS:.lo=.o) \ @@ -96,7 +96,7 @@ if test "$BUILD_PHPDBG" = "" && test "$PHP_PHPDBG" != "no"; then \$(PHP_FRAMEWORKS) \ -o \$(BUILD_BINARY)" - BUILD_PHPDBG_SHARED="\$(LIBTOOL) --mode=link \ + BUILD_PHPDBG_SHARED="\$(LIBTOOL) --tag=CC --mode=link \ \$(CC) -shared -Wl,-soname,libphpdbg.so -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \ \$(PHP_GLOBAL_OBJS) \ \$(PHP_BINARY_OBJS) \ diff --git a/sapi/phpdbg/phpdbg.1.in b/sapi/phpdbg/phpdbg.1.in index 3ab0ff472f9b5..169f0e46558c4 100644 --- a/sapi/phpdbg/phpdbg.1.in +++ b/sapi/phpdbg/phpdbg.1.in @@ -1,4 +1,4 @@ -.TH @program_prefix@phpdbg 1 "2023" "The PHP Group" "Scripting Language" +.TH @program_prefix@phpdbg 1 "2024" "The PHP Group" "Scripting Language" .SH NAME @program_prefix@phpdbg \- The interactive PHP debugger .SH SYNOPSIS diff --git a/sapi/phpdbg/tests/gh12962.phpt b/sapi/phpdbg/tests/gh12962.phpt index c5cf9425d7c47..f307547397b95 100644 --- a/sapi/phpdbg/tests/gh12962.phpt +++ b/sapi/phpdbg/tests/gh12962.phpt @@ -7,7 +7,7 @@ if (!getenv('TEST_PHPDBG_EXECUTABLE')) die("SKIP: No TEST_PHPDBG_EXECUTABLE spec --FILE-- --EXPECT-- Executed .phpdbginit diff --git a/scripts/man1/php-config.1.in b/scripts/man1/php-config.1.in index 77d3b2d005f6e..8ccd171641e14 100644 --- a/scripts/man1/php-config.1.in +++ b/scripts/man1/php-config.1.in @@ -1,4 +1,4 @@ -.TH @program_prefix@php\-config 1 "2023" "The PHP Group" "Scripting Language" +.TH @program_prefix@php\-config 1 "2024" "The PHP Group" "Scripting Language" .SH NAME @program_prefix@php\-config \- get information about PHP configuration and compile options .SH SYNOPSIS diff --git a/scripts/man1/phpize.1.in b/scripts/man1/phpize.1.in index e14aa7cc6a0a9..410a79c544754 100644 --- a/scripts/man1/phpize.1.in +++ b/scripts/man1/phpize.1.in @@ -1,4 +1,4 @@ -.TH @program_prefix@phpize 1 "2023" "The PHP Group" "Scripting Language" +.TH @program_prefix@phpize 1 "2024" "The PHP Group" "Scripting Language" .SH NAME @program_prefix@phpize \- prepare a PHP extension for compiling .SH SYNOPSIS