diff --git a/.github/actions/apt-x64/action.yml b/.github/actions/apt-x64/action.yml index a5b0db49e8270..abae59c8fdea4 100644 --- a/.github/actions/apt-x64/action.yml +++ b/.github/actions/apt-x64/action.yml @@ -5,6 +5,7 @@ runs: - shell: bash run: | set -x + sudo apt-get update sudo apt-get install \ bison \ @@ -58,11 +59,15 @@ runs: libjpeg-dev \ libpng-dev \ libfreetype6-dev + mkdir /opt/oracle wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip unzip instantclient-basiclite-linuxx64.zip && rm instantclient-basiclite-linuxx64.zip wget -nv https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip unzip instantclient-sdk-linuxx64.zip && rm instantclient-sdk-linuxx64.zip mv instantclient_*_* /opt/oracle/instantclient - # Interferes with libldap2 headers. + # interferes with libldap2 headers rm /opt/oracle/instantclient/sdk/include/ldap.h + # fix debug build warning: zend_signal: handler was replaced for signal (2) after startup + echo DISABLE_INTERRUPT=on > /opt/oracle/instantclient/network/admin/sqlnet.ora + sudo sh -c 'echo /opt/oracle/instantclient >/etc/ld.so.conf.d/oracle-instantclient.conf && ldconfig' diff --git a/.github/actions/install-linux/action.yml b/.github/actions/install-linux/action.yml index e0f6726dbd1d3..7ac2ae4c4fcb1 100644 --- a/.github/actions/install-linux/action.yml +++ b/.github/actions/install-linux/action.yml @@ -10,3 +10,5 @@ runs: sudo chmod 777 /etc/php.d echo mysqli.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/mysqli.ini echo pdo_mysql.default_socket=/var/run/mysqld/mysqld.sock > /etc/php.d/pdo_mysql.ini + echo extension=oci8.so > /etc/php.d/oci8.ini + echo extension=pdo_oci.so > /etc/php.d/pdo_oci.ini diff --git a/.github/actions/mssql/action.yml b/.github/actions/setup-mssql/action.yml similarity index 91% rename from .github/actions/mssql/action.yml rename to .github/actions/setup-mssql/action.yml index 894380644c1ea..c069744a21b59 100644 --- a/.github/actions/mssql/action.yml +++ b/.github/actions/setup-mssql/action.yml @@ -1,4 +1,4 @@ -name: Create mssql container +name: Create MSSQL container runs: using: composite steps: diff --git a/.github/actions/setup-oracle/action.yml b/.github/actions/setup-oracle/action.yml new file mode 100644 index 0000000000000..11c16fe93d525 --- /dev/null +++ b/.github/actions/setup-oracle/action.yml @@ -0,0 +1,13 @@ +name: Create Oracle container +runs: + using: composite + steps: + - shell: bash + run: | + set -x + docker run \ + -e "ORACLE_PASSWORD=pass" \ + -p 1521:1521 \ + --name oracle \ + -h oracle \ + -d gvenzl/oracle-xe:slim diff --git a/.github/actions/setup-x64/action.yml b/.github/actions/setup-x64/action.yml index 6cec51d4c8079..bb014bfe9de11 100644 --- a/.github/actions/setup-x64/action.yml +++ b/.github/actions/setup-x64/action.yml @@ -17,7 +17,7 @@ runs: docker exec sql1 /opt/mssql-tools/bin/sqlcmd -S 127.0.0.1 -U SA -P "" -Q "create login pdo_test with password='password', check_policy=off; create user pdo_test for login pdo_test; grant alter, control to pdo_test;" sudo locale-gen de_DE - ./.github/scripts/setup-slapd.sh + ./.github/scripts/setup-slapd.sh &>/dev/null sudo cp ext/snmp/tests/snmpd.conf /etc/snmp sudo cp ext/snmp/tests/bigtest /etc/snmp diff --git a/.github/actions/test-linux/action.yml b/.github/actions/test-linux/action.yml index c7dab609820dd..7db8fa58900d3 100644 --- a/.github/actions/test-linux/action.yml +++ b/.github/actions/test-linux/action.yml @@ -17,6 +17,12 @@ runs: export PDO_DBLIB_TEST_DSN="dblib:host=127.0.0.1;dbname=master;version=7.0" export PDO_DBLIB_TEST_USER="pdo_test" export PDO_DBLIB_TEST_PASS="password" + export PHP_OCI8_TEST_USER="system" + export PHP_OCI8_TEST_PASS="pass" + export PHP_OCI8_TEST_DB="localhost/XEPDB1" + export PDO_OCI_TEST_USER="system" + export PDO_OCI_TEST_PASS="pass" + export PDO_OCI_TEST_DSN="oci:dbname=localhost/XEPDB1;charset=AL32UTF8" export SKIP_IO_CAPTURE_TESTS=1 sapi/cli/php run-tests.php -P -q ${{ inputs.runTestsParameters }} \ -j$(/usr/bin/nproc) \ diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 33c9335ef72da..38d85d2b7224e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -47,8 +47,10 @@ jobs: uses: actions/checkout@v2 with: ref: ${{ matrix.branch.ref }} - - name: Create mssql container - uses: ./.github/actions/mssql + - name: Create MSSQL container + uses: ./.github/actions/setup-mssql + - name: Create Oracle container + uses: ./.github/actions/setup-oracle - name: apt uses: ./.github/actions/apt-x64 - name: ./configure @@ -158,8 +160,10 @@ jobs: steps: - name: git checkout uses: actions/checkout@v2 - - name: Create mssql container - uses: ./.github/actions/mssql + - name: Create MSSQL container + uses: ./.github/actions/setup-mssql + - name: Create Oracle container + uses: ./.github/actions/setup-oracle - name: apt uses: ./.github/actions/apt-x64 - name: Install gcovr diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index bd05e60d89572..520a701a0de43 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -32,8 +32,10 @@ jobs: steps: - name: git checkout uses: actions/checkout@v2 - - name: Create mssql container - uses: ./.github/actions/mssql + - name: Create MSSQL container + uses: ./.github/actions/setup-mssql + - name: Create Oracle container + uses: ./.github/actions/setup-oracle - name: apt uses: ./.github/actions/apt-x64 - name: ./configure diff --git a/NEWS b/NEWS index 3a5d072854716..bf5ac0bc21b24 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,53 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.1.10 +29 Sep 2022, PHP 8.1.11 + +- Core: + . Fixed bug #81726: phar wrapper: DOS when using quine gzip file. + (CVE-2022-31628). (cmb) + . Fixed bug #81727: Don't mangle HTTP variable names that clash with ones + that have a specific semantic meaning. (CVE-2022-31629). (Derick) + . Fixed bug GH-9323 (Crash in ZEND_RETURN/GC/zend_call_function) + (Tim Starling) + . Fixed bug GH-9361 (Segmentation fault on script exit #9379). (cmb, + Christian Schneider) + . Fixed bug GH-9447 (Invalid class FQN emitted by AST dump for new and class + constants in constant expressions). (ilutov) + +- DOM: + . Fixed bug #79451 (DOMDocument->replaceChild on doctype causes double free). + (Nathan Freeman) + +- FPM: + . Fixed bug GH-8885 (FPM access.log with stderr begins to write logs to + error_log after daemon reload). (Dmitry Menshikov) + . Fixed bug #77780 ("Headers already sent..." when previous connection was + aborted). (Jakub Zelenka) + +- GMP + . Fixed bug GH-9308 (GMP throws the wrong error when a GMP object is passed + to gmp_init()). (Girgias) + +- Intl + . Fixed bug GH-9421 (Incorrect argument number for ValueError in NumberFormatter). + (Girgias) + +- PCRE: + . Fixed pcre.jit on Apple Silicon. (Niklas Keller) + +- PDO_PGSQL: + . Fixed bug GH-9411 (PgSQL large object resource is incorrectly closed). + (Yurunsoft) + +- Reflection: + . Fixed bug GH-8932 (ReflectionFunction provides no way to get the called + class of a Closure). (cmb, Nicolas Grekas) + +- Streams: + . Fixed bug GH-9316 ($http_response_header is wrong for long status line). + (cmb, timwolla) + +01 Sep 2022, PHP 8.1.10 - Core: . Fixed --CGI-- support of run-tests.php. (cmb) @@ -39,16 +86,16 @@ PHP NEWS . Fixed bug GH-9164 (Segfault in zend_accel_class_hash_copy). (Arnaud, Sergei Turchanov) +- OpenSSL: + . Fixed bug GH-9339 (OpenSSL oid_file path check warning contains + uninitialized path). (Jakub Zelenka) + - PDO_SQLite: . Fixed bug GH-9032 (SQLite3 authorizer crashes on NULL values). (cmb) - SQLite3: . Fixed bug GH-9032 (SQLite3 authorizer crashes on NULL values). (cmb) -- Standard: - . Fixed bug GH-9296 (`ksort` behaves incorrectly on arrays with mixed keys). - (Denis Vaksman) - - Streams: . Fixed bug GH-8472 (The resource returned by stream_socket_accept may have incorrect metadata). (Jakub Zelenka) diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index 83cab99d0b836..80911a6f1a1b6 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -367,7 +367,9 @@ static bool opline_supports_assign_contraction( return opline->op1_type != IS_CV || opline->op1.var != cv_var; } - if (opline->opcode == ZEND_ASSIGN_OP + if ((opline->opcode == ZEND_ASSIGN_OP + || opline->opcode == ZEND_ASSIGN_OBJ + || opline->opcode == ZEND_ASSIGN_DIM) && opline->op1_type == IS_CV && opline->op1.var == cv_var && zend_may_throw(opline, &ssa->ops[ssa->vars[src_var].definition], op_array, ssa)) { diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 5dec2dd24a3db..0654464f108dc 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -1951,6 +1951,9 @@ static uint32_t assign_dim_array_result_type( tmp |= MAY_BE_ARRAY_KEY_STRING; if (dim_op_type != IS_CONST) { // FIXME: numeric string + if (arr_type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) { + tmp |= MAY_BE_ARRAY_PACKED; + } tmp |= MAY_BE_HASH_ONLY(arr_type) ? MAY_BE_ARRAY_NUMERIC_HASH : MAY_BE_ARRAY_KEY_LONG; } } @@ -3375,6 +3378,9 @@ static zend_always_inline int _zend_update_type_info( opline->op1_type, opline->result_type == IS_VAR, opline->op2_type == IS_UNUSED); + if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG && (t1 & (MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_RESOURCE))) { + tmp |= MAY_BE_NULL; + } if (opline->opcode == ZEND_FETCH_DIM_IS && (t1 & MAY_BE_STRING)) { tmp |= MAY_BE_NULL; } diff --git a/Zend/tests/gh9407.phpt b/Zend/tests/gh9407.phpt new file mode 100644 index 0000000000000..6712c9ccc9c3f --- /dev/null +++ b/Zend/tests/gh9407.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-9407: LSP error in eval'd code refers to wrong class for static type +--FILE-- + +--EXPECTF-- +Fatal error: Declaration of B::duplicate(): A must be compatible with A::duplicate(): static in %s : eval()'d code on line %d diff --git a/Zend/tests/named_params/call_user_func_array_variadic.phpt b/Zend/tests/named_params/call_user_func_array_variadic.phpt new file mode 100644 index 0000000000000..89a8f7cb10e33 --- /dev/null +++ b/Zend/tests/named_params/call_user_func_array_variadic.phpt @@ -0,0 +1,13 @@ +--TEST-- +call_user_func_array() with extra named parameters +--FILE-- + 1); +call_user_func_array("array_multisort", $args); +?> +--EXPECTF-- +Fatal error: Uncaught ArgumentCountError: array_multisort() expects at least 1 argument, 0 given in %scall_user_func_array_variadic.php:3 +Stack trace: +#0 %scall_user_func_array_variadic.php(3): array_multisort(: 1) +#1 {main} + thrown in %scall_user_func_array_variadic.php on line 3 diff --git a/Zend/zend.h b/Zend/zend.h index 001bf329b9e49..ba80a804ed785 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.1.10-dev" +#define ZEND_VERSION "4.1.11" #define ZEND_ENGINE_3 diff --git a/Zend/zend_alloc_sizes.h b/Zend/zend_alloc_sizes.h index 9f1c00eaad568..502b982a50522 100644 --- a/Zend/zend_alloc_sizes.h +++ b/Zend/zend_alloc_sizes.h @@ -19,7 +19,7 @@ #ifndef ZEND_ALLOC_SIZES_H #define ZEND_ALLOC_SIZES_H -#define ZEND_MM_CHUNK_SIZE (2 * 1024 * 1024) /* 2 MB */ +#define ZEND_MM_CHUNK_SIZE ((size_t) (2 * 1024 * 1024)) /* 2 MB */ #define ZEND_MM_PAGE_SIZE (4 * 1024) /* 4 KB */ #define ZEND_MM_PAGES (ZEND_MM_CHUNK_SIZE / ZEND_MM_PAGE_SIZE) /* 512 */ #define ZEND_MM_FIRST_PAGE (1) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index d0a180ee6a9c1..07cb9c5fbb7c0 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -482,7 +482,7 @@ static zend_result zend_ast_add_unpacked_element(zval *result, zval *expr) { zend_class_entry *zend_ast_fetch_class(zend_ast *ast, zend_class_entry *scope) { - return zend_fetch_class_with_scope(zend_ast_get_str(ast), ast->attr | ZEND_FETCH_CLASS_EXCEPTION, scope); + return zend_fetch_class_with_scope(zend_ast_get_str(ast), (ast->attr >> ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT) | ZEND_FETCH_CLASS_EXCEPTION, scope); } ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 609fc2e26559f..9ca8c654fc207 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1227,7 +1227,8 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop } if (type_mask & MAY_BE_STATIC) { zend_string *name = ZSTR_KNOWN(ZEND_STR_STATIC); - if (scope) { + // During compilation of eval'd code the called scope refers to the scope calling the eval + if (scope && !zend_is_compiling()) { zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data)); if (called_scope) { name = called_scope->name; @@ -3995,6 +3996,7 @@ static void zend_compile_init_user_func(zend_ast *name_ast, uint32_t num_args, z static zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, zend_string *lcname) /* {{{ */ { znode arg_node; + zend_op *opline; if (args->children != 2) { return FAILURE; @@ -4036,7 +4038,8 @@ static zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, ze zend_compile_expr(&arg_node, args->child[1]); zend_emit_op(NULL, ZEND_SEND_ARRAY, &arg_node, NULL); zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL); - zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL); + opline = zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL); + opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS; return SUCCESS; } @@ -9736,8 +9739,8 @@ static void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ zend_string_release_ex(class_name, 0); if (tmp != class_name) { zval *zv = zend_ast_get_zval(class_ast); - ZVAL_STR(zv, tmp); + class_ast->attr = ZEND_NAME_FQ; } } @@ -9832,7 +9835,7 @@ static void zend_compile_const_expr_new(zend_ast **ast_ptr) zval *class_ast_zv = zend_ast_get_zval(class_ast); zval_ptr_dtor_nogc(class_ast_zv); ZVAL_STR(class_ast_zv, class_name); - class_ast->attr = fetch_type; + class_ast->attr = fetch_type << ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT; } static void zend_compile_const_expr_args(zend_ast **ast_ptr) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index d396f3530a522..ac304ac7f6ae9 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -917,6 +917,9 @@ ZEND_API zend_string *zend_type_to_string(zend_type type); #define ZEND_NAME_NOT_FQ 1 #define ZEND_NAME_RELATIVE 2 +/* ZEND_FETCH_ flags in class name AST of new const expression must not clash with ZEND_NAME_ flags */ +#define ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT 2 + #define ZEND_TYPE_NULLABLE (1<<8) #define ZEND_ARRAY_SYNTAX_LIST 1 /* list() */ diff --git a/Zend/zend_ini_parser.y b/Zend/zend_ini_parser.y index 7eb3753520775..58ad4b360bbf6 100644 --- a/Zend/zend_ini_parser.y +++ b/Zend/zend_ini_parser.y @@ -269,6 +269,9 @@ ZEND_API zend_result zend_parse_ini_string(char *str, bool unbuffered_errors, in static void zval_ini_dtor(zval *zv) { if (Z_TYPE_P(zv) == IS_STRING) { + if (ZEND_SYSTEM_INI) { + GC_MAKE_PERSISTENT_LOCAL(Z_STR_P(zv)); + } zend_string_release(Z_STR_P(zv)); } } @@ -324,6 +327,9 @@ statement: printf("NORMAL: '%s' = '%s'\n", Z_STRVAL($1), Z_STRVAL($3)); #endif ZEND_INI_PARSER_CB(&$1, &$3, NULL, ZEND_INI_PARSER_ENTRY, ZEND_INI_PARSER_ARG); + if (ZEND_SYSTEM_INI) { + GC_MAKE_PERSISTENT_LOCAL(Z_STR($1)); + } zend_string_release(Z_STR($1)); zval_ini_dtor(&$3); } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index ed1eb96d3eaa2..5996b93a78287 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4329,6 +4329,7 @@ ZEND_VM_INLINE_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY, SPEC(OBSERVER)) zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); @@ -8365,8 +8366,8 @@ ZEND_VM_C_LABEL(check_indirect): zend_refcounted *garbage = Z_COUNTED_P(variable_ptr); ZVAL_REF(variable_ptr, ref); + SAVE_OPLINE(); if (GC_DELREF(garbage) == 0) { - SAVE_OPLINE(); rc_dtor_func(garbage); if (UNEXPECTED(EG(exception))) { ZVAL_NULL(variable_ptr); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index fc3d9dbdc9b0c..41d3d8f59c2f0 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4217,6 +4217,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_ zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); @@ -4294,6 +4295,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_OBSER zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); @@ -18796,6 +18798,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HA zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); @@ -21452,6 +21455,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HA zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); @@ -38320,6 +38324,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HAN zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); @@ -43114,8 +43119,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_GLOBAL_SPEC_C zend_refcounted *garbage = Z_COUNTED_P(variable_ptr); ZVAL_REF(variable_ptr, ref); + SAVE_OPLINE(); if (GC_DELREF(garbage) == 0) { - SAVE_OPLINE(); rc_dtor_func(garbage); if (UNEXPECTED(EG(exception))) { ZVAL_NULL(variable_ptr); @@ -56002,6 +56007,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); @@ -56080,6 +56086,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); @@ -57613,6 +57620,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); @@ -57918,6 +57926,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); @@ -59051,6 +59060,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) zend_refcounted *ref = Z_COUNTED_P(retval_ptr); ZVAL_COPY_VALUE(return_value, retval_ptr); if (GC_MAY_LEAK(ref)) { + SAVE_OPLINE(); gc_possible_root(ref); } ZVAL_NULL(retval_ptr); diff --git a/configure.ac b/configure.ac index 485062e32a8f1..ee2d248105f6a 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.1.10-dev],[https://bugs.php.net],[php],[https://www.php.net]) +AC_INIT([PHP],[8.1.11],[https://bugs.php.net],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/ext/dom/node.c b/ext/dom/node.c index 893670807bca4..880c8cfe3e794 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -1003,6 +1003,7 @@ PHP_METHOD(DOMNode, replaceChild) xmlNodePtr newchild, oldchild, nodep; dom_object *intern, *newchildobj, *oldchildobj; int stricterror; + bool replacedoctype = false; int ret; @@ -1059,6 +1060,9 @@ PHP_METHOD(DOMNode, replaceChild) dom_reconcile_ns(nodep->doc, newchild); } } else if (oldchild != newchild) { + xmlDtdPtr intSubset = xmlGetIntSubset(nodep->doc); + replacedoctype = (intSubset == (xmlDtd *) oldchild); + if (newchild->doc == NULL && nodep->doc != NULL) { xmlSetTreeDoc(newchild, nodep->doc); newchildobj->document = intern->document; @@ -1066,6 +1070,10 @@ PHP_METHOD(DOMNode, replaceChild) } xmlReplaceNode(oldchild, newchild); dom_reconcile_ns(nodep->doc, newchild); + + if (replacedoctype) { + nodep->doc->intSubset = (xmlDtd *) newchild; + } } DOM_RET_OBJ(oldchild, &ret, intern); } diff --git a/ext/dom/tests/bug79451.phpt b/ext/dom/tests/bug79451.phpt new file mode 100644 index 0000000000000..f2c238a91903b --- /dev/null +++ b/ext/dom/tests/bug79451.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #79451 (Using DOMDocument->replaceChild on doctype causes double free) +--EXTENSIONS-- +dom +--FILE-- +loadHTML("

hello

"); +$impl = new \DOMImplementation(); +$dt = $impl->createDocumentType("html_replace", "", ""); +$dom->replaceChild($dt, $dom->doctype); + +var_dump($dom->doctype->name); +echo $dom->saveXML(); +?> +--EXPECTF-- +string(12) "html_replace" + + +

hello

diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index c75e2788b869a..aaab3c9fd3ad9 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -584,6 +584,36 @@ ZEND_MODULE_INFO_D(gmp) } /* }}} */ +static zend_result convert_zstr_to_gmp(mpz_t gmp_number, const zend_string *val, zend_long base, uint32_t arg_pos) +{ + const char *num_str = ZSTR_VAL(val); + bool skip_lead = false; + + if (ZSTR_LEN(val) >= 2 && num_str[0] == '0') { + if ((base == 0 || base == 16) && (num_str[1] == 'x' || num_str[1] == 'X')) { + base = 16; + skip_lead = true; + } else if ((base == 0 || base == 8) && (num_str[1] == 'o' || num_str[1] == 'O')) { + base = 8; + skip_lead = true; + } else if ((base == 0 || base == 2) && (num_str[1] == 'b' || num_str[1] == 'B')) { + base = 2; + skip_lead = true; + } + } + + int gmp_ret = mpz_set_str(gmp_number, (skip_lead ? &num_str[2] : num_str), (int) base); + if (-1 == gmp_ret) { + if (arg_pos == 0) { + zend_value_error("Number is not an integer string"); + } else { + zend_argument_value_error(arg_pos, "is not an integer string"); + } + return FAILURE; + } + + return SUCCESS; +} /* {{{ convert_to_gmp * Convert zval to be gmp number */ @@ -594,34 +624,7 @@ static zend_result convert_to_gmp(mpz_t gmpnumber, zval *val, zend_long base, ui mpz_set_si(gmpnumber, Z_LVAL_P(val)); return SUCCESS; case IS_STRING: { - char *numstr = Z_STRVAL_P(val); - bool skip_lead = 0; - int ret; - - if (Z_STRLEN_P(val) >= 2 && numstr[0] == '0') { - if ((base == 0 || base == 16) && (numstr[1] == 'x' || numstr[1] == 'X')) { - base = 16; - skip_lead = 1; - } else if ((base == 0 || base == 8) && (numstr[1] == 'o' || numstr[1] == 'O')) { - base = 8; - skip_lead = 1; - } else if ((base == 0 || base == 2) && (numstr[1] == 'b' || numstr[1] == 'B')) { - base = 2; - skip_lead = 1; - } - } - - ret = mpz_set_str(gmpnumber, (skip_lead ? &numstr[2] : numstr), (int) base); - if (-1 == ret) { - if (arg_pos == 0) { - zend_value_error("Number is not an integer string"); - } else { - zend_argument_value_error(arg_pos, "is not an integer string"); - } - return FAILURE; - } - - return SUCCESS; + return convert_zstr_to_gmp(gmpnumber, Z_STR_P(val), base, arg_pos); } default: { zend_long lval; @@ -869,22 +872,29 @@ static inline void _gmp_unary_opl(INTERNAL_FUNCTION_PARAMETERS, gmp_unary_opl_t /* {{{ Initializes GMP number */ ZEND_FUNCTION(gmp_init) { - zval *number_arg; - mpz_ptr gmpnumber; + mpz_ptr gmp_number; + zend_string *arg_str = NULL; + zend_long arg_l = 0; zend_long base = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &number_arg, &base) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR_OR_LONG(arg_str, arg_l) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(base) + ZEND_PARSE_PARAMETERS_END(); if (base && (base < 2 || base > GMP_MAX_BASE)) { zend_argument_value_error(2, "must be between 2 and %d", GMP_MAX_BASE); RETURN_THROWS(); } - INIT_GMP_RETVAL(gmpnumber); - if (convert_to_gmp(gmpnumber, number_arg, base, 1) == FAILURE) { - RETURN_THROWS(); + INIT_GMP_RETVAL(gmp_number); + if (arg_str) { + if (convert_zstr_to_gmp(gmp_number, arg_str, base, 1) == FAILURE) { + RETURN_THROWS(); + } + } else { + mpz_set_si(gmp_number, arg_l); } } /* }}} */ diff --git a/ext/gmp/tests/gh9308.phpt b/ext/gmp/tests/gh9308.phpt new file mode 100644 index 0000000000000..af13b8bdf328b --- /dev/null +++ b/ext/gmp/tests/gh9308.phpt @@ -0,0 +1,19 @@ +--TEST-- +Bug GH-9308: GMP throws the wrong error when a GMP object is passed to gmp_init() +--EXTENSIONS-- +gmp +--FILE-- +getMessage(), \PHP_EOL; +} + +?> +--EXPECT-- +gmp_init(): Argument #1 ($num) must be of type string|int, GMP given diff --git a/ext/intl/formatter/formatter_format.c b/ext/intl/formatter/formatter_format.c index fd8638c15e133..1c9d5b5bd9103 100644 --- a/ext/intl/formatter/formatter_format.c +++ b/ext/intl/formatter/formatter_format.c @@ -103,9 +103,18 @@ PHP_FUNCTION( numfmt_format ) } INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" ); break; - + case FORMAT_TYPE_CURRENCY: + if (getThis()) { + const char *space; + const char *class_name = get_active_class_name(&space); + zend_argument_value_error(2, "cannot be NumberFormatter::TYPE_CURRENCY constant, " + "use %s%sformatCurrency() method instead", class_name, space); + } else { + zend_argument_value_error(3, "cannot be NumberFormatter::TYPE_CURRENCY constant, use numfmt_format_currency() function instead"); + } + RETURN_THROWS(); default: - zend_argument_value_error(3, "must be a NumberFormatter::TYPE_* constant"); + zend_argument_value_error(getThis() ? 2 : 3, "must be a NumberFormatter::TYPE_* constant"); RETURN_THROWS(); } diff --git a/ext/intl/formatter/formatter_parse.c b/ext/intl/formatter/formatter_parse.c index df2b4d194d31b..8ea066c586c1f 100644 --- a/ext/intl/formatter/formatter_parse.c +++ b/ext/intl/formatter/formatter_parse.c @@ -85,8 +85,18 @@ PHP_FUNCTION( numfmt_parse ) val_double = unum_parseDouble(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo)); RETVAL_DOUBLE(val_double); break; + case FORMAT_TYPE_CURRENCY: + if (getThis()) { + const char *space; + const char *class_name = get_active_class_name(&space); + zend_argument_value_error(2, "cannot be NumberFormatter::TYPE_CURRENCY constant, " + "use %s%sparseCurrency() method instead", class_name, space); + } else { + zend_argument_value_error(3, "cannot be NumberFormatter::TYPE_CURRENCY constant, use numfmt_parse_currency() function instead"); + } + goto cleanup; default: - zend_argument_value_error(3, "must be a NumberFormatter::TYPE_* constant"); + zend_argument_value_error(getThis() ? 2 : 3, "must be a NumberFormatter::TYPE_* constant"); goto cleanup; } diff --git a/ext/intl/tests/formatter_format_and_parse_errors.phpt b/ext/intl/tests/formatter_format_and_parse_errors.phpt new file mode 100644 index 0000000000000..5d46cafa36b81 --- /dev/null +++ b/ext/intl/tests/formatter_format_and_parse_errors.phpt @@ -0,0 +1,65 @@ +--TEST-- +ValueErrors for format/parse methods and procedural functions +--EXTENSIONS-- +intl +--FILE-- +getMessage(), \PHP_EOL; +} +try { + $o->format($num, -20); +} catch (\ValueError $e) { + echo $e->getMessage(), \PHP_EOL; +} +try { + numfmt_parse($o, $str, -20); +} catch (\ValueError $e) { + echo $e->getMessage(), \PHP_EOL; +} +try { + $o->parse($str, -20); +} catch (\ValueError $e) { + echo $e->getMessage(), \PHP_EOL; +} + +/* With NumberFormatter::TYPE_CURRENCY */ +try { + numfmt_format($o, $num, NumberFormatter::TYPE_CURRENCY); +} catch (\ValueError $e) { + echo $e->getMessage(), \PHP_EOL; +} +try { + $o->format($num, NumberFormatter::TYPE_CURRENCY); +} catch (\ValueError $e) { + echo $e->getMessage(), \PHP_EOL; +} +try { + numfmt_parse($o, $str, NumberFormatter::TYPE_CURRENCY); +} catch (\ValueError $e) { + echo $e->getMessage(), \PHP_EOL; +} +try { + $o->parse($str, NumberFormatter::TYPE_CURRENCY); +} catch (\ValueError $e) { + echo $e->getMessage(), \PHP_EOL; +} + +?> +--EXPECT-- +numfmt_format(): Argument #3 ($type) must be a NumberFormatter::TYPE_* constant +NumberFormatter::format(): Argument #2 ($type) must be a NumberFormatter::TYPE_* constant +numfmt_parse(): Argument #3 ($type) must be a NumberFormatter::TYPE_* constant +NumberFormatter::parse(): Argument #2 ($type) must be a NumberFormatter::TYPE_* constant +numfmt_format(): Argument #3 ($type) cannot be NumberFormatter::TYPE_CURRENCY constant, use numfmt_format_currency() function instead +NumberFormatter::format(): Argument #2 ($type) cannot be NumberFormatter::TYPE_CURRENCY constant, use NumberFormatter::formatCurrency() method instead +numfmt_parse(): Argument #3 ($type) cannot be NumberFormatter::TYPE_CURRENCY constant, use numfmt_parse_currency() function instead +NumberFormatter::parse(): Argument #2 ($type) cannot be NumberFormatter::TYPE_CURRENCY constant, use NumberFormatter::parseCurrency() method instead diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 449bf3248313e..b4099bb7b8227 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -774,7 +774,7 @@ PHP_LIBXML_API void php_libxml_initialize(void) PHP_LIBXML_API void php_libxml_shutdown(void) { if (_php_libxml_initialized) { -#ifdef LIBXML_SCHEMAS_ENABLED +#if defined(LIBXML_SCHEMAS_ENABLED) && LIBXML_VERSION < 21000 xmlRelaxNGCleanupTypes(); #endif /* xmlCleanupParser(); */ diff --git a/ext/mbstring/libmbfl/filters/mbfilter_cp932.c b/ext/mbstring/libmbfl/filters/mbfilter_cp932.c index 10559b593be60..62bf1942193c9 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_cp932.c +++ b/ext/mbstring/libmbfl/filters/mbfilter_cp932.c @@ -27,6 +27,36 @@ * */ +/* CP932 is Microsoft's version of Shift-JIS. + * + * What we call "SJIS-win" is a variant of CP932 which maps U+00A5 + * and U+203E the same way as eucJP-win; namely, instead of mapping + * U+00A5 (YEN SIGN) to 0x5C and U+203E (OVERLINE) to 0x7E, + * these codepoints are mapped to appropriate JIS X 0208 characters. + * + * When converting from Shift-JIS to Unicode, there is no difference + * between CP932 and "SJIS-win". + * + * Additional facts: + * + * • In the libmbfl library which formed the base for mbstring, "CP932" and + * "SJIS-win" were originally aliases. The differing mappings were added in + * December 2002. The libmbfl author later stated that this was done so that + * "CP932" would comply with a certain specification, while "SJIS-win" would + * maintain the existing mappings. He does not remember which specification + * it was. + * • The WHATWG specification for "Shift_JIS" (followed by web browsers) + * agrees with our mappings for "CP932". + * • Microsoft Windows' "best-fit" mappings for CP932 (via the + * WideCharToMultiByte API) convert U+00A5 to 0x5C, which also agrees with + * our mappings for "CP932". + * • glibc's iconv converts U+203E to CP932 0x7E, which again agrees with + * our mappings for "CP932". + * • When converting Shift-JIS to CP932, the conversion goes through Unicode. + * Shift-JIS 0x7E converts to U+203E, so mapping U+203E to 0x7E means that + * 0x7E will go to 0x7E when converting Shift-JIS to CP932. + */ + #include "mbfilter.h" #include "mbfilter_cp932.h" @@ -54,7 +84,8 @@ static const unsigned char mblen_table_sjis[] = { /* 0x80-0x9f,0xE0-0xFF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; -static const char *mbfl_encoding_cp932_aliases[] = {"MS932", "Windows-31J", "MS_Kanji", "SJIS-win", "SJIS-ms", "SJIS-open", NULL}; +static const char *mbfl_encoding_cp932_aliases[] = {"MS932", "Windows-31J", "MS_Kanji", NULL}; +static const char *mbfl_encoding_sjiswin_aliases[] = {"SJIS-ms", "SJIS-open", NULL}; const mbfl_encoding mbfl_encoding_cp932 = { mbfl_no_encoding_cp932, @@ -87,6 +118,37 @@ const struct mbfl_convert_vtbl vtbl_wchar_cp932 = { NULL, }; +const mbfl_encoding mbfl_encoding_sjiswin = { + mbfl_no_encoding_sjiswin, + "SJIS-win", + "Shift_JIS", + mbfl_encoding_sjiswin_aliases, + mblen_table_sjis, + MBFL_ENCTYPE_GL_UNSAFE, + &vtbl_sjiswin_wchar, + &vtbl_wchar_sjiswin +}; + +const struct mbfl_convert_vtbl vtbl_sjiswin_wchar = { + mbfl_no_encoding_sjiswin, + mbfl_no_encoding_wchar, + mbfl_filt_conv_common_ctor, + NULL, + mbfl_filt_conv_cp932_wchar, + mbfl_filt_conv_cp932_wchar_flush, + NULL, +}; + +const struct mbfl_convert_vtbl vtbl_wchar_sjiswin = { + mbfl_no_encoding_wchar, + mbfl_no_encoding_sjiswin, + mbfl_filt_conv_common_ctor, + NULL, + mbfl_filt_conv_wchar_sjiswin, + mbfl_filt_conv_common_flush, + NULL, +}; + #define CK(statement) do { if ((statement) < 0) return (-1); } while (0) #define SJIS_ENCODE(c1,c2,s1,s2) \ @@ -132,12 +194,7 @@ const struct mbfl_convert_vtbl vtbl_wchar_cp932 = { } \ } while (0) - -/* - * SJIS-win => wchar - */ -int -mbfl_filt_conv_cp932_wchar(int c, mbfl_convert_filter *filter) +int mbfl_filt_conv_cp932_wchar(int c, mbfl_convert_filter *filter) { int c1, s, s1, s2, w; @@ -224,11 +281,7 @@ static int mbfl_filt_conv_cp932_wchar_flush(mbfl_convert_filter *filter) return 0; } -/* - * wchar => SJIS-win - */ -int -mbfl_filt_conv_wchar_cp932(int c, mbfl_convert_filter *filter) +int mbfl_filt_conv_wchar_cp932(int c, mbfl_convert_filter *filter) { int c1, c2, s1, s2; @@ -236,6 +289,8 @@ mbfl_filt_conv_wchar_cp932(int c, mbfl_convert_filter *filter) s2 = 0; if (c >= ucs_a1_jis_table_min && c < ucs_a1_jis_table_max) { s1 = ucs_a1_jis_table[c - ucs_a1_jis_table_min]; + } else if (c == 0x203E) { + s1 = 0x7E; } else if (c >= ucs_a2_jis_table_min && c < ucs_a2_jis_table_max) { s1 = ucs_a2_jis_table[c - ucs_a2_jis_table_min]; } else if (c >= ucs_i_jis_table_min && c < ucs_i_jis_table_max) { @@ -251,7 +306,7 @@ mbfl_filt_conv_wchar_cp932(int c, mbfl_convert_filter *filter) } if (s1 <= 0) { if (c == 0xa5) { /* YEN SIGN */ - s1 = 0x216F; /* FULLWIDTH YEN SIGN */ + s1 = 0x5C; } else if (c == 0xff3c) { /* FULLWIDTH REVERSE SOLIDUS */ s1 = 0x2140; } else if (c == 0x2225) { /* PARALLEL TO */ @@ -310,3 +365,17 @@ mbfl_filt_conv_wchar_cp932(int c, mbfl_convert_filter *filter) return 0; } + +int mbfl_filt_conv_wchar_sjiswin(int c, mbfl_convert_filter *filter) +{ + if (c == 0xA5) { + CK((*filter->output_function)(0x81, filter->data)); + CK((*filter->output_function)(0x8F, filter->data)); + } else if (c == 0x203E) { + CK((*filter->output_function)(0x81, filter->data)); + CK((*filter->output_function)(0x50, filter->data)); + } else { + return mbfl_filt_conv_wchar_cp932(c, filter); + } + return 0; +} diff --git a/ext/mbstring/libmbfl/filters/mbfilter_cp932.h b/ext/mbstring/libmbfl/filters/mbfilter_cp932.h index 031276ddf1470..8dce3ab9e91d8 100644 --- a/ext/mbstring/libmbfl/filters/mbfilter_cp932.h +++ b/ext/mbstring/libmbfl/filters/mbfilter_cp932.h @@ -36,7 +36,12 @@ extern const mbfl_encoding mbfl_encoding_cp932; extern const struct mbfl_convert_vtbl vtbl_cp932_wchar; extern const struct mbfl_convert_vtbl vtbl_wchar_cp932; +extern const mbfl_encoding mbfl_encoding_sjiswin; +extern const struct mbfl_convert_vtbl vtbl_sjiswin_wchar; +extern const struct mbfl_convert_vtbl vtbl_wchar_sjiswin; + int mbfl_filt_conv_cp932_wchar(int c, mbfl_convert_filter *filter); int mbfl_filt_conv_wchar_cp932(int c, mbfl_convert_filter *filter); +int mbfl_filt_conv_wchar_sjiswin(int c, mbfl_convert_filter *filter); #endif /* MBFL_MBFILTER_CP932_H */ diff --git a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.c b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.c index f298eab6df238..2495f7447aa3a 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.c +++ b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.c @@ -121,6 +121,7 @@ static const mbfl_encoding *mbfl_encoding_ptr_list[] = { &mbfl_encoding_utf8_kddi_b, &mbfl_encoding_utf8_sb, &mbfl_encoding_cp932, + &mbfl_encoding_sjiswin, &mbfl_encoding_cp51932, &mbfl_encoding_jis, &mbfl_encoding_2022jp, diff --git a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h index c99b42dcc6eab..09505d7238245 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h +++ b/ext/mbstring/libmbfl/mbfl/mbfl_encoding.h @@ -74,6 +74,7 @@ enum mbfl_no_encoding { mbfl_no_encoding_sjis_mac, mbfl_no_encoding_sjis2004, mbfl_no_encoding_cp932, + mbfl_no_encoding_sjiswin, mbfl_no_encoding_cp51932, mbfl_no_encoding_jis, mbfl_no_encoding_2022jp, diff --git a/ext/mbstring/tests/cp932_encoding.phpt b/ext/mbstring/tests/cp932_encoding.phpt index ff7bd9cfcdc28..df700f20286a5 100644 --- a/ext/mbstring/tests/cp932_encoding.phpt +++ b/ext/mbstring/tests/cp932_encoding.phpt @@ -1,5 +1,5 @@ --TEST-- -Exhaustive test of CP932 encoding verification and conversion +Exhaustive test of CP932 encoding verification and conversion (including 'SJIS-win' variant) --EXTENSIONS-- mbstring --SKIPIF-- @@ -34,8 +34,10 @@ for ($i = 0xF0; $i <= 0xF9; $i++) { $fromUnicode["\x00\xA2"] = "\x81\x91"; /* U+00A3 is POUND SIGN; convert to FULLWIDTH POUND SIGN */ $fromUnicode["\x00\xA3"] = "\x81\x92"; -/* U+00A5 is YEN SIGN; convert to FULLWIDTH YEN SIGN */ -$fromUnicode["\x00\xA5"] = "\x81\x8F"; +/* U+00A5 is YEN SIGN; convert to 0x5C, which has conflicting uses + * (either as backslash or as Yen sign) */ +$fromUnicode["\x00\xA5"] = "\x5C"; + /* We map the JIS X 0208 FULLWIDTH TILDE to U+FF5E (FULLWIDTH TILDE) * But when converting Unicode to CP932, we also accept U+301C (WAVE DASH) */ @@ -51,12 +53,13 @@ $fromUnicode["\x20\x16"] = "\x81\x61"; * but when converting Unicode to CP932, we also accept U+00AC (NOT SIGN) */ $fromUnicode["\x00\xAC"] = "\x81\xCA"; -/* U+203E is OVERLINE; convert to JIS X 0208 FULLWIDTH MACRON */ -$fromUnicode["\x20\x3E"] = "\x81\x50"; - -/* U+00AF is MACRON; it can also go to FULLWIDTH MACRON */ +/* U+00AF is MACRON; convert to FULLWIDTH MACRON */ $fromUnicode["\x00\xAF"] = "\x81\x50"; +/* U+203E is OVERLINE; convert to 0x7E, which has conflicting uses + * (either as tilde or as overline) */ +$fromUnicode["\x20\x3E"] = "\x7E"; + findInvalidChars($validChars, $invalidChars, $truncated, array_fill_keys(range(0x81, 0x9F), 2) + array_fill_keys(range(0xE0, 0xFC), 2)); findInvalidChars($fromUnicode, $invalidCodepoints, $unused, array_fill_keys(range(0, 0xFF), 2)); @@ -106,12 +109,38 @@ echo "CP932 verification and conversion works on all invalid characters\n"; convertAllInvalidChars($invalidCodepoints, $fromUnicode, 'UTF-16BE', 'CP932', '%'); echo "Unicode -> CP932 conversion works on all invalid codepoints\n"; +/* Now test 'SJIS-win' variant of CP932, which is really CP932 but with + * two different mappings + * Instead of mapping U+00A5 and U+203E to the single bytes 0x5C and 07E + * (which have conflicting uses), 'SJIS-win' maps them to appropriate + * JIS X 0208 characters */ + +/* U+00A5 is YEN SIGN; convert to FULLWIDTH YEN SIGN */ +$fromUnicode["\x00\xA5"] = "\x81\x8F"; +/* U+203E is OVERLINE; convert to JIS X 0208 FULLWIDTH MACRON */ +$fromUnicode["\x20\x3E"] = "\x81\x50"; + +testAllValidChars($validChars, 'SJIS-win', 'UTF-16BE'); +foreach ($nonInvertible as $cp932 => $unicode) + testValidString($cp932, $unicode, 'SJIS-win', 'UTF-16BE', false); +echo "SJIS-win verification and conversion works on all valid characters\n"; + +testAllInvalidChars($invalidChars, $validChars, 'SJIS-win', 'UTF-16BE', "\x00%"); +echo "SJIS-win verification and conversion works on all invalid characters\n"; + +convertAllInvalidChars($invalidCodepoints, $fromUnicode, 'UTF-16BE', 'SJIS-win', '%'); +echo "Unicode -> SJIS-win conversion works on all invalid codepoints\n"; + // Test "long" illegal character markers mb_substitute_character("long"); convertInvalidString("\x80", "%", "CP932", "UTF-8"); convertInvalidString("\xEA", "%", "CP932", "UTF-8"); convertInvalidString("\x81\x20", "%", "CP932", "UTF-8"); convertInvalidString("\xEA\xA9", "%", "CP932", "UTF-8"); +convertInvalidString("\x80", "%", "SJIS-win", "UTF-8"); +convertInvalidString("\xEA", "%", "SJIS-win", "UTF-8"); +convertInvalidString("\x81\x20", "%", "SJIS-win", "UTF-8"); +convertInvalidString("\xEA\xA9", "%", "SJIS-win", "UTF-8"); echo "Done!\n"; ?> @@ -119,4 +148,7 @@ echo "Done!\n"; CP932 verification and conversion works on all valid characters CP932 verification and conversion works on all invalid characters Unicode -> CP932 conversion works on all invalid codepoints +SJIS-win verification and conversion works on all valid characters +SJIS-win verification and conversion works on all invalid characters +Unicode -> SJIS-win conversion works on all invalid codepoints Done! diff --git a/ext/mbstring/tests/mb_internal_encoding_variation2.phpt b/ext/mbstring/tests/mb_internal_encoding_variation2.phpt index 842d8d519cf06..dab306b12a2b8 100644 --- a/ext/mbstring/tests/mb_internal_encoding_variation2.phpt +++ b/ext/mbstring/tests/mb_internal_encoding_variation2.phpt @@ -176,10 +176,10 @@ string(9) "eucJP-win" -- Iteration 20 -- string(9) "eucJP-win" bool(true) -string(5) "CP932" +string(8) "SJIS-win" -- Iteration 21 -- -string(5) "CP932" +string(8) "SJIS-win" bool(true) string(11) "ISO-2022-JP" diff --git a/ext/oci8/php_oci8_int.h b/ext/oci8/php_oci8_int.h index 08bec33193ae0..171edfa53d24c 100644 --- a/ext/oci8/php_oci8_int.h +++ b/ext/oci8/php_oci8_int.h @@ -103,7 +103,7 @@ extern zend_class_entry *oci_coll_class_entry_ptr; * PHP_OCI_CRED_EXT must be distinct from the OCI_xxx privilege * values. */ -#define PHP_OCI_CRED_EXT (1<<31) +#define PHP_OCI_CRED_EXT (1u<<31) #if ((PHP_OCI_CRED_EXT == OCI_DEFAULT) || (PHP_OCI_CRED_EXT & (OCI_SYSOPER | OCI_SYSDBA))) #error Invalid value for PHP_OCI_CRED_EXT #endif diff --git a/ext/oci8/tests/CONFLICTS b/ext/oci8/tests/CONFLICTS new file mode 100644 index 0000000000000..176b41ab8bb6f --- /dev/null +++ b/ext/oci8/tests/CONFLICTS @@ -0,0 +1 @@ +oci8 diff --git a/ext/oci8/tests/extauth_01.phpt b/ext/oci8/tests/extauth_01.phpt index e576c35eb03b8..3d5ebe9775ec4 100644 --- a/ext/oci8/tests/extauth_01.phpt +++ b/ext/oci8/tests/extauth_01.phpt @@ -5,8 +5,7 @@ oci8 --SKIPIF-- --INI-- oci8.privileged_connect=1 diff --git a/ext/oci8/tests/extauth_02.phpt b/ext/oci8/tests/extauth_02.phpt index bbcb1fca33325..0ba21e4918200 100644 --- a/ext/oci8/tests/extauth_02.phpt +++ b/ext/oci8/tests/extauth_02.phpt @@ -4,8 +4,8 @@ Test External Authentication errors with oci_new_connect oci8 --SKIPIF-- --INI-- oci8.privileged_connect=1 diff --git a/ext/oci8/tests/extauth_03.phpt b/ext/oci8/tests/extauth_03.phpt index 1c37ef1a1fc10..5fb6d4306812f 100644 --- a/ext/oci8/tests/extauth_03.phpt +++ b/ext/oci8/tests/extauth_03.phpt @@ -4,8 +4,8 @@ Test External Authentication errors with oci_pconnect oci8 --SKIPIF-- --INI-- oci8.privileged_connect=1 diff --git a/ext/oci8/tests/extauth_04.phpt b/ext/oci8/tests/extauth_04.phpt index 167358b6345f7..dfd97dbf4e891 100644 --- a/ext/oci8/tests/extauth_04.phpt +++ b/ext/oci8/tests/extauth_04.phpt @@ -6,11 +6,11 @@ oci8 +--INI-- +oci8.privileged_connect=1 --FILE-- --INI-- oci8.privileged_connect=1 --FILE-- diff --git a/ext/opcache/jit/libudis86/udint.h b/ext/opcache/jit/libudis86/udint.h index 734f0eaa82db1..0bf493c182bb6 100644 --- a/ext/opcache/jit/libudis86/udint.h +++ b/ext/opcache/jit/libudis86/udint.h @@ -79,7 +79,7 @@ #if defined(_MSC_VER) || defined(__BORLANDC__) # define FMT64 "I64" #else -# if defined(__APPLE__) +# if defined(__APPLE__) || defined(__OpenBSD__) # define FMT64 "ll" # elif defined(__amd64__) || defined(__x86_64__) # define FMT64 "l" diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index 6ad4797635e87..d2a8fead2672e 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -2997,6 +2997,9 @@ static int zend_jit_trace_end(dasm_State **Dst, zend_jit_trace_info *t) |.cold_code for (i = 0; i < t->exit_count; i++) { exit_addr = zend_jit_trace_get_exit_addr(i); + if (!exit_addr) { + return 0; + } | b &exit_addr } |=>1: // end of the code @@ -3809,6 +3812,9 @@ static int zend_jit_inc_dec(dasm_State **Dst, const zend_op *opline, uint32_t op exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } | bvs &exit_addr if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) && @@ -4111,6 +4117,9 @@ static int zend_jit_math_long_long(dasm_State **Dst, if (res_info & MAY_BE_GUARD) { int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } if ((res_info & MAY_BE_ANY) == MAY_BE_LONG) { if (use_ovf_flag) { | bvs &exit_addr @@ -6319,6 +6328,9 @@ static int zend_jit_assign_dim_op(dasm_State **Dst, const zend_op *opline, uint3 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) { int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!not_found_exit_addr) { + return 0; + } } if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_RW, op1_info, op2_info, dim_type, NULL, not_found_exit_addr, NULL)) { @@ -8233,7 +8245,10 @@ static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_ } } - if ((op1_info & MAY_BE_ANY) == MAY_BE_DOUBLE) { + if ((op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) == MAY_BE_DOUBLE) { + if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + |.cold_code + } |2: | fmov FPR0, xzr // TODO: "movi d0, #0" is not recognized by DynASM/arm64 | DOUBLE_CMP ZREG_FPR0, op1_addr, ZREG_TMP1, ZREG_FPTMP @@ -8278,6 +8293,10 @@ static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_ |1: | SET_ZVAL_TYPE_INFO_FROM_REG res_addr, REG0w, TMP1 } + if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + | b >9 + |.code + } } else { if (exit_addr) { if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { @@ -8289,6 +8308,9 @@ static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_ | beq &exit_addr |1: } + if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + | b >9 + } } else { ZEND_ASSERT(true_label != (uint32_t)-1 || false_label != (uint32_t)-1); if (false_label != (uint32_t)-1 ) { @@ -8297,12 +8319,20 @@ static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_ |1: if (true_label != (uint32_t)-1) { | b =>true_label + } else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + | b >9 } } else { | bvs => true_label | bne => true_label + if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + | b >9 + } } } + if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + |.code + } } } else if (op1_info & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) { if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { @@ -12480,6 +12510,9 @@ static int zend_jit_fetch_obj(dasm_State **Dst, && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT) { exit_point = zend_jit_trace_get_exit_point(opline, 0); exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } } else { val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_REG0, 0); | LOAD_ZVAL_ADDR REG0, prop_addr @@ -12870,6 +12903,9 @@ static int zend_jit_incdec_obj(dasm_State **Dst, if (use_prop_guard) { int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } | IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr, ZREG_TMP1 var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)); @@ -13009,6 +13045,9 @@ static int zend_jit_incdec_obj(dasm_State **Dst, SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0); exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info); ssa->var_info[ssa_op->result_def].type = res_info & ~MAY_BE_GUARD; | b &exit_addr @@ -13302,6 +13341,9 @@ static int zend_jit_assign_obj_op(dasm_State **Dst, if (use_prop_guard) { int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } | IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr, ZREG_TMP1 var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)); @@ -14077,10 +14119,16 @@ static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_o if (next_opline != opline + 1) { exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); fallback_label = zend_jit_trace_get_exit_addr(exit_point); + if (!fallback_label) { + return 0; + } } if (next_opline != default_opline) { exit_point = zend_jit_trace_get_exit_point(default_opline, 0); default_label = zend_jit_trace_get_exit_addr(exit_point); + if (!default_label) { + return 0; + } } } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 9df3ac8dab47d..ead3ffa6fd668 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -1085,6 +1085,9 @@ static int is_checked_guard(const zend_ssa *tssa, const zend_op **ssa_opcodes, u && (tssa->var_info[tssa->ops[idx].op1_use].type & MAY_BE_STRING)) { return 0; } + if (!(tssa->var_info[tssa->ops[idx].op1_use].type & (MAY_BE_LONG|MAY_BE_DOUBLE))) { + return 0; + } return 1; } else if (opline->opcode == ZEND_ASSIGN_OP && (opline->extended_value == ZEND_ADD @@ -1113,11 +1116,7 @@ static int is_checked_guard(const zend_ssa *tssa, const zend_op **ssa_opcodes, u const zend_op *opline = ssa_opcodes[idx]; if (opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB - || opline->opcode == ZEND_MUL - || opline->opcode == ZEND_PRE_DEC - || opline->opcode == ZEND_PRE_INC - || opline->opcode == ZEND_POST_DEC - || opline->opcode == ZEND_POST_INC) { + || opline->opcode == ZEND_MUL) { if ((opline->op1_type & (IS_VAR|IS_CV)) && tssa->ops[idx].op1_use >= 0 && (tssa->var_info[tssa->ops[idx].op1_use].type & MAY_BE_REF)) { @@ -1128,6 +1127,34 @@ static int is_checked_guard(const zend_ssa *tssa, const zend_op **ssa_opcodes, u && (tssa->var_info[tssa->ops[idx].op2_use].type & MAY_BE_REF)) { return 0; } + if (opline->op1_type == IS_CONST) { + zval *zv = RT_CONSTANT(opline, opline->op1); + if (Z_TYPE_P(zv) != IS_LONG && Z_TYPE_P(zv) != IS_DOUBLE) { + return 0; + } + } else if (!(tssa->var_info[tssa->ops[idx].op1_use].type & (MAY_BE_LONG|MAY_BE_DOUBLE))) { + return 0; + } + if (opline->op2_type == IS_CONST) { + zval *zv = RT_CONSTANT(opline, opline->op2); + if (Z_TYPE_P(zv) != IS_LONG && Z_TYPE_P(zv) != IS_DOUBLE) { + return 0; + } + } else if (!(tssa->var_info[tssa->ops[idx].op2_use].type & (MAY_BE_LONG|MAY_BE_DOUBLE))) { + return 0; + } + } else if (opline->opcode == ZEND_PRE_DEC + || opline->opcode == ZEND_PRE_INC + || opline->opcode == ZEND_POST_DEC + || opline->opcode == ZEND_POST_INC) { + if ((opline->op1_type & (IS_VAR|IS_CV)) + && tssa->ops[idx].op1_use >= 0 + && (tssa->var_info[tssa->ops[idx].op1_use].type & MAY_BE_REF)) { + return 0; + } + if (!(tssa->var_info[tssa->ops[idx].op1_use].type & (MAY_BE_LONG|MAY_BE_DOUBLE))) { + return 0; + } return 1; } } diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index f3852ab9c0fe7..ba6e52abbccb0 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -4166,6 +4166,9 @@ static int zend_jit_inc_dec(dasm_State **Dst, const zend_op *opline, uint32_t op exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } | jo &exit_addr if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) && @@ -4465,6 +4468,9 @@ static int zend_jit_math_long_long(dasm_State **Dst, if (res_info & MAY_BE_GUARD) { int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } if ((res_info & MAY_BE_ANY) == MAY_BE_LONG) { | jo &exit_addr if (Z_MODE(res_addr) == IS_REG && result_reg != Z_REG(res_addr)) { @@ -6852,6 +6858,9 @@ static int zend_jit_assign_dim_op(dasm_State **Dst, const zend_op *opline, uint3 && !(op2_info & ((MAY_BE_ANY|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_STRING)))) { int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); not_found_exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!not_found_exit_addr) { + return 0; + } } if (!zend_jit_fetch_dimension_address_inner(Dst, opline, BP_VAR_RW, op1_info, op2_info, dim_type, NULL, not_found_exit_addr, NULL)) { @@ -8820,7 +8829,10 @@ static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_ } } - if ((op1_info & MAY_BE_ANY) == MAY_BE_DOUBLE) { + if ((op1_info & (MAY_BE_ANY-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) == MAY_BE_DOUBLE) { + if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + |.cold_code + } |2: if (CAN_USE_AVX()) { | vxorps xmm0, xmm0, xmm0 @@ -8869,6 +8881,10 @@ static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_ |1: | SET_ZVAL_TYPE_INFO res_addr, eax } + if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + | jmp >9 + |.code + } } else { if (exit_addr) { if (branch_opcode == ZEND_JMPNZ || branch_opcode == ZEND_JMPNZ_EX) { @@ -8880,6 +8896,9 @@ static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_ | je &exit_addr |1: } + if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + | jmp >9 + } } else { ZEND_ASSERT(true_label != (uint32_t)-1 || false_label != (uint32_t)-1); if (false_label != (uint32_t)-1 ) { @@ -8888,12 +8907,20 @@ static int zend_jit_bool_jmpznz(dasm_State **Dst, const zend_op *opline, uint32_ |1: if (true_label != (uint32_t)-1) { | jmp =>true_label + } else if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + | jmp >9 } } else { | jp => true_label | jne => true_label + if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + | jmp >9 + } } } + if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { + |.code + } } } else if (op1_info & (MAY_BE_ANY - (MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG))) { if (op1_info & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG)) { @@ -13217,6 +13244,9 @@ static int zend_jit_fetch_obj(dasm_State **Dst, && (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_OBJECT) { exit_point = zend_jit_trace_get_exit_point(opline, 0); exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } } else { val_addr = ZEND_ADDR_MEM_ZVAL(ZREG_R0, 0); | LOAD_ZVAL_ADDR r0, prop_addr @@ -13608,6 +13638,9 @@ static int zend_jit_incdec_obj(dasm_State **Dst, if (use_prop_guard) { int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } | IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)); @@ -13766,6 +13799,9 @@ static int zend_jit_incdec_obj(dasm_State **Dst, SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_DOUBLE, 0); exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info); ssa->var_info[ssa_op->result_def].type = res_info & ~MAY_BE_GUARD; | jmp &exit_addr @@ -14089,6 +14125,9 @@ static int zend_jit_assign_obj_op(dasm_State **Dst, if (use_prop_guard) { int32_t exit_point = zend_jit_trace_get_exit_point(opline, 0); const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } | IF_NOT_ZVAL_TYPE var_addr, prop_type, &exit_addr var_info = (1 << prop_type) | (var_info & ~(MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)); @@ -14789,6 +14828,10 @@ static int zend_jit_fetch_this(dasm_State **Dst, const zend_op *opline, const ze int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + if (!exit_addr) { + return 0; + } + | cmp byte EX->This.u1.v.type, IS_OBJECT | jne &exit_addr @@ -14962,10 +15005,16 @@ static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_o if (next_opline != opline + 1) { exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); fallback_label = zend_jit_trace_get_exit_addr(exit_point); + if (!fallback_label) { + return 0; + } } if (next_opline != default_opline) { exit_point = zend_jit_trace_get_exit_point(default_opline, 0); default_label = zend_jit_trace_get_exit_addr(exit_point); + if (!default_label) { + return 0; + } } } diff --git a/ext/opcache/tests/jit/add_014.phpt b/ext/opcache/tests/jit/add_014.phpt new file mode 100644 index 0000000000000..b7691c770334c --- /dev/null +++ b/ext/opcache/tests/jit/add_014.phpt @@ -0,0 +1,25 @@ +--TEST-- +JIT ADD: 014 incorrect guard elimination +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--FILE-- +> 4 - $j++; + if ($j > 14) break; + } +} +try { + @test(); +} catch (Throwable $e) { + echo $e->getMessage() . "\n"; +} +?> +--EXPECT-- +Bit shift by negative number diff --git a/ext/opcache/tests/jit/fetch_dim_func_arg_002.phpt b/ext/opcache/tests/jit/fetch_dim_func_arg_002.phpt new file mode 100644 index 0000000000000..3699eba2045f0 --- /dev/null +++ b/ext/opcache/tests/jit/fetch_dim_func_arg_002.phpt @@ -0,0 +1,18 @@ +--TEST-- +JIT FETCH_DIM_FUNC_ARG: 002 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--EXTENSIONS-- +opcache +--FILE-- + +DONE +--EXPECTF-- +Warning: Trying to access array offset on value of type bool in %sfetch_dim_func_arg_002.php on line 2 +DONE diff --git a/ext/opcache/tests/opt/assign_obj_001.phpt b/ext/opcache/tests/opt/assign_obj_001.phpt new file mode 100644 index 0000000000000..205f111233e86 --- /dev/null +++ b/ext/opcache/tests/opt/assign_obj_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +ASSIGN_OP 001: Incorrect optimization of ASSIGN_OBJ may lead to memory leak +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- +y = ''; +} +test(); +?> +--EXPECTF-- +Warning: Undefined variable $y in %sassign_obj_001.php on line 3 + +Warning: Undefined variable $a in %sassign_obj_001.php on line 3 + +Fatal error: Uncaught Error: Attempt to assign property "y" on string in %sassign_obj_001.php:4 +Stack trace: +#0 %sassign_obj_001.php(6): test() +#1 {main} + thrown in %sassign_obj_001.php on line 4 diff --git a/ext/opcache/tests/opt/assign_op_001.phpt b/ext/opcache/tests/opt/assign_op_001.phpt index b9db4202b46c0..7ac23d71c0d17 100644 --- a/ext/opcache/tests/opt/assign_op_001.phpt +++ b/ext/opcache/tests/opt/assign_op_001.phpt @@ -1,5 +1,5 @@ --TEST-- -ASSIGN_OP 001: Incrrect optimization of ASSIGN_OP may lead to memory leak +ASSIGN_OP 001: Incorrect optimization of ASSIGN_OP may lead to memory leak --INI-- opcache.enable=1 opcache.enable_cli=1 diff --git a/ext/opcache/tests/opt/inference_015.phpt b/ext/opcache/tests/opt/inference_015.phpt new file mode 100644 index 0000000000000..e64c02225b32f --- /dev/null +++ b/ext/opcache/tests/opt/inference_015.phpt @@ -0,0 +1,19 @@ +--TEST-- +Type inference 015: ASSIGN_DIM_OP +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- + +DONE +--EXPECT-- +DONE diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index b3e62b5a75ef4..3f6f5c0ce6238 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -518,15 +518,15 @@ static bool php_openssl_check_path_ex( error_msg = "must not contain any null bytes"; error_type = E_ERROR; } else if (expand_filepath(fs_file_path, real_path) == NULL) { - error_msg = "The argument must be a valid file path"; + error_msg = "must be a valid file path"; } if (error_msg != NULL) { if (arg_num == 0) { const char *option_title = option_name ? option_name : "unknown"; const char *option_label = is_from_array ? "array item" : "option"; - php_error_docref(NULL, E_WARNING, "Path '%s' for %s %s %s", - real_path, option_title, option_label, error_msg); + php_error_docref(NULL, E_WARNING, "Path for %s %s %s", + option_title, option_label, error_msg); } else if (is_from_array && option_name != NULL) { php_openssl_check_path_error( arg_num, error_type, "option %s array item %s", option_name, error_msg); diff --git a/ext/openssl/tests/gh9339.phpt b/ext/openssl/tests/gh9339.phpt new file mode 100644 index 0000000000000..9e4adbccce7f6 --- /dev/null +++ b/ext/openssl/tests/gh9339.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-9339: oid_file path check warning contains uninitialized path +--SKIPIF-- + +--FILE-- + $configFile ]); +?> +--CLEAN-- + +--EXPECTF-- + +Warning: openssl_pkey_new(): Path for oid_file option must be a valid file path in %s on line %d diff --git a/ext/pcre/pcre2lib/sljit/sljitExecAllocator.c b/ext/pcre/pcre2lib/sljit/sljitExecAllocator.c index 6e5bf78e45fe9..b268624822219 100644 --- a/ext/pcre/pcre2lib/sljit/sljitExecAllocator.c +++ b/ext/pcre/pcre2lib/sljit/sljitExecAllocator.c @@ -187,10 +187,13 @@ static SLJIT_INLINE void* alloc_chunk(sljit_uw size) if (retval == MAP_FAILED) return NULL; +#ifdef __FreeBSD__ + /* HardenedBSD's mmap lies, so check permissions again */ if (mprotect(retval, size, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) { munmap(retval, size); return NULL; } +#endif /* FreeBSD */ SLJIT_UPDATE_WX_FLAGS(retval, (uint8_t *)retval + size, 0); diff --git a/ext/pdo/tests/bug_73234.phpt b/ext/pdo/tests/bug_73234.phpt index 01dcf4016181e..7e5729a4e6da6 100644 --- a/ext/pdo/tests/bug_73234.phpt +++ b/ext/pdo/tests/bug_73234.phpt @@ -8,6 +8,11 @@ $dir = getenv('REDIR_TEST_DIR'); if (false == $dir) die('skip no driver'); require_once $dir . 'pdo_test.inc'; PDOTest::skip(); + +$db = PDOTest::factory(); +if ($db->getAttribute(PDO::ATTR_DRIVER_NAME) == 'oci') { + die("xfail PDO::PARAM_NULL is not honored by OCI driver, related with bug #81586"); +} ?> --FILE-- getAttribute(PDO::ATTR_DRIVER_NAME) == 'oci') { + die("xfail OCI driver errorInfo is inconsistent with other PDO drivers"); +} ?> --FILE-- errorCode()); +$query = 'SELECT 1'; +if ($conn->getAttribute(PDO::ATTR_DRIVER_NAME) === 'oci') { + $query .= ' FROM DUAL'; +} var_dump($conn->errorCode()); -$stmt = $conn->prepare($query); var_dump($conn->errorCode()); +$stmt = $conn->prepare($query); +var_dump($conn->errorCode()); var_dump($stmt->errorCode()); + $stmt->execute(); var_dump($stmt->errorCode()); +var_dump($stmt->errorCode()); ?> --EXPECT-- NULL string(5) "00000" +string(5) "00000" +string(5) "00000" NULL string(5) "00000" +string(5) "00000" diff --git a/ext/pdo_dblib/tests/CONFLICTS b/ext/pdo_dblib/tests/CONFLICTS new file mode 100644 index 0000000000000..7980e46ba3fa6 --- /dev/null +++ b/ext/pdo_dblib/tests/CONFLICTS @@ -0,0 +1 @@ +pdo_dblib diff --git a/ext/pdo_oci/tests/CONFLICTS b/ext/pdo_oci/tests/CONFLICTS new file mode 100644 index 0000000000000..176b41ab8bb6f --- /dev/null +++ b/ext/pdo_oci/tests/CONFLICTS @@ -0,0 +1 @@ +oci8 diff --git a/ext/pdo_oci/tests/oci_success_with_info.phpt b/ext/pdo_oci/tests/oci_success_with_info.phpt index 144bb816d425c..8c43c46e19681 100644 --- a/ext/pdo_oci/tests/oci_success_with_info.phpt +++ b/ext/pdo_oci/tests/oci_success_with_info.phpt @@ -3,6 +3,10 @@ Handling OCI_SUCCESS_WITH_INFO --EXTENSIONS-- pdo pdo_oci +--SKIPIF-- + --FILE-- exec('CREATE USER BUG77120_USER IDENTIFIED BY "' . $password . '" PROFILE $conn->exec('GRANT CREATE SESSION TO BUG77120_USER'); // let the password expire -sleep(2); +sleep(3); // 2 seconds is causing random test failures $conn = connectAsUser('BUG77120_USER', $password); var_dump($conn->errorInfo()); diff --git a/ext/pdo_oci/tests/pdo_oci_quote1.phpt b/ext/pdo_oci/tests/pdo_oci_quote1.phpt index 024625a181fc4..ea2a5468dbd5b 100644 --- a/ext/pdo_oci/tests/pdo_oci_quote1.phpt +++ b/ext/pdo_oci/tests/pdo_oci_quote1.phpt @@ -21,7 +21,7 @@ $stmt = $db->prepare('select * from poq_tab'); // The intent is that the fetched data be identical to the unquoted string. // Remember!: use bind variables instead of PDO->quote() -$a = array(null, "", "a", "ab", "abc", "ab'cd", "a\b\n", "'", "''", "a'", "'z", "a''b", '"'); +$a = array("", "a", "ab", "abc", "ab'cd", "a\b\n", "'", "''", "a'", "'z", "a''b", '"'); foreach ($a as $u) { $q = $db->quote($u); echo "Unquoted : "; @@ -42,15 +42,6 @@ echo "Done\n"; ?> --EXPECT-- -Unquoted : NULL -Quoted : string(2) "''" -array(1) { - [0]=> - array(1) { - ["t"]=> - NULL - } -} Unquoted : string(0) "" Quoted : string(2) "''" array(1) { diff --git a/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt b/ext/pdo_oci/tests/pdo_oci_stream_2.phpt similarity index 59% rename from ext/pdo_oci/tests/pdo_oci_stream_2a.phpt rename to ext/pdo_oci/tests/pdo_oci_stream_2.phpt index 0bb1e3996efef..93b71039187e9 100644 --- a/ext/pdo_oci/tests/pdo_oci_stream_2a.phpt +++ b/ext/pdo_oci/tests/pdo_oci_stream_2.phpt @@ -1,5 +1,5 @@ --TEST-- -PDO OCI: Inserts 1K with 1 number and 2 LOB columns (stress test) +PDO OCI: Insert and fetch 1K records from a table that contains 1 number and 2 LOB columns (stress test) --EXTENSIONS-- pdo pdo_oci @@ -71,6 +71,57 @@ printf("Done\n"); /* Cleanup is done in pdo_oci_stream_2b.phpt */ //$db->exec("drop table pdo_oci_stream_2"); +$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams + +// Since each column only has one lob descriptor, the last row is +// shown twice because the lob descriptor for each column is reused in +// the stream + +$i = 0; +$j = 9; +$a_val = ord('a'); +foreach($db->query("select data1 as d4_1, data2 as d4_2 from pdo_oci_stream_2 order by id") as $row) { + $a = $row['d4_1']; + $a1 = $row['d4_2']; + + $str1 = stream_get_contents($a); + $str2 = stream_get_contents($a1); + + $str1len = strlen($str1); + $str2len = strlen($str2); + + $b = ord($str1[0]); + $b1 = ord($str2[0]); + + if (($b != ($a_val + $i)) && ($str1len != (4086 + $i)) && + ($b1 != ($a_val + $j)) && ($str2len != (4086 + $j))) { + printf("There is a bug!\n"); + printf("Col1:\n"); + printf("a_val = %d\n", $a_val); + printf("b = %d\n", $b); + printf("i = %d\n", $i); + printf("str1len = %d\n", $str1len); + + printf("Col2:\n"); + printf("a_val = %d\n", $a_val); + printf("b1 = %d\n", $b1); + printf("j = %d\n", $j); + printf("str2len = %d\n", $str1len); + + } + $i++; + if ($i>9) + $i = 0; + $j--; + if ($j<0) + $j = 9; +} +echo "Fetch operation done!\n"; + +/* Cleanup */ +$db->exec("drop table pdo_oci_stream_2"); + ?> --EXPECT-- Inserting 1000 Records ... Done +Fetch operation done! diff --git a/ext/pdo_oci/tests/pdo_oci_stream_2b.phpt b/ext/pdo_oci/tests/pdo_oci_stream_2b.phpt deleted file mode 100644 index 614590a4c1790..0000000000000 --- a/ext/pdo_oci/tests/pdo_oci_stream_2b.phpt +++ /dev/null @@ -1,72 +0,0 @@ ---TEST-- -PDO OCI: Fetches 1K records from a table that contains 1 number and 2 LOB columns (stress test) ---EXTENSIONS-- -pdo -pdo_oci ---SKIPIF-- - ---FILE-- -setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams - -// Since each column only has one lob descriptor, the last row is -// shown twice because the lob descriptor for each column is reused in -// the stream - -$i = 0; -$j = 9; -$a_val = ord('a'); -foreach($db->query("select data1 as d4_1, data2 as d4_2 from pdo_oci_stream_2 order by id") as $row) { - $a = $row['d4_1']; - $a1 = $row['d4_2']; - - $str1 = stream_get_contents($a); - $str2 = stream_get_contents($a1); - - $str1len = strlen($str1); - $str2len = strlen($str2); - - $b = ord($str1[0]); - $b1 = ord($str2[0]); - - if (($b != ($a_val + $i)) && ($str1len != (4086 + $i)) && - ($b1 != ($a_val + $j)) && ($str2len != (4086 + $j))) { - printf("There is a bug!\n"); - printf("Col1:\n"); - printf("a_val = %d\n", $a_val); - printf("b = %d\n", $b); - printf("i = %d\n", $i); - printf("str1len = %d\n", $str1len); - - printf("Col2:\n"); - printf("a_val = %d\n", $a_val); - printf("b1 = %d\n", $b1); - printf("j = %d\n", $j); - printf("str2len = %d\n", $str1len); - - } - $i++; - if ($i>9) - $i = 0; - $j--; - if ($j<0) - $j = 9; -} -echo "Fetch operation done!\n"; - -/* Cleanup */ -$db->exec("drop table pdo_oci_stream_2"); - -?> ---EXPECT-- -Fetch operation done! diff --git a/ext/pdo_oci/tests/pecl_bug_6364.phpt b/ext/pdo_oci/tests/pecl_bug_6364.phpt index 5decdcb80001e..9a97496fd0fb2 100644 --- a/ext/pdo_oci/tests/pecl_bug_6364.phpt +++ b/ext/pdo_oci/tests/pecl_bug_6364.phpt @@ -5,6 +5,7 @@ pdo pdo_oci --SKIPIF-- diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 54bf7ede6bfaa..f84bfba9f8453 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -35,6 +35,8 @@ #include "zend_exceptions.h" #include "pgsql_driver_arginfo.h" +static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh); + static char * _pdo_pgsql_trim_message(const char *message, int persistent) { size_t i = strlen(message)-1; @@ -140,10 +142,12 @@ static ssize_t pgsql_lob_read(php_stream *stream, char *buf, size_t count) static int pgsql_lob_close(php_stream *stream, int close_handle) { struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stream->abstract; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)(Z_PDO_DBH_P(&self->dbh))->driver_data; if (close_handle) { lo_close(self->conn, self->lfd); } + zend_hash_index_del(H->lob_streams, php_stream_get_resource_id(stream)); zval_ptr_dtor(&self->dbh); efree(self); return 0; @@ -194,6 +198,7 @@ php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid) if (stm) { Z_ADDREF_P(dbh); + zend_hash_index_add_ptr(H->lob_streams, php_stream_get_resource_id(stm), stm->res); return stm; } @@ -202,10 +207,29 @@ php_stream *pdo_pgsql_create_lob_stream(zval *dbh, int lfd, Oid oid) } /* }}} */ +void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh) +{ + zend_resource *res; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + if (H->lob_streams) { + ZEND_HASH_REVERSE_FOREACH_PTR(H->lob_streams, res) { + if (res->type >= 0) { + zend_list_close(res); + } + } ZEND_HASH_FOREACH_END(); + } +} + static void pgsql_handle_closer(pdo_dbh_t *dbh) /* {{{ */ { pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; if (H) { + if (H->lob_streams) { + pdo_pgsql_close_lob_streams(dbh); + zend_hash_destroy(H->lob_streams); + pefree(H->lob_streams, dbh->is_persistent); + H->lob_streams = NULL; + } if (H->server) { PQfinish(H->server); H->server = NULL; @@ -295,6 +319,8 @@ static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) zend_long ret = 1; ExecStatusType qs; + bool in_trans = pgsql_handle_in_transaction(dbh); + if (!(res = PQexec(H->server, ZSTR_VAL(sql)))) { /* fatal error */ pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); @@ -313,6 +339,9 @@ static zend_long pgsql_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) ret = Z_L(0); } PQclear(res); + if (in_trans && !pgsql_handle_in_transaction(dbh)) { + pdo_pgsql_close_lob_streams(dbh); + } return ret; } @@ -503,9 +532,7 @@ static zend_result pdo_pgsql_check_liveness(pdo_dbh_t *dbh) static bool pgsql_handle_in_transaction(pdo_dbh_t *dbh) { - pdo_pgsql_db_handle *H; - - H = (pdo_pgsql_db_handle *)dbh->driver_data; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; return PQtransactionStatus(H->server) > PQTRANS_IDLE; } @@ -538,7 +565,9 @@ static bool pgsql_handle_commit(pdo_dbh_t *dbh) /* When deferred constraints are used the commit could fail, and a ROLLBACK implicitly ran. See bug #67462 */ - if (!ret) { + if (ret) { + pdo_pgsql_close_lob_streams(dbh); + } else { dbh->in_txn = pgsql_handle_in_transaction(dbh); } @@ -547,7 +576,13 @@ static bool pgsql_handle_commit(pdo_dbh_t *dbh) static bool pgsql_handle_rollback(pdo_dbh_t *dbh) { - return pdo_pgsql_transaction_cmd("ROLLBACK", dbh); + int ret = pdo_pgsql_transaction_cmd("ROLLBACK", dbh); + + if (ret) { + pdo_pgsql_close_lob_streams(dbh); + } + + return ret; } /* {{{ Returns true if the copy worked fine or false if error */ @@ -1242,6 +1277,8 @@ static int pdo_pgsql_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ } H->server = PQconnectdb(conn_str); + H->lob_streams = (HashTable *) pemalloc(sizeof(HashTable), dbh->is_persistent); + zend_hash_init(H->lob_streams, 0, NULL, NULL, 1); if (tmp_user) { zend_string_release_ex(tmp_user, 0); diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index 03ac14c32db9a..76157ae17fec4 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -134,6 +134,8 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt) pdo_pgsql_db_handle *H = S->H; ExecStatusType status; + bool in_trans = stmt->dbh->methods->in_transaction(stmt->dbh); + /* ensure that we free any previous unfetched results */ if(S->result) { PQclear(S->result); @@ -252,6 +254,10 @@ static int pgsql_stmt_execute(pdo_stmt_t *stmt) stmt->row_count = (zend_long)PQntuples(S->result); } + if (in_trans && !stmt->dbh->methods->in_transaction(stmt->dbh)) { + pdo_pgsql_close_lob_streams(stmt->dbh); + } + return 1; } diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index 8ef8b68138489..f45718f0c96a2 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -45,6 +45,7 @@ typedef struct { bool emulate_prepares; bool disable_native_prepares; /* deprecated since 5.6 */ bool disable_prepares; + HashTable *lob_streams; } pdo_pgsql_db_handle; typedef struct { @@ -106,5 +107,6 @@ php_stream *pdo_pgsql_create_lob_stream(zval *pdh, int lfd, Oid oid); extern const php_stream_ops pdo_pgsql_lob_stream_ops; void pdo_libpq_version(char *buf, size_t len); +void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh); #endif /* PHP_PDO_PGSQL_INT_H */ diff --git a/ext/pdo_pgsql/tests/gh9411.phpt b/ext/pdo_pgsql/tests/gh9411.phpt new file mode 100644 index 0000000000000..c8a11e89df627 --- /dev/null +++ b/ext/pdo_pgsql/tests/gh9411.phpt @@ -0,0 +1,41 @@ +--TEST-- +Bug GH-9411 (PgSQL large object resource is incorrectly closed) +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + +$db->beginTransaction(); +$oid = $db->pgsqlLOBCreate(); +var_dump($lob = $db->pgsqlLOBOpen($oid, 'wb')); +fwrite($lob, 'test'); +$db->rollback(); +var_dump($lob); + +$db->beginTransaction(); +$oid = $db->pgsqlLOBCreate(); +var_dump($lob = $db->pgsqlLOBOpen($oid, 'wb')); +fwrite($lob, 'test'); +$db->commit(); +var_dump($lob); + +$db->beginTransaction(); +var_dump($lob = $db->pgsqlLOBOpen($oid, 'wb')); +var_dump(fgets($lob)); +?> +--EXPECTF-- +resource(%d) of type (stream) +resource(%d) of type (Unknown) +resource(%d) of type (stream) +resource(%d) of type (Unknown) +resource(%d) of type (stream) +string(4) "test" diff --git a/ext/phar/phar.c b/ext/phar/phar.c index bc08e4edde05d..b45586d1e8fad 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1627,6 +1627,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char const char gz_magic[] = "\x1f\x8b\x08"; const char bz_magic[] = "BZh"; char *pos, test = '\0'; + int recursion_count = 3; // arbitrary limit to avoid too deep or even infinite recursion const int window_size = 1024; char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */ const zend_long readsize = sizeof(buffer) - sizeof(token); @@ -1654,7 +1655,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)") } - if (!test) { + if (!test && recursion_count) { test = '\1'; pos = buffer+tokenlen; if (!memcmp(pos, gz_magic, 3)) { @@ -1716,6 +1717,10 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char /* now, start over */ test = '\0'; + if (!--recursion_count) { + MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\""); + break; + } continue; } else if (!memcmp(pos, bz_magic, 3)) { php_stream_filter *filter; @@ -1754,6 +1759,10 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char /* now, start over */ test = '\0'; + if (!--recursion_count) { + MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\""); + break; + } continue; } diff --git a/ext/phar/tests/bug81726.gz b/ext/phar/tests/bug81726.gz new file mode 100644 index 0000000000000..67b41ba3b6567 Binary files /dev/null and b/ext/phar/tests/bug81726.gz differ diff --git a/ext/phar/tests/bug81726.phpt b/ext/phar/tests/bug81726.phpt new file mode 100644 index 0000000000000..b698f0803706a --- /dev/null +++ b/ext/phar/tests/bug81726.phpt @@ -0,0 +1,14 @@ +--TEST-- +Bug #81726 (phar wrapper: DOS when using quine gzip file) +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: fopen(phar://%s): Failed to open stream: unable to decompress gzipped phar archive "%s" in %s on line %d +bool(false) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index c4291a9ab8cbb..8b53e4fced9b4 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -793,7 +793,7 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent } else if (fptr->common.scope->parent) { lc_name = zend_string_tolower(fptr->common.function_name); if ((overwrites = zend_hash_find_ptr(&fptr->common.scope->parent->function_table, lc_name)) != NULL) { - if (fptr->common.scope != overwrites->common.scope) { + if (fptr->common.scope != overwrites->common.scope && !(overwrites->common.fn_flags & ZEND_ACC_PRIVATE)) { smart_str_append_printf(str, ", overwrites %s", ZSTR_VAL(overwrites->common.scope->name)); } } @@ -1696,6 +1696,28 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass) } /* }}} */ +/* {{{ Returns the called scope associated to the closure */ +ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass) +{ + reflection_object *intern; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT(); + if (!Z_ISUNDEF(intern->obj)) { + zend_class_entry *called_scope; + zend_function *closure_func; + zend_object *object; + if (Z_OBJ_HANDLER(intern->obj, get_closure) + && Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, 1) == SUCCESS + && closure_func && (called_scope || closure_func->common.scope)) { + zend_reflection_class_factory(called_scope ? (zend_class_entry *) called_scope : closure_func->common.scope, return_value); + } + } +} +/* }}} */ + /* {{{ Returns an associative array containing the closures lexical scope variables */ ZEND_METHOD(ReflectionFunctionAbstract, getClosureUsedVariables) { diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 8f2ef6ec6f44e..750eceed66402 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -54,6 +54,9 @@ public function getClosureThis(): ?object {} /** @tentative-return-type */ public function getClosureScopeClass(): ?ReflectionClass {} + /** @tentative-return-type */ + public function getClosureCalledClass(): ?ReflectionClass {} + public function getClosureUsedVariables(): array {} /** @tentative-return-type */ diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 12b01c4d264fd..6242ac1c31e22 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 62fcf63d2f3e93537560c3a03e71fda131a31586 */ + * Stub hash: ab0dd21b2fc7ff18c39275e1ec82211c7058c32a */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -31,6 +31,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass, 0, 0, ReflectionClass, 1) ZEND_END_ARG_INFO() +#define arginfo_class_ReflectionFunctionAbstract_getClosureCalledClass arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -608,6 +610,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, isVariadic); ZEND_METHOD(ReflectionFunctionAbstract, isStatic); ZEND_METHOD(ReflectionFunctionAbstract, getClosureThis); ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass); +ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass); ZEND_METHOD(ReflectionFunctionAbstract, getClosureUsedVariables); ZEND_METHOD(ReflectionFunctionAbstract, getDocComment); ZEND_METHOD(ReflectionFunctionAbstract, getEndLine); @@ -851,6 +854,7 @@ static const zend_function_entry class_ReflectionFunctionAbstract_methods[] = { ZEND_ME(ReflectionFunctionAbstract, isStatic, arginfo_class_ReflectionFunctionAbstract_isStatic, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getClosureThis, arginfo_class_ReflectionFunctionAbstract_getClosureThis, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getClosureScopeClass, arginfo_class_ReflectionFunctionAbstract_getClosureScopeClass, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFunctionAbstract, getClosureCalledClass, arginfo_class_ReflectionFunctionAbstract_getClosureCalledClass, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getClosureUsedVariables, arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getDocComment, arginfo_class_ReflectionFunctionAbstract_getDocComment, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getEndLine, arginfo_class_ReflectionFunctionAbstract_getEndLine, ZEND_ACC_PUBLIC) diff --git a/ext/reflection/tests/ReflectionClass_toString_003.phpt b/ext/reflection/tests/ReflectionClass_toString_003.phpt index a67d0bfac5fe3..4d488325eebbf 100644 --- a/ext/reflection/tests/ReflectionClass_toString_003.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_003.phpt @@ -66,7 +66,7 @@ Class [ class B extends A ] { } - Methods [1] { - Method [ private method f ] { + Method [ private method f ] { @@ %s 6 - 6 } } @@ -111,7 +111,7 @@ Class [ class D extends C ] { } - Methods [1] { - Method [ private method f ] { + Method [ private method f ] { @@ %s 12 - 12 } } diff --git a/ext/reflection/tests/ReflectionFunction_getClosureCalledClass.phpt b/ext/reflection/tests/ReflectionFunction_getClosureCalledClass.phpt new file mode 100644 index 0000000000000..ab06a6472e0c1 --- /dev/null +++ b/ext/reflection/tests/ReflectionFunction_getClosureCalledClass.phpt @@ -0,0 +1,108 @@ +--TEST-- +GH-8932 (Provide a way to get the called-scope of closures) +--FILE-- +'.$name, "\n"; + } + + public static function b() { + echo static::class.'::b', "\n"; + } + + + public function c() { + echo static::class.'->c', "\n"; + } + + public function makeClosure() { + return function () { + echo static::class.'::{closure}'."\n"; + }; + } +} + +class B extends A {} + +$c = ['B', 'b']; +$d = \Closure::fromCallable($c); +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); + +$c = [new B(), 'c']; +$d = \Closure::fromCallable($c); +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); + +$c = ['B', 'd']; +$d = \Closure::fromCallable($c); +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); + +$c = [new B(), 'e']; +$d = \Closure::fromCallable($c); +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); + +$c = ['A', 'b']; +$d = \Closure::fromCallable($c); +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); + +$b = new B(); +$d = $b->makeClosure(); +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); + +$d = function () { + echo "{closure}\n"; +}; +$r = new \ReflectionFunction($d); +var_dump($r->getClosureCalledClass()); +$d(); + +?> +--EXPECTF-- +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "B" +} +B::b +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "B" +} +B->c +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "B" +} +B::d +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "B" +} +B->e +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "A" +} +A::b +object(ReflectionClass)#%d (1) { + ["name"]=> + string(1) "B" +} +B::{closure} +NULL +{closure} diff --git a/ext/reflection/tests/gh9409.phpt b/ext/reflection/tests/gh9409.phpt new file mode 100644 index 0000000000000..a10e7b5303a77 --- /dev/null +++ b/ext/reflection/tests/gh9409.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-9409: Private method is incorrectly dumped as "overwrites" +--FILE-- +getMethod('privateMethod'); +echo (new ReflectionClass('C'))->getMethod('privateMethod'); + +?> +--EXPECTF-- +Method [ private method privateMethod ] { + @@ %s %d - %d +} +Method [ private method privateMethod ] { + @@ %s %d - %d +} diff --git a/ext/reflection/tests/gh9447.phpt b/ext/reflection/tests/gh9447.phpt new file mode 100644 index 0000000000000..9ee908329733a --- /dev/null +++ b/ext/reflection/tests/gh9447.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-9447: Invalid class FQN emitted by AST dump for new and class constants in constant expressions +--FILE-- +getParameters() as $p) { + echo $p, "\n"; +} + +?> +--EXPECT-- +Parameter #0 [ $a = \App\Bar::BAZ ] +Parameter #1 [ $b = new \App\Bar() ] +Parameter #2 [ $c = new parent() ] +Parameter #3 [ $d = new self() ] +Parameter #4 [ $e = new \App\Bar() ] +Parameter #5 [ $f = new \SomewhereElse\Qux() ] +Parameter #6 [ $g = new \App\Qux() ] +Parameter #7 [ $i = new \Qux() ] diff --git a/ext/standard/array.c b/ext/standard/array.c index aa57b261c1fdb..e4261b428e9b0 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -166,25 +166,40 @@ static zend_never_inline ZEND_COLD int stable_sort_fallback(Bucket *a, Bucket *b static zend_always_inline int php_array_key_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */ { - zval first; - zval second; - - if (f->key == NULL && s->key == NULL) { - return (zend_long)f->h > (zend_long)s->h ? 1 : -1; - } else if (f->key && s->key) { - return zendi_smart_strcmp(f->key, s->key); - } - if (f->key) { - ZVAL_STR(&first, f->key); - } else { - ZVAL_LONG(&first, f->h); - } - if (s->key) { - ZVAL_STR(&second, s->key); - } else { - ZVAL_LONG(&second, s->h); - } - return zend_compare(&first, &second); + zend_uchar t; + zend_long l1, l2; + double d; + + if (f->key == NULL) { + if (s->key == NULL) { + return (zend_long)f->h > (zend_long)s->h ? 1 : -1; + } else { + l1 = (zend_long)f->h; + t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1); + if (t == IS_LONG) { + /* pass */ + } else if (t == IS_DOUBLE) { + return ZEND_NORMALIZE_BOOL((double)l1 - d); + } else { + l2 = 0; + } + } + } else { + if (s->key) { + return zendi_smart_strcmp(f->key, s->key); + } else { + l2 = (zend_long)s->h; + t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1); + if (t == IS_LONG) { + /* pass */ + } else if (t == IS_DOUBLE) { + return ZEND_NORMALIZE_BOOL(d - (double)l2); + } else { + l1 = 0; + } + } + } + return ZEND_NORMALIZE_BOOL(l1 - l2); } /* }}} */ diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index b5132d9e005a3..5964efd2f9a1c 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -713,6 +713,10 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (tmp_line_len >= 1 &&tmp_line[tmp_line_len - 1] == '\r') { --tmp_line_len; } + } else { + // read and discard rest of status line + char *line = php_stream_get_line(stream, NULL, 0, NULL); + efree(line); } ZVAL_STRINGL(&http_response, tmp_line, tmp_line_len); zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_response); diff --git a/ext/standard/tests/array/002.phpt b/ext/standard/tests/array/002.phpt index 5cf377e2a3538..fdb838deb19dd 100644 --- a/ext/standard/tests/array/002.phpt +++ b/ext/standard/tests/array/002.phpt @@ -276,8 +276,6 @@ array(8) { -- Testing krsort() -- No second argument: array(8) { - ["test"]=> - int(27) [16777216]=> float(-0.3333333333333333) [1001]=> @@ -290,6 +288,8 @@ array(8) { string(4) "Test" [0]=> string(3) "PHP" + ["test"]=> + int(27) [-1000]=> array(2) { [0]=> @@ -300,8 +300,6 @@ array(8) { } Using SORT_REGULAR: array(8) { - ["test"]=> - int(27) [16777216]=> float(-0.3333333333333333) [1001]=> @@ -314,6 +312,8 @@ array(8) { string(4) "Test" [0]=> string(3) "PHP" + ["test"]=> + int(27) [-1000]=> array(2) { [0]=> @@ -334,10 +334,10 @@ array(8) { string(27) "PHP: Hypertext Preprocessor" [5]=> string(4) "Test" - ["test"]=> - int(27) [0]=> string(3) "PHP" + ["test"]=> + int(27) [-1000]=> array(2) { [0]=> @@ -383,6 +383,8 @@ array(8) { } [0]=> string(3) "PHP" + ["test"]=> + int(27) [5]=> string(4) "Test" [17]=> @@ -393,8 +395,6 @@ array(8) { string(6) "monkey" [16777216]=> float(-0.3333333333333333) - ["test"]=> - int(27) } Using SORT_REGULAR: array(8) { @@ -407,6 +407,8 @@ array(8) { } [0]=> string(3) "PHP" + ["test"]=> + int(27) [5]=> string(4) "Test" [17]=> @@ -417,8 +419,6 @@ array(8) { string(6) "monkey" [16777216]=> float(-0.3333333333333333) - ["test"]=> - int(27) } Using SORT_NUMERIC: array(8) { diff --git a/ext/standard/tests/array/gh9296.phpt b/ext/standard/tests/array/gh9296.phpt deleted file mode 100644 index cfbc5cbbb09ec..0000000000000 --- a/ext/standard/tests/array/gh9296.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -GH-9296: incorrect ksort(..., SORT_REGULAR) behaviour on arrays with numeric and string keys ---FILE-- - 0, 600 => 1]; -ksort($array, SORT_REGULAR); -var_dump($array); -var_dump(array_key_first($array) <=> array_key_last($array)); -?> ---EXPECT-- -array(2) { - [600]=> - int(1) - ["aaa"]=> - int(0) -} -int(-1) diff --git a/ext/standard/tests/array/krsort_variation8.phpt b/ext/standard/tests/array/krsort_variation8.phpt index 4c1c6f2173d26..caa0f21936027 100644 --- a/ext/standard/tests/array/krsort_variation8.phpt +++ b/ext/standard/tests/array/krsort_variation8.phpt @@ -41,6 +41,10 @@ echo "Done\n"; -- Testing krsort() by supplying mixed value array, 'flag' value is default -- bool(true) array(13) { + [5]=> + string(1) "5" + [4]=> + float(4.01) ["b"]=> string(1) "b" ["array2"]=> @@ -81,23 +85,23 @@ array(13) { string(2) "ab" ["True"]=> string(4) "True" - [5]=> - string(1) "5" - [4]=> - float(4.01) [0]=> float(0.001) ["-.9"]=> string(3) "-.9" - [-2]=> - float(-2.98989) [""]=> string(0) "" + [-2]=> + float(-2.98989) } -- Testing krsort() by supplying mixed value array, 'flag' value is SORT_REGULAR -- bool(true) array(13) { + [5]=> + string(1) "5" + [4]=> + float(4.01) ["b"]=> string(1) "b" ["array2"]=> @@ -138,17 +142,13 @@ array(13) { string(2) "ab" ["True"]=> string(4) "True" - [5]=> - string(1) "5" - [4]=> - float(4.01) [0]=> float(0.001) ["-.9"]=> string(3) "-.9" - [-2]=> - float(-2.98989) [""]=> string(0) "" + [-2]=> + float(-2.98989) } Done diff --git a/ext/standard/tests/array/krsort_variation9.phpt b/ext/standard/tests/array/krsort_variation9.phpt index b0309f373d362..4364911b62127 100644 --- a/ext/standard/tests/array/krsort_variation9.phpt +++ b/ext/standard/tests/array/krsort_variation9.phpt @@ -82,22 +82,22 @@ array(5) { - With default sort flag - bool(true) array(3) { - ["c"]=> - string(5) "apple" ["a"]=> string(6) "orange" [0]=> string(6) "banana" + ["c"]=> + string(5) "apple" } - Sort flag = SORT_REGULAR - bool(true) array(3) { - ["c"]=> - string(5) "apple" ["a"]=> string(6) "orange" [0]=> string(6) "banana" + ["c"]=> + string(5) "apple" } -- Iteration 3 -- diff --git a/ext/standard/tests/array/ksort_variation8.phpt b/ext/standard/tests/array/ksort_variation8.phpt index 236c405c64b95..10ed218211f07 100644 --- a/ext/standard/tests/array/ksort_variation8.phpt +++ b/ext/standard/tests/array/ksort_variation8.phpt @@ -40,26 +40,18 @@ echo "Done\n"; -- Testing ksort() by supplying mixed value array, 'flag' value is default -- bool(true) array(13) { - [""]=> - string(0) "" [-2]=> float(-2.98989) + [""]=> + string(0) "" ["-.9"]=> string(3) "-.9" - [0]=> - float(0.001) - [4]=> - float(4.01) - [5]=> - string(1) "5" ["True"]=> string(4) "True" ["ab"]=> string(2) "ab" ["abcd"]=> string(4) "abcd" - ["abcd%0abcd%0abcd"]=> - string(14) "abcd%0abcd%0abcd" ["array1"]=> array(0) { } @@ -92,31 +84,31 @@ array(13) { } ["b"]=> string(1) "b" + [0]=> + float(0.001) + ["abcd%0abcd%0abcd"]=> + string(14) "abcd%0abcd%0abcd" + [4]=> + float(4.01) + [5]=> + string(1) "5" } -- Testing ksort() by supplying mixed value array, 'flag' value is SORT_REGULAR -- bool(true) array(13) { - [""]=> - string(0) "" [-2]=> float(-2.98989) + [""]=> + string(0) "" ["-.9"]=> string(3) "-.9" - [0]=> - float(0.001) - [4]=> - float(4.01) - [5]=> - string(1) "5" ["True"]=> string(4) "True" ["ab"]=> string(2) "ab" ["abcd"]=> string(4) "abcd" - ["abcd%0abcd%0abcd"]=> - string(14) "abcd%0abcd%0abcd" ["array1"]=> array(0) { } @@ -149,5 +141,13 @@ array(13) { } ["b"]=> string(1) "b" + [0]=> + float(0.001) + ["abcd%0abcd%0abcd"]=> + string(14) "abcd%0abcd%0abcd" + [4]=> + float(4.01) + [5]=> + string(1) "5" } Done diff --git a/ext/standard/tests/array/ksort_variation9.phpt b/ext/standard/tests/array/ksort_variation9.phpt index 0023a9e0602ae..9322af70f57fa 100644 --- a/ext/standard/tests/array/ksort_variation9.phpt +++ b/ext/standard/tests/array/ksort_variation9.phpt @@ -81,20 +81,20 @@ array(5) { - With default sort flag - bool(true) array(3) { - [0]=> - string(6) "banana" ["a"]=> string(6) "orange" + [0]=> + string(6) "banana" ["c"]=> string(5) "apple" } - Sort flag = SORT_REGULAR - bool(true) array(3) { - [0]=> - string(6) "banana" ["a"]=> string(6) "orange" + [0]=> + string(6) "banana" ["c"]=> string(5) "apple" } diff --git a/ext/standard/tests/bug81727.phpt b/ext/standard/tests/bug81727.phpt new file mode 100644 index 0000000000000..71a9cb46c83be --- /dev/null +++ b/ext/standard/tests/bug81727.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #81727: $_COOKIE name starting with ..Host/..Secure should be discarded +--COOKIE-- +..Host-test=ignore; __Host-test=correct; . Secure-test=ignore; . Elephpant=Awesome; +--FILE-- + +--EXPECT-- +array(2) { + ["__Host-test"]=> + string(7) "correct" + ["__Elephpant"]=> + string(7) "Awesome" +} diff --git a/ext/standard/tests/http/gh9316.phpt b/ext/standard/tests/http/gh9316.phpt new file mode 100644 index 0000000000000..126e1bb7bf72f --- /dev/null +++ b/ext/standard/tests/http/gh9316.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug GH-9316 ($http_response_header is wrong for long status line) +--SKIPIF-- + +--INI-- +allow_url_fopen=1 +--FILE-- + $pid, 'uri' => $uri] = http_server($responses, $output); + +for ($i = 0; $i < count($responses); ++$i) { + $f = @fopen($uri, "r"); + var_dump($http_response_header); + fclose($f); +} + +http_server_kill($pid); + +--EXPECT-- +array(2) { + [0]=> + string(126) "HTTP/1.1 200 Some very long reason-phrase to test that this is properly handled by our code without adding a new header like " + [1]=> + string(12) "Good: Header" +} +array(2) { + [0]=> + string(13) "HTTP/1.1 200 " + [1]=> + string(12) "Good: Header" +} diff --git a/main/SAPI.c b/main/SAPI.c index 039ba06827089..561cf6d598c6d 100644 --- a/main/SAPI.c +++ b/main/SAPI.c @@ -489,7 +489,7 @@ static void sapi_send_headers_free(void) } } -SAPI_API void sapi_deactivate(void) +SAPI_API void sapi_deactivate_module(void) { zend_llist_destroy(&SG(sapi_headers).headers); if (SG(request_info).request_body) { @@ -523,6 +523,10 @@ SAPI_API void sapi_deactivate(void) if (sapi_module.deactivate) { sapi_module.deactivate(); } +} + +SAPI_API void sapi_deactivate_destroy(void) +{ if (SG(rfc1867_uploaded_files)) { destroy_uploaded_files_hash(); } @@ -537,6 +541,12 @@ SAPI_API void sapi_deactivate(void) SG(global_request_time) = 0; } +SAPI_API void sapi_deactivate(void) +{ + sapi_deactivate_module(); + sapi_deactivate_destroy(); +} + SAPI_API void sapi_initialize_empty_request(void) { diff --git a/main/SAPI.h b/main/SAPI.h index 97c52c41eccce..56304e0fefb59 100644 --- a/main/SAPI.h +++ b/main/SAPI.h @@ -143,6 +143,8 @@ extern SAPI_API sapi_globals_struct sapi_globals; SAPI_API void sapi_startup(sapi_module_struct *sf); SAPI_API void sapi_shutdown(void); SAPI_API void sapi_activate(void); +SAPI_API void sapi_deactivate_module(void); +SAPI_API void sapi_deactivate_destroy(void); SAPI_API void sapi_deactivate(void); SAPI_API void sapi_initialize_empty_request(void); SAPI_API void sapi_add_request_header(const char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg); diff --git a/main/main.c b/main/main.c index 252e50a9ff3c2..b536504be72ff 100644 --- a/main/main.c +++ b/main/main.c @@ -1854,10 +1854,12 @@ void php_request_shutdown(void *dummy) zend_post_deactivate_modules(); } zend_end_try(); - /* 12. SAPI related shutdown (free stuff) */ + /* 12. SAPI related shutdown*/ zend_try { - sapi_deactivate(); + sapi_deactivate_module(); } zend_end_try(); + /* free SAPI stuff */ + sapi_deactivate_destroy(); /* 13. free virtual CWD memory */ virtual_cwd_deactivate(); diff --git a/main/php_variables.c b/main/php_variables.c index 0261cf0098ee4..17e4a1e5d2cf1 100644 --- a/main/php_variables.c +++ b/main/php_variables.c @@ -104,6 +104,20 @@ PHPAPI void php_register_variable_ex(const char *var_name, zval *val, zval *trac } var_len = p - var; + /* Discard variable if mangling made it start with __Host-, where pre-mangling it did not start with __Host- */ + if (strncmp(var, "__Host-", sizeof("__Host-")-1) == 0 && strncmp(var_name, "__Host-", sizeof("__Host-")-1) != 0) { + zval_ptr_dtor_nogc(val); + free_alloca(var_orig, use_heap); + return; + } + + /* Discard variable if mangling made it start with __Secure-, where pre-mangling it did not start with __Secure- */ + if (strncmp(var, "__Secure-", sizeof("__Secure-")-1) == 0 && strncmp(var_name, "__Secure-", sizeof("__Secure-")-1) != 0) { + zval_ptr_dtor_nogc(val); + free_alloca(var_orig, use_heap); + return; + } + if (var_len==0) { /* empty variable name, or variable name with a space in it */ zval_ptr_dtor_nogc(val); free_alloca(var_orig, use_heap); diff --git a/main/php_version.h b/main/php_version.h index 218f92c2fc14b..ceeb6aeff3abe 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 1 -#define PHP_RELEASE_VERSION 10 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.1.10-dev" -#define PHP_VERSION_ID 80110 +#define PHP_RELEASE_VERSION 11 +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.1.11" +#define PHP_VERSION_ID 80111 diff --git a/main/streams/streams.c b/main/streams/streams.c index 1f73ee6587122..9359ed2fcccdd 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1674,6 +1674,7 @@ static void stream_resource_persistent_dtor(zend_resource *rsrc) void php_shutdown_stream_hashes(void) { + FG(user_stream_current_filename) = NULL; if (FG(stream_wrappers)) { zend_hash_destroy(FG(stream_wrappers)); efree(FG(stream_wrappers)); diff --git a/sapi/fpm/fpm/events/devpoll.c b/sapi/fpm/fpm/events/devpoll.c index 21d9811c3a1a5..c7287b14f705e 100644 --- a/sapi/fpm/fpm/events/devpoll.c +++ b/sapi/fpm/fpm/events/devpoll.c @@ -29,7 +29,7 @@ #include static int fpm_event_devpoll_init(int max); -static int fpm_event_devpoll_clean(); +static int fpm_event_devpoll_clean(void); static int fpm_event_devpoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout); static int fpm_event_devpoll_add(struct fpm_event_s *ev); static int fpm_event_devpoll_remove(struct fpm_event_s *ev); @@ -51,7 +51,7 @@ static int npollfds = 0; #endif /* HAVE_DEVPOLL */ -struct fpm_event_module_s *fpm_event_devpoll_module(void) /* {{{ */ +struct fpm_event_module_s *fpm_event_devpoll_module(void) { #ifdef HAVE_DEVPOLL return &devpoll_module; @@ -59,7 +59,6 @@ struct fpm_event_module_s *fpm_event_devpoll_module(void) /* {{{ */ return NULL; #endif /* HAVE_DEVPOLL */ } -/* }}} */ #ifdef HAVE_DEVPOLL @@ -113,7 +112,7 @@ static int fpm_event_devpoll_init(int max) /* {{{ */ /* * Clean the module */ -static int fpm_event_devpoll_clean() /* {{{ */ +static int fpm_event_devpoll_clean(void) { /* close /dev/poll if open */ if (dpfd > -1) { @@ -136,7 +135,6 @@ static int fpm_event_devpoll_clean() /* {{{ */ npollfds = 0; return 0; } -/* }}} */ /* * wait for events or timeout diff --git a/sapi/fpm/fpm/events/epoll.c b/sapi/fpm/fpm/events/epoll.c index 4b2d437b8807b..0a6eadb6aebe8 100644 --- a/sapi/fpm/fpm/events/epoll.c +++ b/sapi/fpm/fpm/events/epoll.c @@ -46,7 +46,7 @@ static int epollfd = -1; #endif /* HAVE_EPOLL */ -struct fpm_event_module_s *fpm_event_epoll_module(void) /* {{{ */ +struct fpm_event_module_s *fpm_event_epoll_module(void) { #ifdef HAVE_EPOLL return &epoll_module; @@ -54,7 +54,6 @@ struct fpm_event_module_s *fpm_event_epoll_module(void) /* {{{ */ return NULL; #endif /* HAVE_EPOLL */ } -/* }}} */ #ifdef HAVE_EPOLL @@ -92,7 +91,7 @@ static int fpm_event_epoll_init(int max) /* {{{ */ /* * Clean the module */ -static int fpm_event_epoll_clean() /* {{{ */ +static int fpm_event_epoll_clean(void) { /* free epollfds */ if (epollfds) { @@ -108,7 +107,6 @@ static int fpm_event_epoll_clean() /* {{{ */ return 0; } -/* }}} */ /* * wait for events or timeout diff --git a/sapi/fpm/fpm/events/poll.c b/sapi/fpm/fpm/events/poll.c index cceecbde5edc5..30ebbc785d943 100644 --- a/sapi/fpm/fpm/events/poll.c +++ b/sapi/fpm/fpm/events/poll.c @@ -50,7 +50,7 @@ static int next_free_slot = 0; /* * return the module configuration */ -struct fpm_event_module_s *fpm_event_poll_module(void) /* {{{ */ +struct fpm_event_module_s *fpm_event_poll_module(void) { #ifdef HAVE_POLL return &poll_module; @@ -58,14 +58,13 @@ struct fpm_event_module_s *fpm_event_poll_module(void) /* {{{ */ return NULL; #endif /* HAVE_POLL */ } -/* }}} */ #ifdef HAVE_POLL /* * Init the module */ -static int fpm_event_poll_init(int max) /* {{{ */ +static int fpm_event_poll_init(int max) { int i; @@ -99,12 +98,11 @@ static int fpm_event_poll_init(int max) /* {{{ */ npollfds = max; return 0; } -/* }}} */ /* * Clean the module */ -static int fpm_event_poll_clean() /* {{{ */ +static int fpm_event_poll_clean(void) { /* free pollfds */ if (pollfds) { @@ -121,7 +119,6 @@ static int fpm_event_poll_clean() /* {{{ */ npollfds = 0; return 0; } -/* }}} */ /* * wait for events or timeout diff --git a/sapi/fpm/fpm/events/port.c b/sapi/fpm/fpm/events/port.c index 4ee98807b6daf..0df106250a977 100644 --- a/sapi/fpm/fpm/events/port.c +++ b/sapi/fpm/fpm/events/port.c @@ -26,7 +26,7 @@ #include static int fpm_event_port_init(int max); -static int fpm_event_port_clean(); +static int fpm_event_port_clean(void); static int fpm_event_port_wait(struct fpm_event_queue_s *queue, unsigned long int timeout); static int fpm_event_port_add(struct fpm_event_s *ev); static int fpm_event_port_remove(struct fpm_event_s *ev); @@ -90,7 +90,7 @@ static int fpm_event_port_init(int max) /* {{{ */ /* * Clean the module */ -static int fpm_event_port_clean() /* {{{ */ +static int fpm_event_port_clean(void) { if (pfd > -1) { close(pfd); @@ -105,7 +105,6 @@ static int fpm_event_port_clean() /* {{{ */ nevents = 0; return 0; } -/* }}} */ /* * wait for events or timeout diff --git a/sapi/fpm/fpm/events/select.c b/sapi/fpm/fpm/events/select.c index e8dfafdb642a3..66e8107f07c63 100644 --- a/sapi/fpm/fpm/events/select.c +++ b/sapi/fpm/fpm/events/select.c @@ -53,7 +53,7 @@ static fd_set fds; /* * return the module configuration */ -struct fpm_event_module_s *fpm_event_select_module(void) /* {{{ */ +struct fpm_event_module_s *fpm_event_select_module(void) { #ifdef HAVE_SELECT return &select_module; @@ -61,7 +61,6 @@ struct fpm_event_module_s *fpm_event_select_module(void) /* {{{ */ return NULL; #endif /* HAVE_SELECT */ } -/* }}} */ #ifdef HAVE_SELECT diff --git a/sapi/fpm/fpm/fpm_children.c b/sapi/fpm/fpm/fpm_children.c index bcb5b4cef7a70..741fc27631516 100644 --- a/sapi/fpm/fpm/fpm_children.c +++ b/sapi/fpm/fpm/fpm_children.c @@ -38,7 +38,7 @@ static void fpm_children_cleanup(int which, void *arg) /* {{{ */ } /* }}} */ -static struct fpm_child_s *fpm_child_alloc(void) /* {{{ */ +static struct fpm_child_s *fpm_child_alloc(void) { struct fpm_child_s *ret; @@ -52,7 +52,6 @@ static struct fpm_child_s *fpm_child_alloc(void) /* {{{ */ ret->scoreboard_i = -1; return ret; } -/* }}} */ static void fpm_child_free(struct fpm_child_s *child) /* {{{ */ { @@ -177,7 +176,7 @@ int fpm_children_free(struct fpm_child_s *child) /* {{{ */ } /* }}} */ -void fpm_children_bury() /* {{{ */ +void fpm_children_bury(void) { int status; pid_t pid; @@ -303,7 +302,6 @@ void fpm_children_bury() /* {{{ */ } } } -/* }}} */ static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp) /* {{{ */ { @@ -472,7 +470,7 @@ int fpm_children_create_initial(struct fpm_worker_pool_s *wp) /* {{{ */ } /* }}} */ -int fpm_children_init_main() /* {{{ */ +int fpm_children_init_main(void) { if (fpm_global_config.emergency_restart_threshold && fpm_global_config.emergency_restart_interval) { @@ -492,4 +490,3 @@ int fpm_children_init_main() /* {{{ */ return 0; } -/* }}} */ diff --git a/sapi/fpm/fpm/fpm_clock.c b/sapi/fpm/fpm/fpm_clock.c index 72de43e327506..57faccbe9d80d 100644 --- a/sapi/fpm/fpm/fpm_clock.c +++ b/sapi/fpm/fpm/fpm_clock.c @@ -15,7 +15,7 @@ static int monotonic_works; -int fpm_clock_init() /* {{{ */ +int fpm_clock_init(void) { struct timespec ts; @@ -27,7 +27,6 @@ int fpm_clock_init() /* {{{ */ return 0; } -/* }}} */ int fpm_clock_get(struct timeval *tv) /* {{{ */ { @@ -59,7 +58,7 @@ static clock_serv_t mach_clock; /* this code borrowed from here: http://lists.apple.com/archives/Darwin-development/2002/Mar/msg00746.html */ /* mach_clock also should be re-initialized in child process after fork */ -int fpm_clock_init() /* {{{ */ +int fpm_clock_init(void) { kern_return_t ret; mach_timespec_t aTime; @@ -81,7 +80,6 @@ int fpm_clock_init() /* {{{ */ return 0; } -/* }}} */ int fpm_clock_get(struct timeval *tv) /* {{{ */ { @@ -104,11 +102,10 @@ int fpm_clock_get(struct timeval *tv) /* {{{ */ #else /* no clock */ -int fpm_clock_init() /* {{{ */ +int fpm_clock_init(void) { return 0; } -/* }}} */ int fpm_clock_get(struct timeval *tv) /* {{{ */ { diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c index b4178625611ea..b2b1d64aa7e46 100644 --- a/sapi/fpm/fpm/fpm_conf.c +++ b/sapi/fpm/fpm/fpm_conf.c @@ -593,7 +593,7 @@ static char *fpm_conf_set_array(zval *key, zval *value, void **config, int conve } /* }}} */ -static void *fpm_worker_pool_config_alloc(void) /* {{{ */ +static void *fpm_worker_pool_config_alloc(void) { struct fpm_worker_pool_s *wp; @@ -635,7 +635,6 @@ static void *fpm_worker_pool_config_alloc(void) /* {{{ */ current_wp = wp; return wp->config; } -/* }}} */ int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc) /* {{{ */ { @@ -795,7 +794,7 @@ static int fpm_evaluate_full_path(char **path, struct fpm_worker_pool_s *wp, cha } /* }}} */ -static int fpm_conf_process_all_pools(void) /* {{{ */ +static int fpm_conf_process_all_pools(void) { struct fpm_worker_pool_s *wp, *wp2; @@ -1194,9 +1193,8 @@ static int fpm_conf_process_all_pools(void) /* {{{ */ } return 0; } -/* }}} */ -int fpm_conf_unlink_pid() /* {{{ */ +int fpm_conf_unlink_pid(void) { if (fpm_global_config.pid_file) { if (0 > unlink(fpm_global_config.pid_file)) { @@ -1206,9 +1204,8 @@ int fpm_conf_unlink_pid() /* {{{ */ } return 0; } -/* }}} */ -int fpm_conf_write_pid() /* {{{ */ +int fpm_conf_write_pid(void) { int fd; @@ -1235,7 +1232,6 @@ int fpm_conf_write_pid() /* {{{ */ } return 0; } -/* }}} */ static int fpm_conf_post_process(int force_daemon) /* {{{ */ { @@ -1294,6 +1290,10 @@ static int fpm_conf_post_process(int force_daemon) /* {{{ */ fpm_evaluate_full_path(&fpm_global_config.error_log, NULL, PHP_LOCALSTATEDIR, 0); } + if (0 > fpm_stdio_save_original_stderr()) { + return -1; + } + if (0 > fpm_stdio_open_error_log(0)) { return -1; } @@ -1662,7 +1662,7 @@ int fpm_conf_load_ini_file(char *filename) /* {{{ */ } /* }}} */ -static void fpm_conf_dump(void) /* {{{ */ +static void fpm_conf_dump(void) { struct fpm_worker_pool_s *wp; @@ -1766,7 +1766,6 @@ static void fpm_conf_dump(void) /* {{{ */ zlog(ZLOG_NOTICE, " "); } } -/* }}} */ int fpm_conf_init_main(int test_conf, int force_daemon) /* {{{ */ { diff --git a/sapi/fpm/fpm/fpm_env.c b/sapi/fpm/fpm/fpm_env.c index 7e70e52fd2637..72ed1aa94d383 100644 --- a/sapi/fpm/fpm/fpm_env.c +++ b/sapi/fpm/fpm/fpm_env.c @@ -60,7 +60,7 @@ int setenv(char *name, char *value, int overwrite) /* {{{ */ #endif #ifndef HAVE_CLEARENV -void clearenv() /* {{{ */ +void clearenv(void) { char **envp; char *s; @@ -79,7 +79,6 @@ void clearenv() /* {{{ */ } } -/* }}} */ #endif #ifndef HAVE_UNSETENV @@ -195,7 +194,7 @@ static int fpm_env_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */ } /* }}} */ -int fpm_env_init_main() /* {{{ */ +int fpm_env_init_main(void) { struct fpm_worker_pool_s *wp; char *title; @@ -273,4 +272,3 @@ int fpm_env_init_main() /* {{{ */ efree(title); return 0; } -/* }}} */ diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index dfbd5bd58c495..4cc56067bfc6c 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -104,6 +104,11 @@ static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg) /* {{ break; case '1' : /* SIGUSR1 */ zlog(ZLOG_DEBUG, "received SIGUSR1"); + + /* fpm_stdio_init_final tied STDERR fd with error_log fd. This affects logging to the + * access.log if it was configured to write to the stderr. Check #8885. */ + fpm_stdio_restore_original_stderr(0); + if (0 == fpm_stdio_open_error_log(1)) { zlog(ZLOG_NOTICE, "error log file re-opened"); } else { @@ -118,6 +123,9 @@ static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg) /* {{ } /* else no access log are set */ + /* We need to tie stderr with error_log in the master process after log files reload. Check #8885. */ + fpm_stdio_redirect_stderr_to_error_log(); + break; case '2' : /* SIGUSR2 */ zlog(ZLOG_DEBUG, "received SIGUSR2"); @@ -304,19 +312,17 @@ int fpm_event_pre_init(char *mechanism) /* {{{ */ } /* }}} */ -const char *fpm_event_mechanism_name() /* {{{ */ +const char *fpm_event_mechanism_name(void) { return module ? module->name : NULL; } -/* }}} */ -int fpm_event_support_edge_trigger() /* {{{ */ +int fpm_event_support_edge_trigger(void) { return module ? module->support_edge_trigger : 0; } -/* }}} */ -int fpm_event_init_main() /* {{{ */ +int fpm_event_init_main(void) { struct fpm_worker_pool_s *wp; int max; @@ -352,7 +358,6 @@ int fpm_event_init_main() /* {{{ */ } return 0; } -/* }}} */ void fpm_event_loop(int err) /* {{{ */ { diff --git a/sapi/fpm/fpm/fpm_events.h b/sapi/fpm/fpm/fpm_events.h index 8097477c827f3..5cc2b942ed25b 100644 --- a/sapi/fpm/fpm/fpm_events.h +++ b/sapi/fpm/fpm/fpm_events.h @@ -43,7 +43,7 @@ int fpm_event_init_main(void); int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg); int fpm_event_add(struct fpm_event_s *ev, unsigned long int timeout); int fpm_event_del(struct fpm_event_s *ev); -int fpm_event_pre_init(char *mechanism); +int fpm_event_pre_init(char *machanism); const char *fpm_event_mechanism_name(void); int fpm_event_support_edge_trigger(void); diff --git a/sapi/fpm/fpm/fpm_php.c b/sapi/fpm/fpm/fpm_php.c index 96648f44372db..92b189668206e 100644 --- a/sapi/fpm/fpm/fpm_php.c +++ b/sapi/fpm/fpm/fpm_php.c @@ -155,41 +155,35 @@ static int fpm_php_set_fcgi_mgmt_vars(struct fpm_worker_pool_s *wp) /* {{{ */ /* }}} */ #endif -char *fpm_php_script_filename(void) /* {{{ */ +char *fpm_php_script_filename(void) { return SG(request_info).path_translated; } -/* }}} */ -char *fpm_php_request_uri(void) /* {{{ */ +char *fpm_php_request_uri(void) { return (char *) SG(request_info).request_uri; } -/* }}} */ -char *fpm_php_request_method(void) /* {{{ */ +char *fpm_php_request_method(void) { return (char *) SG(request_info).request_method; } -/* }}} */ -char *fpm_php_query_string(void) /* {{{ */ +char *fpm_php_query_string(void) { return SG(request_info).query_string; } -/* }}} */ -char *fpm_php_auth_user(void) /* {{{ */ +char *fpm_php_auth_user(void) { return SG(request_info).auth_user; } -/* }}} */ -size_t fpm_php_content_length(void) /* {{{ */ +size_t fpm_php_content_length(void) { return SG(request_info).content_length; } -/* }}} */ static void fpm_php_cleanup(int which, void *arg) /* {{{ */ { @@ -201,20 +195,18 @@ static void fpm_php_cleanup(int which, void *arg) /* {{{ */ } /* }}} */ -void fpm_php_soft_quit() /* {{{ */ +void fpm_php_soft_quit(void) { fcgi_terminate(); } -/* }}} */ -int fpm_php_init_main() /* {{{ */ +int fpm_php_init_main(void) { if (0 > fpm_cleanup_add(FPM_CLEANUP_PARENT, fpm_php_cleanup, 0)) { return -1; } return 0; } -/* }}} */ int fpm_php_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ { diff --git a/sapi/fpm/fpm/fpm_process_ctl.c b/sapi/fpm/fpm/fpm_process_ctl.c index 97b96e5e3ea76..48eb0003d4918 100644 --- a/sapi/fpm/fpm/fpm_process_ctl.c +++ b/sapi/fpm/fpm/fpm_process_ctl.c @@ -18,6 +18,7 @@ #include "fpm_worker_pool.h" #include "fpm_scoreboard.h" #include "fpm_sockets.h" +#include "fpm_stdio.h" #include "zlog.h" @@ -63,7 +64,7 @@ static int fpm_pctl_timeout_set(int sec) /* {{{ */ } /* }}} */ -static void fpm_pctl_exit(void) /* {{{ */ +static void fpm_pctl_exit(void) { zlog(ZLOG_NOTICE, "exiting, bye-bye!"); @@ -71,11 +72,10 @@ static void fpm_pctl_exit(void) /* {{{ */ fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT_MAIN); exit(FPM_EXIT_OK); } -/* }}} */ #define optional_arg(c) (saved_argc > c ? ", \"" : ""), (saved_argc > c ? saved_argv[c] : ""), (saved_argc > c ? "\"" : "") -static void fpm_pctl_exec(void) /* {{{ */ +static void fpm_pctl_exec(void) { zlog(ZLOG_DEBUG, "Blocking some signals before reexec"); if (0 > fpm_signals_block()) { @@ -100,13 +100,15 @@ static void fpm_pctl_exec(void) /* {{{ */ ); fpm_cleanups_run(FPM_CLEANUP_PARENT_EXEC); + + fpm_stdio_restore_original_stderr(1); + execvp(saved_argv[0], saved_argv); zlog(ZLOG_SYSERROR, "failed to reload: execvp() failed"); exit(FPM_EXIT_SOFTWARE); } -/* }}} */ -static void fpm_pctl_action_last(void) /* {{{ */ +static void fpm_pctl_action_last(void) { switch (fpm_state) { case FPM_PCTL_STATE_RELOADING: @@ -119,7 +121,6 @@ static void fpm_pctl_action_last(void) /* {{{ */ break; } } -/* }}} */ int fpm_pctl_kill(pid_t pid, int how) /* {{{ */ { @@ -175,7 +176,7 @@ void fpm_pctl_kill_all(int signo) /* {{{ */ } /* }}} */ -static void fpm_pctl_action_next(void) /* {{{ */ +static void fpm_pctl_action_next(void) { int sig, timeout; @@ -203,7 +204,6 @@ static void fpm_pctl_action_next(void) /* {{{ */ fpm_signal_sent = sig; fpm_pctl_timeout_set(timeout); } -/* }}} */ void fpm_pctl(int new_state, int action) /* {{{ */ { @@ -250,13 +250,12 @@ void fpm_pctl(int new_state, int action) /* {{{ */ } /* }}} */ -int fpm_pctl_can_spawn_children() /* {{{ */ +int fpm_pctl_can_spawn_children(void) { return fpm_state == FPM_PCTL_STATE_NORMAL; } -/* }}} */ -int fpm_pctl_child_exited() /* {{{ */ +int fpm_pctl_child_exited(void) { if (fpm_state == FPM_PCTL_STATE_NORMAL) { return 0; @@ -267,9 +266,8 @@ int fpm_pctl_child_exited() /* {{{ */ } return 0; } -/* }}} */ -int fpm_pctl_init_main() /* {{{ */ +int fpm_pctl_init_main(void) { int i; @@ -295,7 +293,6 @@ int fpm_pctl_init_main() /* {{{ */ } return 0; } -/* }}} */ static void fpm_pctl_check_request_timeout(struct timeval *now) /* {{{ */ { diff --git a/sapi/fpm/fpm/fpm_request.c b/sapi/fpm/fpm/fpm_request.c index 4f51824138d4d..8d3e2b2dd9e77 100644 --- a/sapi/fpm/fpm/fpm_request.c +++ b/sapi/fpm/fpm/fpm_request.c @@ -34,7 +34,7 @@ const char *fpm_request_get_stage_name(int stage) { return requests_stages[stage]; } -void fpm_request_accepting() /* {{{ */ +void fpm_request_accepting(void) { struct fpm_scoreboard_proc_s *proc; struct timeval now; @@ -56,9 +56,8 @@ void fpm_request_accepting() /* {{{ */ /* idle++, active-- */ fpm_scoreboard_update_commit(1, -1, 0, 0, 0, 0, 0, FPM_SCOREBOARD_ACTION_INC, NULL); } -/* }}} */ -void fpm_request_reading_headers() /* {{{ */ +void fpm_request_reading_headers(void) { struct fpm_scoreboard_proc_s *proc; @@ -101,9 +100,8 @@ void fpm_request_reading_headers() /* {{{ */ /* idle--, active++, request++ */ fpm_scoreboard_update_commit(-1, 1, 0, 0, 1, 0, 0, FPM_SCOREBOARD_ACTION_INC, NULL); } -/* }}} */ -void fpm_request_info() /* {{{ */ +void fpm_request_info(void) { struct fpm_scoreboard_proc_s *proc; char *request_uri = fpm_php_request_uri(); @@ -151,9 +149,8 @@ void fpm_request_info() /* {{{ */ fpm_scoreboard_proc_release(proc); } -/* }}} */ -void fpm_request_executing() /* {{{ */ +void fpm_request_executing(void) { struct fpm_scoreboard_proc_s *proc; struct timeval now; @@ -170,9 +167,8 @@ void fpm_request_executing() /* {{{ */ proc->tv = now; fpm_scoreboard_proc_release(proc); } -/* }}} */ -void fpm_request_end(void) /* {{{ */ +void fpm_request_end(void) { struct fpm_scoreboard_proc_s *proc; struct timeval now; @@ -204,9 +200,8 @@ void fpm_request_end(void) /* {{{ */ proc->memory = memory; fpm_scoreboard_proc_release(proc); } -/* }}} */ -void fpm_request_finished() /* {{{ */ +void fpm_request_finished(void) { struct fpm_scoreboard_proc_s *proc; struct timeval now; @@ -223,7 +218,6 @@ void fpm_request_finished() /* {{{ */ proc->tv = now; fpm_scoreboard_proc_release(proc); } -/* }}} */ void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *now, int terminate_timeout, int slowlog_timeout, int track_finished) /* {{{ */ { diff --git a/sapi/fpm/fpm/fpm_scoreboard.c b/sapi/fpm/fpm/fpm_scoreboard.c index bf847d9dcac98..52d10a0416832 100644 --- a/sapi/fpm/fpm/fpm_scoreboard.c +++ b/sapi/fpm/fpm/fpm_scoreboard.c @@ -21,7 +21,7 @@ static float fpm_scoreboard_tick; #endif -int fpm_scoreboard_init_main() /* {{{ */ +int fpm_scoreboard_init_main(void) { struct fpm_worker_pool_s *wp; @@ -72,7 +72,6 @@ int fpm_scoreboard_init_main() /* {{{ */ } return 0; } -/* }}} */ static struct fpm_scoreboard_s *fpm_scoreboard_get_for_update(struct fpm_scoreboard_s *scoreboard) /* {{{ */ { @@ -186,11 +185,10 @@ void fpm_scoreboard_update( } /* }}} */ -struct fpm_scoreboard_s *fpm_scoreboard_get() /* {{{*/ +struct fpm_scoreboard_s *fpm_scoreboard_get(void) { return fpm_scoreboard; } -/* }}} */ static inline struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_ex( struct fpm_scoreboard_s *scoreboard, int child_index, unsigned int nprocs) /* {{{*/ @@ -442,9 +440,8 @@ int fpm_scoreboard_proc_alloc(struct fpm_child_s *child) /* {{{ */ /* }}} */ #ifdef HAVE_TIMES -float fpm_scoreboard_get_tick() /* {{{ */ +float fpm_scoreboard_get_tick(void) { return fpm_scoreboard_tick; } -/* }}} */ #endif diff --git a/sapi/fpm/fpm/fpm_shm.c b/sapi/fpm/fpm/fpm_shm.c index 035f2443e30f0..386fef8c49fa2 100644 --- a/sapi/fpm/fpm/fpm_shm.c +++ b/sapi/fpm/fpm/fpm_shm.c @@ -60,8 +60,7 @@ int fpm_shm_free(void *mem, size_t size) /* {{{ */ } /* }}} */ -size_t fpm_shm_get_size_allocated() /* {{{*/ +size_t fpm_shm_get_size_allocated(void) { return fpm_shm_size; } -/* }}} */ diff --git a/sapi/fpm/fpm/fpm_signals.c b/sapi/fpm/fpm/fpm_signals.c index f5a5ae5ec1743..6aad4403768cf 100644 --- a/sapi/fpm/fpm/fpm_signals.c +++ b/sapi/fpm/fpm/fpm_signals.c @@ -182,7 +182,7 @@ static void sig_handler(int signo) /* {{{ */ } /* }}} */ -int fpm_signals_init_main() /* {{{ */ +int fpm_signals_init_main(void) { struct sigaction act; @@ -222,9 +222,8 @@ int fpm_signals_init_main() /* {{{ */ } return 0; } -/* }}} */ -int fpm_signals_init_child() /* {{{ */ +int fpm_signals_init_child(void) { struct sigaction act, act_dfl; @@ -257,15 +256,13 @@ int fpm_signals_init_child() /* {{{ */ } return 0; } -/* }}} */ -int fpm_signals_get_fd() /* {{{ */ +int fpm_signals_get_fd(void) { return sp[0]; } -/* }}} */ -int fpm_signals_init_mask() /* {{{ */ +int fpm_signals_init_mask(void) { /* Subset of signals from fpm_signals_init_main() and fpm_got_signal() blocked to avoid unexpected death during early init @@ -298,9 +295,8 @@ int fpm_signals_init_mask() /* {{{ */ } return 0; } -/* }}} */ -int fpm_signals_block() /* {{{ */ +int fpm_signals_block(void) { if (0 > sigprocmask(SIG_BLOCK, &block_sigset, NULL)) { zlog(ZLOG_SYSERROR, "failed to block signals"); @@ -308,9 +304,8 @@ int fpm_signals_block() /* {{{ */ } return 0; } -/* }}} */ -int fpm_signals_child_block() /* {{{ */ +int fpm_signals_child_block(void) { if (0 > sigprocmask(SIG_BLOCK, &child_block_sigset, NULL)) { zlog(ZLOG_SYSERROR, "failed to block child signals"); @@ -318,9 +313,8 @@ int fpm_signals_child_block() /* {{{ */ } return 0; } -/* }}} */ -int fpm_signals_unblock() /* {{{ */ +int fpm_signals_unblock(void) { /* Ensure that during reload after upgrade all signals are unblocked. block_sigset could have different value before execve() */ @@ -332,4 +326,3 @@ int fpm_signals_unblock() /* {{{ */ } return 0; } -/* }}} */ diff --git a/sapi/fpm/fpm/fpm_sockets.c b/sapi/fpm/fpm/fpm_sockets.c index e46b49dcc3bd4..453c8400c55c1 100644 --- a/sapi/fpm/fpm/fpm_sockets.c +++ b/sapi/fpm/fpm/fpm_sockets.c @@ -386,7 +386,7 @@ static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp) /* } /* }}} */ -int fpm_sockets_init_main() /* {{{ */ +int fpm_sockets_init_main(void) { unsigned i, lq_len; struct fpm_worker_pool_s *wp; @@ -485,7 +485,6 @@ int fpm_sockets_init_main() /* {{{ */ } return 0; } -/* }}} */ #if HAVE_FPM_LQ diff --git a/sapi/fpm/fpm/fpm_stdio.c b/sapi/fpm/fpm/fpm_stdio.c index e920923b009bc..0b723108ae59b 100644 --- a/sapi/fpm/fpm/fpm_stdio.c +++ b/sapi/fpm/fpm/fpm_stdio.c @@ -20,10 +20,11 @@ #include "fpm_stdio.h" #include "zlog.h" +static int fd_stderr_original = -1; static int fd_stdout[2]; static int fd_stderr[2]; -int fpm_stdio_init_main() /* {{{ */ +int fpm_stdio_init_main(void) { int fd = open("/dev/null", O_RDWR); @@ -40,9 +41,8 @@ int fpm_stdio_init_main() /* {{{ */ close(fd); return 0; } -/* }}} */ -static inline int fpm_use_error_log(void) { /* {{{ */ +static inline int fpm_use_error_log(void) { /* * the error_log is NOT used when running in foreground * and from a tty (user looking at output). @@ -60,8 +60,54 @@ static inline int fpm_use_error_log(void) { /* {{{ */ return 0; } +int fpm_stdio_init_final(void) +{ + if (0 > fpm_stdio_redirect_stderr_to_error_log() || + 0 > fpm_stdio_redirect_stderr_to_dev_null_for_syslog()) { + + return -1; + } + + zlog_set_launched(); + return 0; +} /* }}} */ -int fpm_stdio_init_final() /* {{{ */ + +int fpm_stdio_save_original_stderr(void) +{ + /* php-fpm loses STDERR fd after call of the fpm_stdio_init_final(). Check #8555. */ + zlog(ZLOG_DEBUG, "saving original STDERR fd: dup()"); + fd_stderr_original = dup(STDERR_FILENO); + if (0 > fd_stderr_original) { + zlog(ZLOG_SYSERROR, "failed to save original STDERR fd, access.log records may appear in error_log: dup()"); + return -1; + } + + return 0; +} + +int fpm_stdio_restore_original_stderr(int close_after_restore) +{ + /* php-fpm loses STDERR fd after call of the fpm_stdio_init_final(). Check #8555. */ + if (-1 != fd_stderr_original) { + zlog(ZLOG_DEBUG, "restoring original STDERR fd: dup2()"); + if (0 > dup2(fd_stderr_original, STDERR_FILENO)) { + zlog(ZLOG_SYSERROR, "failed to restore original STDERR fd, access.log records may appear in error_log: dup2()"); + return -1; + } else { + if (close_after_restore) { + close(fd_stderr_original); + } + } + } else { + zlog(ZLOG_DEBUG, "original STDERR fd is not restored, maybe function is called from a child: dup2()"); + return -1; + } + + return 0; +} + +int fpm_stdio_redirect_stderr_to_error_log(void) { if (fpm_use_error_log()) { /* prevent duping if logging to syslog */ @@ -69,21 +115,28 @@ int fpm_stdio_init_final() /* {{{ */ /* there might be messages to stderr from other parts of the code, we need to log them all */ if (0 > dup2(fpm_globals.error_log_fd, STDERR_FILENO)) { - zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()"); + zlog(ZLOG_SYSERROR, "failed to tie stderr fd with error_log fd: dup2()"); return -1; } } + } + + return 0; +} + +int fpm_stdio_redirect_stderr_to_dev_null_for_syslog(void) +{ + if (fpm_use_error_log()) { #ifdef HAVE_SYSLOG_H - else if (fpm_globals.error_log_fd == ZLOG_SYSLOG) { + if (fpm_globals.error_log_fd == ZLOG_SYSLOG) { /* dup to /dev/null when using syslog */ dup2(STDOUT_FILENO, STDERR_FILENO); } #endif } - zlog_set_launched(); + return 0; } -/* }}} */ int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ { @@ -109,11 +162,10 @@ int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ #define FPM_STDIO_CMD_FLUSH "\0fscf" -int fpm_stdio_flush_child() /* {{{ */ +int fpm_stdio_flush_child(void) { return write(STDERR_FILENO, FPM_STDIO_CMD_FLUSH, sizeof(FPM_STDIO_CMD_FLUSH)); } -/* }}} */ static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) /* {{{ */ { @@ -336,10 +388,6 @@ int fpm_stdio_open_error_log(int reopen) /* {{{ */ } if (reopen) { - if (fpm_use_error_log()) { - dup2(fd, STDERR_FILENO); - } - dup2(fd, fpm_globals.error_log_fd); close(fd); fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */ diff --git a/sapi/fpm/fpm/fpm_stdio.h b/sapi/fpm/fpm/fpm_stdio.h index c0ff94d3cb609..49be50de562d9 100644 --- a/sapi/fpm/fpm/fpm_stdio.h +++ b/sapi/fpm/fpm/fpm_stdio.h @@ -16,5 +16,9 @@ void fpm_stdio_child_use_pipes(struct fpm_child_s *child); int fpm_stdio_parent_use_pipes(struct fpm_child_s *child); int fpm_stdio_discard_pipes(struct fpm_child_s *child); int fpm_stdio_open_error_log(int reopen); +int fpm_stdio_save_original_stderr(void); +int fpm_stdio_restore_original_stderr(int close_after_restore); +int fpm_stdio_redirect_stderr_to_dev_null_for_syslog(void); +int fpm_stdio_redirect_stderr_to_error_log(void); #endif diff --git a/sapi/fpm/fpm/fpm_systemd.c b/sapi/fpm/fpm/fpm_systemd.c index 31b6a3d27ae65..175312412330f 100644 --- a/sapi/fpm/fpm/fpm_systemd.c +++ b/sapi/fpm/fpm/fpm_systemd.c @@ -11,7 +11,7 @@ #include "fpm_systemd.h" -static void fpm_systemd() /* {{{ */ +static void fpm_systemd(void) { static unsigned long int last=0; struct fpm_worker_pool_s *wp; @@ -43,7 +43,6 @@ static void fpm_systemd() /* {{{ */ last = requests; } -/* }}} */ void fpm_systemd_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */ { @@ -79,7 +78,7 @@ void fpm_systemd_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{ } /* }}} */ -int fpm_systemd_conf() /* {{{ */ +int fpm_systemd_conf(void) { char *watchdog; int interval = 0; @@ -109,4 +108,3 @@ int fpm_systemd_conf() /* {{{ */ } return 0; } -/* }}} */ diff --git a/sapi/fpm/fpm/fpm_trace_mach.c b/sapi/fpm/fpm/fpm_trace_mach.c index 1ecee23d261e9..092858e287d46 100644 --- a/sapi/fpm/fpm/fpm_trace_mach.c +++ b/sapi/fpm/fpm/fpm_trace_mach.c @@ -18,7 +18,7 @@ static vm_offset_t target_page_base; static vm_offset_t local_page; static mach_msg_type_number_t local_size; -static void fpm_mach_vm_deallocate() /* {{{ */ +static void fpm_mach_vm_deallocate(void) { if (local_page) { mach_vm_deallocate(mach_task_self(), local_page, local_size); @@ -27,7 +27,6 @@ static void fpm_mach_vm_deallocate() /* {{{ */ local_size = 0; } } -/* }}} */ static int fpm_mach_vm_read_page(vm_offset_t page) /* {{{ */ { diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c index 55c3ada2bf508..ed5b66294fb0e 100644 --- a/sapi/fpm/fpm/fpm_unix.c +++ b/sapi/fpm/fpm/fpm_unix.c @@ -452,7 +452,7 @@ int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ } /* }}} */ -int fpm_unix_init_main() /* {{{ */ +int fpm_unix_init_main(void) { struct fpm_worker_pool_s *wp; int is_root = !geteuid(); @@ -589,4 +589,3 @@ int fpm_unix_init_main() /* {{{ */ return 0; } -/* }}} */ diff --git a/sapi/fpm/fpm/fpm_worker_pool.c b/sapi/fpm/fpm/fpm_worker_pool.c index 1d902d511366c..974238de296d4 100644 --- a/sapi/fpm/fpm/fpm_worker_pool.c +++ b/sapi/fpm/fpm/fpm_worker_pool.c @@ -62,7 +62,7 @@ static void fpm_worker_pool_cleanup(int which, void *arg) /* {{{ */ } /* }}} */ -struct fpm_worker_pool_s *fpm_worker_pool_alloc() /* {{{ */ +struct fpm_worker_pool_s *fpm_worker_pool_alloc(void) { struct fpm_worker_pool_s *ret; @@ -77,13 +77,11 @@ struct fpm_worker_pool_s *fpm_worker_pool_alloc() /* {{{ */ ret->log_fd = -1; return ret; } -/* }}} */ -int fpm_worker_pool_init_main() /* {{{ */ +int fpm_worker_pool_init_main(void) { if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_worker_pool_cleanup, 0)) { return -1; } return 0; } -/* }}} */ diff --git a/sapi/fpm/tests/bug77780-header-sent-error.phpt b/sapi/fpm/tests/bug77780-header-sent-error.phpt new file mode 100644 index 0000000000000..b5e76918547f1 --- /dev/null +++ b/sapi/fpm/tests/bug77780-header-sent-error.phpt @@ -0,0 +1,54 @@ +--TEST-- +FPM: bug77780 - Headers already sent error incorrectly emitted +--SKIPIF-- + +--EXTENSIONS-- +session +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester + ->request( + headers: [ + 'PHP_VALUE' => "session.cookie_secure=1", + ], + readLimit: 10, + expectError: true + ); +$tester->request( + headers: [ + 'PHP_VALUE' => "session.cookie_secure=1", + ] + ) + ->expectNoError(); +$tester->terminate(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/fcgi.inc b/sapi/fpm/tests/fcgi.inc index d17a659dd9c02..7d236c1b03a5e 100644 --- a/sapi/fpm/tests/fcgi.inc +++ b/sapi/fpm/tests/fcgi.inc @@ -26,6 +26,7 @@ namespace Adoy\FastCGI; class TimedOutException extends \Exception {} class ForbiddenException extends \Exception {} +class ReadLimitExceeded extends \Exception {} /** * Handles communication with a FastCGI application @@ -404,16 +405,24 @@ class Client /** * Read a FastCGI Packet * + * @param int $readLimit max content size * @return array + * @throws ReadLimitExceeded */ - private function readPacket() + private function readPacket($readLimit = -1) { if ($packet = fread($this->_sock, self::HEADER_LEN)) { $resp = $this->decodePacketHeader($packet); $resp['content'] = ''; if ($resp['contentLength']) { - $len = $resp['contentLength']; - while ($len && $buf=fread($this->_sock, $len)) { + $len = $resp['contentLength']; + if ($readLimit >= 0 && $len > $readLimit) { + // close connection so it can be re-set reset and throw an error + fclose($this->_sock); + $this->_sock = null; + throw new ReadLimitExceeded("Content has $len bytes but the limit is $readLimit bytes"); + } + while ($len && $buf = fread($this->_sock, $len)) { $len -= strlen($buf); $resp['content'] .= $buf; } @@ -473,15 +482,16 @@ class Client * * @param array $params Array of parameters * @param string $stdin Content + * @param int $readLimit [optional] the number of bytes to accept in a single packet or -1 if unlimited * @return array * @throws ForbiddenException * @throws TimedOutException * @throws \Exception */ - public function request_data(array $params, $stdin) + public function request_data(array $params, $stdin, $readLimit = -1) { $id = $this->async_request($params, $stdin); - return $this->wait_for_response_data($id); + return $this->wait_for_response_data($id, 0, $readLimit); } /** @@ -579,12 +589,13 @@ class Client * * @param int $requestId * @param int $timeoutMs [optional] the number of milliseconds to wait. + * @param int $readLimit [optional] the number of bytes to accept in a single packet or -1 if unlimited * @return array response data * @throws ForbiddenException * @throws TimedOutException * @throws \Exception */ - public function wait_for_response_data($requestId, $timeoutMs = 0) + public function wait_for_response_data($requestId, $timeoutMs = 0, $readLimit = -1) { if (!isset($this->_requests[$requestId])) { throw new \Exception('Invalid request id given'); @@ -608,7 +619,7 @@ class Client // but still not get the response requested $startTime = microtime(true); - while ($resp = $this->readPacket()) { + while ($resp = $this->readPacket($readLimit)) { if ($resp['type'] == self::STDOUT || $resp['type'] == self::STDERR) { if ($resp['type'] == self::STDERR) { $this->_requests[$resp['requestId']]['state'] = self::REQ_STATE_ERR; diff --git a/sapi/fpm/tests/gh8885-stderr-fd-reload-usr1.phpt b/sapi/fpm/tests/gh8885-stderr-fd-reload-usr1.phpt new file mode 100644 index 0000000000000..4ff8d0660936e --- /dev/null +++ b/sapi/fpm/tests/gh8885-stderr-fd-reload-usr1.phpt @@ -0,0 +1,92 @@ +--TEST-- +FPM: GH-8885 - access.log with stderr begins to write logs to error_log after daemon reload +--SKIPIF-- + +--FILE-- + true]); +// getPrefixedFile('err.log') is the same path that returns processTemplate('{{FILE:LOG}}') +$errorLogFile = $tester->getPrefixedFile('err.log'); + +$tester->start(); +$tester->expectNoLogMessages(); + +$content = file_get_contents($errorLogFile); +assert($content !== false && strlen($content) > 0, 'File must not be empty'); + +$errorLogLines = explode("\n", $content); +array_pop($errorLogLines); + +assert(count($errorLogLines) === 2, 'Expected 2 records in the error_log file'); +assert(strpos($errorLogLines[0], 'NOTICE: fpm is running, pid')); +assert(strpos($errorLogLines[1], 'NOTICE: ready to handle connections')); + +$tester->ping('{{ADDR}}'); +$stderrLines = $tester->getLogLines(-1); +assert(count($stderrLines) === 1, 'Expected 1 record in the stderr output (access.log)'); +$stderrLine = $stderrLines[0]; +assert(preg_match('/127.0.0.1 .* "GET \/ping" 200$/', $stderrLine), 'Incorrect format of access.log record'); + +$tester->signal('USR1'); +$tester->expectNoLogMessages(); + +$content = file_get_contents($errorLogFile); +assert($content !== false && strlen($content) > 0, 'File must not be empty'); +$errorLogLines = explode("\n", $content); +array_pop($errorLogLines); + +assert(count($errorLogLines) >= 4, 'Expected at least 4 records in the error_log file'); +assert(strpos($errorLogLines[0], 'NOTICE: fpm is running, pid')); +assert(strpos($errorLogLines[1], 'NOTICE: ready to handle connections')); +assert(strpos($errorLogLines[2], 'NOTICE: error log file re-opened')); +assert(strpos($errorLogLines[3], 'NOTICE: access log file re-opened')); + + +$tester->ping('{{ADDR}}'); +$stderrLines = $tester->getLogLines(-1); +assert(count($stderrLines) === 1, 'Must be only 1 record in the access.log'); +assert(preg_match('/127.0.0.1 .* "GET \/ping" 200$/', $stderrLines[0]), 'Incorrect format of access.log record'); + +$tester->terminate(); +$stderrLines = $tester->expectNoLogMessages(); + +$content = file_get_contents($errorLogFile); +assert($content !== false && strlen($content) > 0, 'File must not be empty'); +$errorLogLines = explode("\n", $content); +array_pop($errorLogLines); +$errorLogLastLine = array_pop($errorLogLines); +assert(strpos($errorLogLastLine, 'NOTICE: exiting, bye-bye')); + +$tester->close(); +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/gh8885-stderr-fd-reload-usr2.phpt b/sapi/fpm/tests/gh8885-stderr-fd-reload-usr2.phpt new file mode 100644 index 0000000000000..68d9f6fa68b64 --- /dev/null +++ b/sapi/fpm/tests/gh8885-stderr-fd-reload-usr2.phpt @@ -0,0 +1,92 @@ +--TEST-- +FPM: GH-8885 - access.log with stderr begins to write logs to error_log after daemon reload +--SKIPIF-- + +--FILE-- + true]); +// getPrefixedFile('err.log') is the same path that returns processTemplate('{{FILE:LOG}}') +$errorLogFile = $tester->getPrefixedFile('err.log'); + +$tester->start(); +$tester->expectNoLogMessages(); + +$content = file_get_contents($errorLogFile); +assert($content !== false && strlen($content) > 0, 'File must not be empty'); + +$errorLogLines = explode("\n", $content); +array_pop($errorLogLines); + +assert(count($errorLogLines) === 2, 'Expected 2 records in the error_log file'); +assert(strpos($errorLogLines[0], 'NOTICE: fpm is running, pid')); +assert(strpos($errorLogLines[1], 'NOTICE: ready to handle connections')); + +$tester->ping('{{ADDR}}'); +$stderrLines = $tester->getLogLines(-1); +assert(count($stderrLines) === 1, 'Expected 1 record in the stderr output (access.log)'); +$stderrLine = $stderrLines[0]; +assert(preg_match('/127.0.0.1 .* "GET \/ping" 200$/', $stderrLine), 'Incorrect format of access.log record'); + +$tester->signal('USR2'); +$tester->expectLogNotice('using inherited socket fd=\d+, "127.0.0.1:\d+"'); + +$content = file_get_contents($errorLogFile); +assert($content !== false && strlen($content) > 0, 'File must not be empty'); +$errorLogLines = explode("\n", $content); +array_pop($errorLogLines); + +assert(count($errorLogLines) >= 5, 'Expected at least 5 records in the error_log file'); +assert(strpos($errorLogLines[0], 'NOTICE: fpm is running, pid')); +assert(strpos($errorLogLines[1], 'NOTICE: ready to handle connections')); +assert(strpos($errorLogLines[2], 'NOTICE: Reloading in progress')); +assert(strpos($errorLogLines[3], 'NOTICE: reloading: execvp')); +assert(strpos($errorLogLines[4], 'NOTICE: using inherited socket')); + +$tester->ping('{{ADDR}}'); +$stderrLines = $tester->getLogLines(-1); +assert(count($stderrLines) === 1, 'Must be only 1 record in the access.log'); +assert(preg_match('/127.0.0.1 .* "GET \/ping" 200$/', $stderrLines[0]), 'Incorrect format of access.log record'); + +$tester->terminate(); +$stderrLines = $tester->expectNoLogMessages(); + +$content = file_get_contents($errorLogFile); +assert($content !== false && strlen($content) > 0, 'File must not be empty'); +$errorLogLines = explode("\n", $content); +array_pop($errorLogLines); +$errorLogLastLine = array_pop($errorLogLines); +assert(strpos($errorLogLastLine, 'NOTICE: exiting, bye-bye')); + +$tester->close(); +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/response.inc b/sapi/fpm/tests/response.inc index b531feacdca7e..1d0783b66ec35 100644 --- a/sapi/fpm/tests/response.inc +++ b/sapi/fpm/tests/response.inc @@ -111,21 +111,31 @@ class Response } /** - * @param string $errorMessage + * @param string|null $errorMessage * @return Response */ public function expectError($errorMessage) { $errorData = $this->getErrorData(); if ($errorData !== $errorMessage) { - $this->error( - "The expected error message '$errorMessage' is not equal to returned error '$errorData'" - ); + $expectedErrorMessage = $errorMessage !== null + ? "The expected error message '$errorMessage' is not equal to returned error '$errorData'" + : "No error message expected but received '$errorData'"; + $this->error($expectedErrorMessage); } return $this; } + /** + * @param string $errorMessage + * @return Response + */ + public function expectNoError() + { + return $this->expectError(null); + } + /** * @param string $contentType * @return string|null diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc index e607035df1b31..8eaed315779fb 100644 --- a/sapi/fpm/tests/tester.inc +++ b/sapi/fpm/tests/tester.inc @@ -35,6 +35,11 @@ class Tester */ const FILE_EXT_PID = 'pid'; + /** + * Name for the option to manage php-fpm --force-stderr flag + */ + const PHP_FPM_DISABLE_FORCE_STDERR = 'disable-force-stderr'; + /** * @var array */ @@ -365,7 +370,15 @@ class Tester { $configFile = $this->createConfig(); $desc = $this->outDesc ? [] : [1 => array('pipe', 'w'), 2 => array('redirect', 1)]; - $cmd = [self::findExecutable(), '-F', '-O', '-y', $configFile]; + + $cmd = [self::findExecutable(), '-F', '-y', $configFile]; + + if (!(isset($this->options[self::PHP_FPM_DISABLE_FORCE_STDERR]) && + $this->options[self::PHP_FPM_DISABLE_FORCE_STDERR] === true) + ) { + $cmd[] = '-O'; + } + if (getenv('TEST_FPM_RUN_AS_ROOT')) { $cmd[] = '--allow-to-run-as-root'; } @@ -579,6 +592,8 @@ class Tester * @param string|null $errorMessage * @param bool $connKeepAlive * @param string|null $scriptFilename = null + * @param bool $expectError + * @param int $readLimit * @return Response */ public function request( @@ -589,7 +604,9 @@ class Tester string $successMessage = null, string $errorMessage = null, bool $connKeepAlive = false, - string $scriptFilename = null + string $scriptFilename = null, + bool $expectError = false, + int $readLimit = -1, ) { if ($this->hasError()) { return new Response(null, true); @@ -599,11 +616,17 @@ class Tester try { $this->response = new Response( - $this->getClient($address, $connKeepAlive)->request_data($params, false) + $this->getClient($address, $connKeepAlive)->request_data($params, false, $readLimit) ); - $this->message($successMessage); + if ($expectError) { + $this->error('Expected request error but the request was successful'); + } else { + $this->message($successMessage); + } } catch (\Exception $exception) { - if ($errorMessage === null) { + if ($expectError) { + $this->message($successMessage); + } elseif ($errorMessage === null) { $this->error("Request failed", $exception); } else { $this->message($errorMessage); @@ -1138,7 +1161,7 @@ class Tester * @param string $prefix * @return string */ - private function getPrefixedFile(string $extension, string $prefix = null) + public function getPrefixedFile(string $extension, string $prefix = null) { $fileName = rtrim($this->fileName, '.'); if (!is_null($prefix)) {