From a757f276f95e20c1bd0e5102e6af9cddcdaf0149 Mon Sep 17 00:00:00 2001 From: George Wang Date: Sat, 6 Sep 2025 12:38:13 -0400 Subject: [PATCH 01/83] fix pipe detection for STDERR. --- sapi/litespeed/lsapilib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c index 09cd2e6bf9a76..2ab314d7d73f3 100644 --- a/sapi/litespeed/lsapilib.c +++ b/sapi/litespeed/lsapilib.c @@ -634,8 +634,8 @@ static inline int isPipe( int fd ) { char achPeer[128]; socklen_t len = 128; - if (( getpeername( fd, (struct sockaddr *)achPeer, &len ) != 0 )&& - ( errno == ENOTCONN )) + if ((getpeername(fd, (struct sockaddr *)achPeer, &len) != 0 ) + && (errno == ENOTCONN || errno == ENOTSOCK)) return 0; else return 1; From 828080146b648c7474ab6a6f585739aa3ac7ef8c Mon Sep 17 00:00:00 2001 From: George Wang Date: Sat, 6 Sep 2025 12:38:13 -0400 Subject: [PATCH 02/83] fix pipe detection for STDERR. --- sapi/litespeed/lsapilib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c index fc2882d898d3f..6b63053cced65 100644 --- a/sapi/litespeed/lsapilib.c +++ b/sapi/litespeed/lsapilib.c @@ -634,8 +634,8 @@ static inline int isPipe( int fd ) { char achPeer[128]; socklen_t len = 128; - if (( getpeername( fd, (struct sockaddr *)achPeer, &len ) != 0 )&& - ( errno == ENOTCONN )) + if ((getpeername(fd, (struct sockaddr *)achPeer, &len) != 0 ) + && (errno == ENOTCONN || errno == ENOTSOCK)) return 0; else return 1; From d2fa1ca60021a5cf328f91022531ef29472c0d59 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Tue, 9 Sep 2025 16:19:05 +0200 Subject: [PATCH 03/83] Mark test as ONLINE (#19776) --- sapi/cli/tests/php_cli_server_ipv4_error_message.phpt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sapi/cli/tests/php_cli_server_ipv4_error_message.phpt b/sapi/cli/tests/php_cli_server_ipv4_error_message.phpt index 71280179cccfc..d00dd3ec9278f 100644 --- a/sapi/cli/tests/php_cli_server_ipv4_error_message.phpt +++ b/sapi/cli/tests/php_cli_server_ipv4_error_message.phpt @@ -2,6 +2,9 @@ IPv4 address error message formatting --SKIPIF-- Date: Tue, 9 Sep 2025 11:37:59 -0300 Subject: [PATCH 04/83] PHP 8.4 is now for PHP 8.4.14-dev --- NEWS | 6 +++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index a6a5c63891cc9..91780c255fd2a 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.4.13 +?? ??? ????, PHP 8.4.14 + + + +25 Sep 2025, PHP 8.4.13 - Core: . Fixed bug GH-18850 (Repeated inclusion of file with __halt_compiler() diff --git a/Zend/zend.h b/Zend/zend.h index 1cf82f437d301..1c6d37b7f5044 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.13-dev" +#define ZEND_VERSION "4.4.14-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index d2250cae43331..30bbdf22ff3c6 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.4.13-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.14-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index dfc15c4c33bc8..5c0e7705b9e10 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 4 -#define PHP_RELEASE_VERSION 13 +#define PHP_RELEASE_VERSION 14 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.13-dev" -#define PHP_VERSION_ID 80413 +#define PHP_VERSION "8.4.14-dev" +#define PHP_VERSION_ID 80414 From 215ebbb8d5c44ae86dc1c21097cee77dc9491320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 9 Sep 2025 19:33:45 +0200 Subject: [PATCH 05/83] zend_API: Do not overwrite `readonly` properties in `object_properties_load()` (#19767) Fixes php/php-src#19765. --- NEWS | 2 ++ Zend/zend_API.c | 8 +++++++ .../03_randomizer/gh_19765_unserialize.phpt | 21 +++++++++++++++++++ .../03_randomizer/gh_9186_unserialize.phpt | 2 +- 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 ext/random/tests/03_randomizer/gh_19765_unserialize.phpt diff --git a/NEWS b/NEWS index b006f93e0c32c..b11f00ad7d162 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,8 @@ PHP NEWS . Fixed bug GH-19681 (PHP_EXPAND_PATH broken with bash 5.3.0). (Remi) . Fixed bug GH-19720 (Assertion failure when error handler throws when accessing a deprecated constant). (nielsdos) + . Fixed bug GH-19765 (object_properties_load() bypasses readonly property + checks). (timwolla) - CLI: . Fixed bug GH-19461 (Improve error message on listening error with IPv6 diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 79800a61a0476..ed6b24bc55216 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1701,6 +1701,14 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) property_info && (property_info->flags & ZEND_ACC_STATIC) == 0) { zval *slot = OBJ_PROP(object, property_info->offset); + if (UNEXPECTED((property_info->flags & ZEND_ACC_READONLY) && !Z_ISUNDEF_P(slot))) { + if (Z_PROP_FLAG_P(slot) & IS_PROP_REINITABLE) { + Z_PROP_FLAG_P(slot) &= ~IS_PROP_REINITABLE; + } else { + zend_readonly_property_modification_error(property_info); + return; + } + } zval_ptr_dtor(slot); ZVAL_COPY_VALUE(slot, prop); zval_add_ref(slot); diff --git a/ext/random/tests/03_randomizer/gh_19765_unserialize.phpt b/ext/random/tests/03_randomizer/gh_19765_unserialize.phpt new file mode 100644 index 0000000000000..24abfca293fc5 --- /dev/null +++ b/ext/random/tests/03_randomizer/gh_19765_unserialize.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-19765: object_properties_load() bypasses readonly property checks +--FILE-- +__unserialize([['engine' => new PcgOneseq128XslRr64()]]); +} catch (Exception $error) { + echo $error->getMessage() . "\n"; +} +var_dump($r->engine::class); + +?> +--EXPECT-- +Invalid serialization data for Random\Randomizer object +string(21) "Random\Engine\Mt19937" diff --git a/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt b/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt index fe1acb2f74be4..edadb7380bd1f 100644 --- a/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt +++ b/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt @@ -1,5 +1,5 @@ --TEST-- -Fix GH-9186 @strict-properties can be bypassed using unserialization +GH-9186: @strict-properties can be bypassed using unserialization --FILE-- Date: Tue, 9 Sep 2025 21:57:54 +0200 Subject: [PATCH 06/83] Revert "fix pipe detection for STDERR." This reverts commit a757f276f95e20c1bd0e5102e6af9cddcdaf0149. --- sapi/litespeed/lsapilib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c index 2ab314d7d73f3..09cd2e6bf9a76 100644 --- a/sapi/litespeed/lsapilib.c +++ b/sapi/litespeed/lsapilib.c @@ -634,8 +634,8 @@ static inline int isPipe( int fd ) { char achPeer[128]; socklen_t len = 128; - if ((getpeername(fd, (struct sockaddr *)achPeer, &len) != 0 ) - && (errno == ENOTCONN || errno == ENOTSOCK)) + if (( getpeername( fd, (struct sockaddr *)achPeer, &len ) != 0 )&& + ( errno == ENOTCONN )) return 0; else return 1; From c9cc68b9edaac690b2c337dccd8ee865118296cd Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 9 Sep 2025 23:02:24 +0200 Subject: [PATCH 07/83] PHP-8.3 is now for PHP-8.3.27-dev --- NEWS | 10 +++++++--- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index b11f00ad7d162..b035fa9999956 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,12 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.26 +?? ??? ????, PHP 8.3.27 + +- Core: + . Fixed bug GH-19765 (object_properties_load() bypasses readonly property + checks). (timwolla) + +25 Sep 2025, PHP 8.3.26 - Core: . Fixed bug GH-18850 (Repeated inclusion of file with __halt_compiler() @@ -14,8 +20,6 @@ PHP NEWS . Fixed bug GH-19681 (PHP_EXPAND_PATH broken with bash 5.3.0). (Remi) . Fixed bug GH-19720 (Assertion failure when error handler throws when accessing a deprecated constant). (nielsdos) - . Fixed bug GH-19765 (object_properties_load() bypasses readonly property - checks). (timwolla) - CLI: . Fixed bug GH-19461 (Improve error message on listening error with IPv6 diff --git a/Zend/zend.h b/Zend/zend.h index 23df63cde2d4a..f0925298413ff 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.26-dev" +#define ZEND_VERSION "4.3.27-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 0c9bf96e64b9a..1e39e16644b1f 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.3.26-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.27-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 866ca3a80a072..bf1b95c631936 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 3 -#define PHP_RELEASE_VERSION 26 +#define PHP_RELEASE_VERSION 27 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.26-dev" -#define PHP_VERSION_ID 80326 +#define PHP_VERSION "8.3.27-dev" +#define PHP_VERSION_ID 80327 From 2ff7a18bdc8e1ce184c8df312fe87a178f775761 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 10 Sep 2025 17:08:17 +0300 Subject: [PATCH 08/83] Update IR IR commit: 2283f5eedf2a238b4d819c1774e47f3721b80cd8 --- ext/opcache/jit/ir/ir.c | 2 +- ext/opcache/jit/ir/ir_aarch64.dasc | 111 +++++++++++++++++++++++------ ext/opcache/jit/ir/ir_aarch64.h | 4 +- ext/opcache/jit/ir/ir_x86.dasc | 8 ++- ext/opcache/jit/ir/ir_x86.h | 4 +- 5 files changed, 99 insertions(+), 30 deletions(-) diff --git a/ext/opcache/jit/ir/ir.c b/ext/opcache/jit/ir/ir.c index 97d32680e4fdd..9d1b698761fbb 100644 --- a/ext/opcache/jit/ir/ir.c +++ b/ext/opcache/jit/ir/ir.c @@ -624,7 +624,7 @@ ir_ref ir_const_bool(ir_ctx *ctx, bool c) ir_ref ir_const_char(ir_ctx *ctx, char c) { ir_val val; - val.i64 = c; + val.i64 = (signed char)c; return ir_const(ctx, val, IR_CHAR); } diff --git a/ext/opcache/jit/ir/ir_aarch64.dasc b/ext/opcache/jit/ir/ir_aarch64.dasc index 476529555e93e..3a2c3687042cd 100644 --- a/ext/opcache/jit/ir/ir_aarch64.dasc +++ b/ext/opcache/jit/ir/ir_aarch64.dasc @@ -397,6 +397,15 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co n++; } break; + case IR_INT2FP: + case IR_FP2INT: + insn = &ctx->ir_base[ref]; + n = 0; + if (IR_IS_CONST_REF(insn->op1)) { + constraints->tmp_regs[n] = IR_TMP_REG(1, ctx->ir_base[insn->op1].type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); + n++; + } + break; case IR_MUL_PWR2: case IR_DIV_PWR2: case IR_MOD_PWR2: @@ -404,8 +413,6 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co case IR_SHIFT_CONST: case IR_OP_INT: case IR_OP_FP: - case IR_INT2FP: - case IR_FP2INT: case IR_FP2FP: insn = &ctx->ir_base[ref]; n = 0; @@ -1398,7 +1405,7 @@ static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) | add Rx(reg), Rx(base), #offset } else { ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); - | add sp, sp, Rx(IR_REG_INT_TMP) + | add Rx(reg), sp, Rx(IR_REG_INT_TMP) } } @@ -1587,19 +1594,31 @@ static void ir_emit_prologue(ir_ctx *ctx) offset = -(ctx->stack_frame_size+16); if (aarch64_may_encode_imm7_addr_offset(offset, 8)) { | stp x29, x30, [sp, #offset]! - } else { + } else if (aarch64_may_encode_imm12(ctx->stack_frame_size+16)) { | sub sp, sp, #(ctx->stack_frame_size+16) | stp x29, x30, [sp] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->stack_frame_size+16); + | sub sp, sp, Rx(IR_REG_INT_TMP) + | stp x29, x30, [sp] } | mov x29, sp if (ctx->call_stack_size) { - | sub sp, sp, #(ctx->call_stack_size) + if (aarch64_may_encode_imm12(ctx->call_stack_size)) { + | sub sp, sp, #(ctx->call_stack_size) + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->call_stack_size); + | sub sp, sp, Rx(IR_REG_INT_TMP) + } } } else if (ctx->stack_frame_size + ctx->call_stack_size) { if (ctx->fixed_stack_red_zone) { IR_ASSERT(ctx->stack_frame_size + ctx->call_stack_size <= ctx->fixed_stack_red_zone); + } else if (aarch64_may_encode_imm12(ctx->stack_frame_size + ctx->call_stack_size)) { + | sub sp, sp, #(ctx->stack_frame_size + ctx->call_stack_size) } else { - | sub sp, sp, #(ctx->stack_frame_size + ctx->call_stack_size) + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->stack_frame_size + ctx->call_stack_size); + | sub sp, sp, Rx(IR_REG_INT_TMP) } } if (ctx->used_preserved_regs) { @@ -1623,26 +1642,41 @@ static void ir_emit_prologue(ir_ctx *ctx) offset -= sizeof(void*) * 2; if (aarch64_may_encode_imm7_addr_offset(offset, 8)) { | stp Rx(prev), Rx(i), [Rx(fp), #offset] - } else { - IR_ASSERT(aarch64_may_encode_addr_offset(offset, 8)); + } else if (aarch64_may_encode_addr_offset(offset + 8, 8)) { | str Rx(prev), [Rx(fp), #offset] | str Rx(i), [Rx(fp), #(offset+8)] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + | add Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 + | str Rx(i), [Rx(fp), Rx(IR_REG_INT_TMP)] } prev = IR_REG_NONE; } else { if (prev < IR_REG_FP_FIRST) { offset -= sizeof(void*); - | str Rx(prev), [Rx(fp), #offset] - offset -= sizeof(void*); - | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | str Rx(prev), [Rx(fp), #offset] + offset -= sizeof(void*); + | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + | sub Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 + | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } else { offset -= sizeof(void*) * 2; if (aarch64_may_encode_imm7_addr_offset(offset, 8)) { | stp Rd(prev-IR_REG_FP_FIRST), Rd(i-IR_REG_FP_FIRST), [Rx(fp), #offset] - } else { - IR_ASSERT(aarch64_may_encode_addr_offset(offset, 8)); + } else if (aarch64_may_encode_addr_offset(offset + 8, 8)) { | str Rd(prev-IR_REG_FP_FIRST), [Rx(fp), #offset] | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), #(offset+8)] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rd(prev-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] + | add Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 + | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] } } prev = IR_REG_NONE; @@ -1652,10 +1686,20 @@ static void ir_emit_prologue(ir_ctx *ctx) if (prev != IR_REG_NONE) { if (prev < IR_REG_FP_FIRST) { offset -= sizeof(void*); - | str Rx(prev), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | str Rx(prev), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } else { offset -= sizeof(void*); - | str Rd(prev-IR_REG_FP_FIRST), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | str Rd(prev-IR_REG_FP_FIRST), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rd(prev-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } } } @@ -1685,10 +1729,14 @@ static void ir_emit_prologue(ir_ctx *ctx) if (prev != IR_REG_NONE) { if (aarch64_may_encode_imm7_addr_offset(offset, 8)) { | stp Rx(prev), Rx(int_reg_params[i]), [Rx(fp), #offset] - } else { - IR_ASSERT(aarch64_may_encode_addr_offset(offset, 8)); + } else if (aarch64_may_encode_addr_offset(offset + 8, 8)) { | str Rx(prev), [Rx(fp), #offset] | str Rx(int_reg_params[i]), [Rx(fp), #(offset+8)] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + | add Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 + | str Rx(int_reg_params[i]), [Rx(fp), Rx(IR_REG_INT_TMP)] } prev = IR_REG_NONE; offset += sizeof(void*) * 2; @@ -1697,7 +1745,12 @@ static void ir_emit_prologue(ir_ctx *ctx) } } if (prev != IR_REG_NONE) { - | str Rx(prev), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset + 8, 8)) { + | str Rx(prev), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + } offset += sizeof(void*); } } @@ -1782,15 +1835,22 @@ static void ir_emit_epilogue(ir_ctx *ctx) } if (aarch64_may_encode_imm7_addr_offset(ctx->stack_frame_size+16, 8)) { | ldp x29, x30, [sp], #(ctx->stack_frame_size+16) - } else { + } else if (aarch64_may_encode_imm12(ctx->stack_frame_size+16)) { | ldp x29, x30, [sp] | add sp, sp, #(ctx->stack_frame_size+16) + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->stack_frame_size+16); + | ldp x29, x30, [sp] + | add sp, sp, Rx(IR_REG_INT_TMP) } } else if (ctx->stack_frame_size + ctx->call_stack_size) { if (ctx->fixed_stack_red_zone) { IR_ASSERT(ctx->stack_frame_size + ctx->call_stack_size <= ctx->fixed_stack_red_zone); - } else { + } else if (aarch64_may_encode_imm12(ctx->stack_frame_size + ctx->call_stack_size)) { | add sp, sp, #(ctx->stack_frame_size + ctx->call_stack_size) + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->stack_frame_size + ctx->call_stack_size); + | add sp, sp, Rx(IR_REG_INT_TMP) } } } @@ -4808,7 +4868,9 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg fp_param++; } if (dst_reg != IR_REG_NONE) { - if (src_reg == IR_REG_NONE) { + if (IR_IS_CONST_REF(arg) || + src_reg == IR_REG_NONE || + (IR_REG_SPILLED(src_reg) && !IR_REGSET_IN(IR_REGSET_PRESERVED, IR_REG_NUM(src_reg)))) { /* delay CONST->REG and MEM->REG moves to third pass */ do_pass3 = 1; } else { @@ -4874,7 +4936,9 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg fp_param++; } if (dst_reg != IR_REG_NONE) { - if (src_reg == IR_REG_NONE) { + if (IR_IS_CONST_REF(arg) || + src_reg == IR_REG_NONE || + (IR_REG_SPILLED(src_reg) && !IR_REGSET_IN(IR_REGSET_PRESERVED, IR_REG_NUM(src_reg)))) { if (IR_IS_CONST_REF(arg) && IR_IS_TYPE_INT(type)) { if (ir_type_size[type] == 1) { type = IR_ADDR; @@ -5473,7 +5537,8 @@ static void ir_emit_load_params(ir_ctx *ctx) int32_t stack_offset = 0; if (ctx->flags & IR_USE_FRAME_POINTER) { - stack_offset = sizeof(void*) * 2; /* skip old frame pointer and return address */ + /* skip old frame pointer and return address */ + stack_offset = sizeof(void*) * 2 + ctx->stack_frame_size + ctx->call_stack_size; } else { stack_offset = ctx->stack_frame_size + ctx->call_stack_size; } diff --git a/ext/opcache/jit/ir/ir_aarch64.h b/ext/opcache/jit/ir/ir_aarch64.h index 183d3ec2e1d88..9da64b9249f72 100644 --- a/ext/opcache/jit/ir/ir_aarch64.h +++ b/ext/opcache/jit/ir/ir_aarch64.h @@ -174,8 +174,8 @@ typedef struct _ir_tmp_reg { int8_t reg; }; uint8_t type; - uint8_t start; - uint8_t end; + int8_t start; + int8_t end; } ir_tmp_reg; struct _ir_target_constraints { diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index d9967d24dbea2..e5c038fce8e5a 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -9000,7 +9000,9 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg #endif } if (dst_reg != IR_REG_NONE) { - if (src_reg == IR_REG_NONE) { + if (IR_IS_CONST_REF(arg) || + src_reg == IR_REG_NONE || + (IR_REG_SPILLED(src_reg) && !IR_REGSET_IN(IR_REGSET_PRESERVED, IR_REG_NUM(src_reg)))) { /* delay CONST->REG and MEM->REG moves to third pass */ do_pass3 = 1; } else { @@ -9068,7 +9070,9 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg #endif } if (dst_reg != IR_REG_NONE) { - if (src_reg == IR_REG_NONE) { + if (IR_IS_CONST_REF(arg) || + src_reg == IR_REG_NONE || + (IR_REG_SPILLED(src_reg) && !IR_REGSET_IN(IR_REGSET_PRESERVED, IR_REG_NUM(src_reg)))) { if (IR_IS_TYPE_INT(type)) { if (IR_IS_CONST_REF(arg)) { if (type == IR_I8 || type == IR_I16) { diff --git a/ext/opcache/jit/ir/ir_x86.h b/ext/opcache/jit/ir/ir_x86.h index 4b86c291bdfc6..06bfa951cf21d 100644 --- a/ext/opcache/jit/ir/ir_x86.h +++ b/ext/opcache/jit/ir/ir_x86.h @@ -218,8 +218,8 @@ typedef struct _ir_tmp_reg { int8_t reg; }; uint8_t type; - uint8_t start; - uint8_t end; + int8_t start; + int8_t end; } ir_tmp_reg; struct _ir_target_constraints { From f6f17484abb592b20297ef26e18807da85af7d4d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 23 Sep 2023 20:53:07 +0200 Subject: [PATCH 09/83] Fix GH-12265: Cloning an object breaks serialization recursion Closes GH-12287. --- NEWS | 4 ++ ext/standard/tests/serialize/gh12265.phpt | 47 +++++++++++++++++++++ ext/standard/tests/serialize/gh12265b.phpt | 48 ++++++++++++++++++++++ ext/standard/var.c | 5 ++- 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/serialize/gh12265.phpt create mode 100644 ext/standard/tests/serialize/gh12265b.phpt diff --git a/NEWS b/NEWS index b035fa9999956..b50dd0802a7d9 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,10 @@ PHP NEWS . Fixed bug GH-19765 (object_properties_load() bypasses readonly property checks). (timwolla) +- Standard: + . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). + (nielsdos) + 25 Sep 2025, PHP 8.3.26 - Core: diff --git a/ext/standard/tests/serialize/gh12265.phpt b/ext/standard/tests/serialize/gh12265.phpt new file mode 100644 index 0000000000000..5f49a62fee29f --- /dev/null +++ b/ext/standard/tests/serialize/gh12265.phpt @@ -0,0 +1,47 @@ +--TEST-- +GH-12265 (Cloning an object breaks serialization recursion) - __serialize variation +--FILE-- + new A($this)]; + } +} + +class C { + public B $b; + + public function __construct() { + $this->b = new B; + } +} + +$b = new B(); +$sb = serialize($b); +$stb = serialize(new B); + +printf("serialized original: %s\n", $sb); +printf("serialized temp : %s\n", $stb); + +$c = new C; +$sc = serialize($c); +$stc = serialize(new C); + +printf("serialized original: %s\n", $sc); +printf("serialized temp : %s\n", $stc); + +?> +--EXPECT-- +serialized original: O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +serialized temp : O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +serialized original: O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}} +serialized temp : O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}} diff --git a/ext/standard/tests/serialize/gh12265b.phpt b/ext/standard/tests/serialize/gh12265b.phpt new file mode 100644 index 0000000000000..2b9d80a848cee --- /dev/null +++ b/ext/standard/tests/serialize/gh12265b.phpt @@ -0,0 +1,48 @@ +--TEST-- +GH-12265 (Cloning an object breaks serialization recursion) - __sleep variation +--FILE-- +a = new A($this); + return ['a']; + } +} + +class C { + public B $b; + + public function __construct() { + $this->b = new B; + } +} + +$b = new B(); +$sb = serialize($b); +$stb = serialize(new B); + +printf("serialized original: %s\n", $sb); +printf("serialized temp : %s\n", $stb); + +$c = new C; +$sc = serialize($c); +$stc = serialize(new C); + +printf("serialized original: %s\n", $sc); +printf("serialized temp : %s\n", $stc); + +?> +--EXPECT-- +serialized original: O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +serialized temp : O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +serialized original: O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}} +serialized temp : O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}} diff --git a/ext/standard/var.c b/ext/standard/var.c index 3d53ebf5137a4..b7dc6dff1d884 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -681,7 +681,10 @@ static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var, b return 0; } else if (!in_rcn_array && Z_REFCOUNT_P(var) == 1 - && (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1)) { + && (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1) + /* __serialize and __sleep may arbitrarily increase the refcount */ + && Z_OBJCE_P(var)->__serialize == NULL + && zend_hash_find_known_hash(&Z_OBJCE_P(var)->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP)) == NULL) { return 0; } From a76d01d48e325a39dd422442f5ef54137dd29b95 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:59:46 +0200 Subject: [PATCH 10/83] [ci skip] Add NEWS item for 9b71c616 --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 1175e5c8e971c..1ad06e570d9bb 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.14 +- Core: + . Fixed bug GH-19765 (object_properties_load() bypasses readonly property + checks). (timwolla) + - Standard: . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). (nielsdos) From 901f71e6e3a9c97928a8c32ab7e70bd52e93819c Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 3 Sep 2025 22:45:24 +0200 Subject: [PATCH 11/83] Fix GH-19688: Remove pattern overflow in zip addGlob() memcmp() can overread the filename if the filename is shorter than the pattern. Closes GH-19689. --- NEWS | 3 +++ ext/zip/php_zip.c | 2 +- ext/zip/tests/gh19688.phpt | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 ext/zip/tests/gh19688.phpt diff --git a/NEWS b/NEWS index b50dd0802a7d9..8ed895987f9cc 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,9 @@ PHP NEWS . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). (nielsdos) +- Zip: + . Fixed bug GH-19688 (Remove pattern overflow in zip addGlob()). (nielsdos) + 25 Sep 2025, PHP 8.3.26 - Core: diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 9f1d296336e09..3613fb0f7ca7a 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -1784,7 +1784,7 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* basename = php_basename(Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), NULL, 0); file_stripped = ZSTR_VAL(basename); file_stripped_len = ZSTR_LEN(basename); - } else if (opts.remove_path && !memcmp(Z_STRVAL_P(zval_file), opts.remove_path, opts.remove_path_len)) { + } else if (opts.remove_path && Z_STRLEN_P(zval_file) > opts.remove_path_len && !memcmp(Z_STRVAL_P(zval_file), opts.remove_path, opts.remove_path_len)) { if (IS_SLASH(Z_STRVAL_P(zval_file)[opts.remove_path_len])) { file_stripped = Z_STRVAL_P(zval_file) + opts.remove_path_len + 1; file_stripped_len = Z_STRLEN_P(zval_file) - opts.remove_path_len - 1; diff --git a/ext/zip/tests/gh19688.phpt b/ext/zip/tests/gh19688.phpt new file mode 100644 index 0000000000000..7e2a2800b2553 --- /dev/null +++ b/ext/zip/tests/gh19688.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-19688 (Remove pattern overflow in zip addGlob()) +--EXTENSIONS-- +zip +--FILE-- +open($filename, ZipArchive::CREATE | ZipArchive::OVERWRITE); +$options = array('remove_path' => $dir . 'a very long string here that will overrun'); +$zip->addGlob($testfile, 0, $options); +var_dump($zip->getNameIndex(0)); +?> +--CLEAN-- + +--EXPECTF-- +string(%d) "%s001.phpt" From 4e0e88a140f8bb32d75d43b3bb3e4d2a58a35acf Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 3 Sep 2025 11:48:39 +0200 Subject: [PATCH 12/83] Fix deoptimization after exit during inc/dec When the assumption that (PRE|POST)_(INC|DEC) overflows turns out to be false and we exit, effects are lost if op1 or result were in regs. Fix by updating the stack map before creating the exit point. Fixes GH-19669 Closes GH-19680 --- NEWS | 4 ++++ ext/opcache/jit/zend_jit_ir.c | 27 ++++++++++++-------------- ext/opcache/tests/jit/gh19669-001.phpt | 24 +++++++++++++++++++++++ ext/opcache/tests/jit/gh19669-002.phpt | 24 +++++++++++++++++++++++ 4 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 ext/opcache/tests/jit/gh19669-001.phpt create mode 100644 ext/opcache/tests/jit/gh19669-002.phpt diff --git a/NEWS b/NEWS index 529679116e305..35342af36fb55 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,10 @@ PHP NEWS . Fixed bug GH-19765 (object_properties_load() bypasses readonly property checks). (timwolla) +- Opcache: + . Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex). + (Arnaud) + - Standard: . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). (nielsdos) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 1a2c9f122a367..cd75856acb126 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -4902,33 +4902,30 @@ static int zend_jit_inc_dec(zend_jit_ctx *jit, const zend_op *opline, uint32_t o int32_t exit_point; const void *exit_addr; zend_jit_trace_stack *stack; - uint32_t old_res_info = 0; + uint32_t old_res_info = 0, old_op1_info = 0; stack = JIT_G(current_frame)->stack; if (opline->result_type != IS_UNUSED) { old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var)); + SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0); if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) { - SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0); + SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->result.var), ref); + } else { + SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->result.var), op1_lval_ref); } } + old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var)); + SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_LONG, 0); + SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->op1.var), ref); + exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); exit_addr = zend_jit_trace_get_exit_addr(exit_point); - if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) && - opline->result_type != IS_UNUSED) { - if_overflow = ir_IF(ir_OVERFLOW(ref)); - ir_IF_FALSE_cold(if_overflow); - jit_set_Z_LVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); - } - jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr)); - ir_IF_TRUE(if_overflow); - } else { - ir_GUARD(ir_OVERFLOW(ref), ir_CONST_ADDR(exit_addr)); - } + ir_GUARD(ir_OVERFLOW(ref), ir_CONST_ADDR(exit_addr)); + if (opline->result_type != IS_UNUSED) { SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info); } + SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info); } else { if_overflow = ir_IF(ir_OVERFLOW(ref)); ir_IF_FALSE(if_overflow); diff --git a/ext/opcache/tests/jit/gh19669-001.phpt b/ext/opcache/tests/jit/gh19669-001.phpt new file mode 100644 index 0000000000000..7d63643bb0157 --- /dev/null +++ b/ext/opcache/tests/jit/gh19669-001.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-19669: assertion failure zend_jit_trace_type_to_info_ex +--CREDITS-- +YuanchengJiang +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(-3) diff --git a/ext/opcache/tests/jit/gh19669-002.phpt b/ext/opcache/tests/jit/gh19669-002.phpt new file mode 100644 index 0000000000000..373356bcd0612 --- /dev/null +++ b/ext/opcache/tests/jit/gh19669-002.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-19669 002: assertion failure zend_jit_trace_type_to_info_ex +--CREDITS-- +YuanchengJiang +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(-10) From ed9430a5d1c1023bf25769aa33539fd33c6d3a32 Mon Sep 17 00:00:00 2001 From: Appla Date: Wed, 10 Sep 2025 16:37:55 +0800 Subject: [PATCH 13/83] Fix hard_timeout when zend-max-execution-timers is enabled Closes GH-19786 --- NEWS | 1 + Zend/zend_execute_API.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 8ed895987f9cc..1af55dd4c84f8 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ PHP NEWS - Core: . Fixed bug GH-19765 (object_properties_load() bypasses readonly property checks). (timwolla) + . Fixed hard_timeout with --enable-zend-max-execution-timers. (Appla) - Standard: . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index fe37b024934df..d4a373616fe92 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1531,7 +1531,9 @@ static void zend_set_timeout_ex(zend_long seconds, bool reset_signals) /* {{{ */ return; } #elif defined(ZEND_MAX_EXECUTION_TIMERS) - zend_max_execution_timer_settime(seconds); + if (seconds > 0) { + zend_max_execution_timer_settime(seconds); + } if (reset_signals) { sigset_t sigset; @@ -1618,7 +1620,9 @@ void zend_unset_timeout(void) /* {{{ */ tq_timer = NULL; } #elif ZEND_MAX_EXECUTION_TIMERS - zend_max_execution_timer_settime(0); + if (EG(timeout_seconds)) { + zend_max_execution_timer_settime(0); + } #elif defined(HAVE_SETITIMER) if (EG(timeout_seconds)) { struct itimerval no_timeout; From be5b2bf4b8fa20273d6bdf82d7c200fda1fc4318 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 11 Sep 2025 14:59:11 +0200 Subject: [PATCH 14/83] [skip ci] Add --enable-zend-max-execution-timers to variation nightly build Only enable for 8.3 because of GH-19786. --- .github/workflows/nightly.yml | 4 ++++ .github/workflows/root.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 62ea796502127..a8c8c80d7d500 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -44,6 +44,9 @@ on: skip_wordpress: required: true type: boolean + variation_enable_zend_max_execution_timers: + required: true + type: boolean permissions: contents: read jobs: @@ -199,6 +202,7 @@ jobs: zts: true configuration_parameters: >- CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE" + ${{ inputs.variation_enable_zend_max_execution_timers && '--enable-zend-max-execution-timers' || '' }} run_tests_parameters: -d zend_test.observer.enabled=1 -d zend_test.observer.show_output=0 timeout_minutes: 360 test_function_jit: true diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index 16418b1aa1d3a..b70b026eced07 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -64,4 +64,5 @@ jobs: skip_laravel: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_symfony: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_wordpress: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} + variation_enable_zend_max_execution_timers: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 3) || matrix.branch.version[0] >= 9 }} secrets: inherit From 2ad0b5cf052eba0c3c16fb0800fafe74412e8629 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 10 Sep 2025 21:28:23 +0200 Subject: [PATCH 15/83] Fix GH-19792: SCCP causes UAF for return value if both warning and exception are triggered If an exception _and_ a warning (or deprecation) is emitted, then the result is destroyed twice. Use an `else if` to prevent this. This is tested via zend_test because the deprecation that triggered the original reproducer may disappear in the future. Closes GH-19793. --- NEWS | 2 ++ Zend/Optimizer/sccp.c | 4 +--- ext/opcache/tests/opt/gh19792.phpt | 27 +++++++++++++++++++++++++++ ext/zend_test/test.c | 9 +++++++++ ext/zend_test/test.stub.php | 3 +++ ext/zend_test/test_arginfo.h | 6 +++++- 6 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 ext/opcache/tests/opt/gh19792.phpt diff --git a/NEWS b/NEWS index 1af55dd4c84f8..9f97a6985f9ed 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ PHP NEWS . Fixed bug GH-19765 (object_properties_load() bypasses readonly property checks). (timwolla) . Fixed hard_timeout with --enable-zend-max-execution-timers. (Appla) + . Fixed bug GH-19792 (SCCP causes UAF for return value if both warning and + exception are triggered). (nielsdos) - Standard: . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). diff --git a/Zend/Optimizer/sccp.c b/Zend/Optimizer/sccp.c index d5486803e19a6..4b7a77abb0911 100644 --- a/Zend/Optimizer/sccp.c +++ b/Zend/Optimizer/sccp.c @@ -842,9 +842,7 @@ static inline zend_result ct_eval_func_call( zval_ptr_dtor(result); zend_clear_exception(); retval = FAILURE; - } - - if (EG(capture_warnings_during_sccp) > 1) { + } else if (EG(capture_warnings_during_sccp) > 1) { zval_ptr_dtor(result); retval = FAILURE; } diff --git a/ext/opcache/tests/opt/gh19792.phpt b/ext/opcache/tests/opt/gh19792.phpt new file mode 100644 index 0000000000000..edd805ca57a19 --- /dev/null +++ b/ext/opcache/tests/opt/gh19792.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-19792 (SCCP causes UAF for return value if both warning and exception are triggered) +--EXTENSIONS-- +opcache +zend_test +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +Warning: a warning in %s on line %d +an exception diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index e7bb97cb44abb..67150d47b0441 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1528,3 +1528,12 @@ static PHP_FUNCTION(zend_test_gh18756) zend_mm_gc(heap); zend_mm_shutdown(heap, true, false); } + +static PHP_FUNCTION(zend_test_gh19792) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + RETVAL_STRING("this is a non-interned string"); + zend_error(E_WARNING, "a warning"); + zend_throw_error(NULL, "an exception"); +} diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index f9cb93b5a1ccb..290f55976d169 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -264,6 +264,9 @@ function zend_test_is_zend_ptr(int $addr): bool {} function zend_test_log_err_debug(string $str): void {} function zend_test_gh18756(): void {} + + /** @compile-time-eval */ + function zend_test_gh19792(): void {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index c7e3df5c58d24..cccd4608d9829 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2f161861ab09b6b5b594dc2db7c2c9df49d76aa7 */ + * Stub hash: fa769dbbf7ba1d10f0141fed6a45fc06a84cd94c */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -164,6 +164,8 @@ ZEND_END_ARG_INFO() #define arginfo_zend_test_gh18756 arginfo_zend_test_void_return +#define arginfo_zend_test_gh19792 arginfo_zend_test_void_return + #define arginfo_ZendTestNS2_namespaced_func arginfo_zend_test_is_pcre_bundled #define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_void_return @@ -295,6 +297,7 @@ static ZEND_FUNCTION(zend_test_cast_fread); static ZEND_FUNCTION(zend_test_is_zend_ptr); static ZEND_FUNCTION(zend_test_log_err_debug); static ZEND_FUNCTION(zend_test_gh18756); +static ZEND_FUNCTION(zend_test_gh19792); static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); @@ -376,6 +379,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_test_is_zend_ptr, arginfo_zend_test_is_zend_ptr) ZEND_FE(zend_test_log_err_debug, arginfo_zend_test_log_err_debug) ZEND_FE(zend_test_gh18756, arginfo_zend_test_gh18756) + ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE(zend_test_gh19792, arginfo_zend_test_gh19792) ZEND_NS_FALIAS("ZendTestNS2", namespaced_func, ZendTestNS2_namespaced_func, arginfo_ZendTestNS2_namespaced_func) ZEND_NS_DEP_FALIAS("ZendTestNS2", namespaced_deprecated_func, ZendTestNS2_namespaced_deprecated_func, arginfo_ZendTestNS2_namespaced_deprecated_func) ZEND_NS_FALIAS("ZendTestNS2", namespaced_aliased_func, zend_test_void_return, arginfo_ZendTestNS2_namespaced_aliased_func) From 4974d5ef49f4467ce183df73ffa85493cff93712 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 8 Sep 2025 18:55:47 +0200 Subject: [PATCH 16/83] Fix GH-19701: Serialize/deserialize loses some data See GH-19701 for discussion. This now restores the (correct) serialization output from versions PHP 7.4.1 and below. Closes GH-19762. --- NEWS | 1 + ext/standard/tests/serialize/gh19701.phpt | 30 +++++++++++++++++++++++ ext/standard/var.c | 9 +------ 3 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 ext/standard/tests/serialize/gh19701.phpt diff --git a/NEWS b/NEWS index 9f97a6985f9ed..28b811682f2cd 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ PHP NEWS - Standard: . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). (nielsdos) + . Fixed bug GH-19701 (Serialize/deserialize loses some data). (nielsdos) - Zip: . Fixed bug GH-19688 (Remove pattern overflow in zip addGlob()). (nielsdos) diff --git a/ext/standard/tests/serialize/gh19701.phpt b/ext/standard/tests/serialize/gh19701.phpt new file mode 100644 index 0000000000000..a1a3fd5c64bcc --- /dev/null +++ b/ext/standard/tests/serialize/gh19701.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-19701 (Serialize/deserialize loses some data) +--CREDITS-- +cuchac +DanielEScherzer +--FILE-- +parent = $baseProduct; +$baseProduct->children = [ $child ]; + +$data = [clone $baseProduct, $baseProduct]; + +echo serialize($data), "\n"; + +?> +--EXPECT-- +a:2:{i:0;O:4:"Item":2:{s:8:"children";a:1:{i:0;O:4:"Item":2:{s:8:"children";a:0:{}s:6:"parent";O:4:"Item":2:{s:8:"children";a:1:{i:0;r:4;}s:6:"parent";N;}}}s:6:"parent";N;}i:1;r:6;} diff --git a/ext/standard/var.c b/ext/standard/var.c index b7dc6dff1d884..c6e280d15ab93 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -954,18 +954,11 @@ static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable /* we should still add element even if it's not OK, * since we already wrote the length of the array before */ if (Z_TYPE_P(data) == IS_ARRAY) { - if (UNEXPECTED(Z_IS_RECURSIVE_P(data)) - || UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))) { + if (UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))) { php_add_var_hash(var_hash, struc, in_rcn_array); smart_str_appendl(buf, "N;", 2); } else { - if (Z_REFCOUNTED_P(data)) { - Z_PROTECT_RECURSION_P(data); - } php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false); - if (Z_REFCOUNTED_P(data)) { - Z_UNPROTECT_RECURSION_P(data); - } } } else { php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false); From 871fe600c244f3b63817ce1929e349fc02da8ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 12 Sep 2025 20:53:14 +0200 Subject: [PATCH 17/83] curl: Fix cloning of POST fields (#19813) --- NEWS | 4 +++ ext/curl/interface.c | 2 +- .../curl_copy_handle_variation3_clone.phpt | 34 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 ext/curl/tests/curl_copy_handle_variation3_clone.phpt diff --git a/NEWS b/NEWS index 28b811682f2cd..8dde1a0cd87dd 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,10 @@ PHP NEWS . Fixed bug GH-19792 (SCCP causes UAF for return value if both warning and exception are triggered). (nielsdos) +- Curl: + . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead + of the curl_copy_handle() function to clone a CurlHandle. + - Standard: . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). (nielsdos) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index b3139422cffa5..79f53be14f3d7 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -468,7 +468,7 @@ static zend_object *curl_clone_obj(zend_object *object) { clone_ch->cp = cp; _php_setup_easy_copy_handlers(clone_ch, ch); - postfields = &clone_ch->postfields; + postfields = &ch->postfields; if (Z_TYPE_P(postfields) != IS_UNDEF) { if (build_mime_structure_from_hash(clone_ch, postfields) == FAILURE) { zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); diff --git a/ext/curl/tests/curl_copy_handle_variation3_clone.phpt b/ext/curl/tests/curl_copy_handle_variation3_clone.phpt new file mode 100644 index 0000000000000..7951cb0258111 --- /dev/null +++ b/ext/curl/tests/curl_copy_handle_variation3_clone.phpt @@ -0,0 +1,34 @@ +--TEST-- +clone() allows to post CURLFile multiple times +--EXTENSIONS-- +curl +--FILE-- + $file); +var_dump(curl_setopt($ch1, CURLOPT_POSTFIELDS, $params)); + +$ch2 = clone($ch1); + +var_dump(curl_exec($ch1)); + +var_dump(curl_exec($ch2)); +?> +--EXPECTF-- +bool(true) +string(%d) "curl_copy_handle_variation3_clone.txt|application/octet-stream|5" +string(%d) "curl_copy_handle_variation3_clone.txt|application/octet-stream|5" +--CLEAN-- + From b8a9d790af149633ef3783d12dc099fe88560b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 12 Sep 2025 20:55:00 +0200 Subject: [PATCH 18/83] [skip ci] Add missing name for GH-19813 in NEWS --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 8dde1a0cd87dd..04bd8789f30b3 100644 --- a/NEWS +++ b/NEWS @@ -11,7 +11,7 @@ PHP NEWS - Curl: . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead - of the curl_copy_handle() function to clone a CurlHandle. + of the curl_copy_handle() function to clone a CurlHandle. (timwolla) - Standard: . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). From d30dd1b47b023ffa98b2f2b62a6f209f64822c32 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 12 Sep 2025 21:45:46 +0200 Subject: [PATCH 19/83] Fix GH-19784: SoapServer memory leak Closes GH-19818. --- NEWS | 3 +++ ext/soap/soap.c | 8 +++++++- ext/soap/tests/bugs/gh19784.phpt | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 ext/soap/tests/bugs/gh19784.phpt diff --git a/NEWS b/NEWS index 04bd8789f30b3..3572f95178465 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,9 @@ PHP NEWS . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead of the curl_copy_handle() function to clone a CurlHandle. (timwolla) +- Soap: + . Fixed bug GH-19784 (SoapServer memory leak). (nielsdos) + - Standard: . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). (nielsdos) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index fc51e32658f3a..4a4fa4f4e626b 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -880,7 +880,13 @@ PHP_METHOD(SoapServer, __construct) service->soap_functions.ft = zend_new_array(0); if (wsdl) { - service->sdl = get_sdl(ZEND_THIS, ZSTR_VAL(wsdl), cache_wsdl); + zend_try { + service->sdl = get_sdl(ZEND_THIS, ZSTR_VAL(wsdl), cache_wsdl); + } zend_catch { + xmlCharEncCloseFunc(service->encoding); + service->encoding = NULL; + zend_bailout(); + } zend_end_try(); if (service->uri == NULL) { if (service->sdl->target_ns) { service->uri = estrdup(service->sdl->target_ns); diff --git a/ext/soap/tests/bugs/gh19784.phpt b/ext/soap/tests/bugs/gh19784.phpt new file mode 100644 index 0000000000000..1f718e74c4598 --- /dev/null +++ b/ext/soap/tests/bugs/gh19784.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-19784 (SoapServer memory leak) +--EXTENSIONS-- +soap +--FILE-- + $v_5257); +new SoapServer('foobarbaz',$v_5238,); +?> +--EXPECTF-- + +%s%a From b46681d68685408f49e921f54e3b422180f1c096 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:15:01 +0200 Subject: [PATCH 20/83] Fix curl build failure on macOS+curl 8.16 Closes GH-19820. --- NEWS | 1 + ext/curl/interface.c | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/NEWS b/NEWS index 3572f95178465..e40677a759f13 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,7 @@ PHP NEWS - Curl: . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead of the curl_copy_handle() function to clone a CurlHandle. (timwolla) + . Fix curl build failure on macOS+curl 8.16. (nielsdos) - Soap: . Fixed bug GH-19784 (SoapServer memory leak). (nielsdos) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 79f53be14f3d7..67ec5aabc3911 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -677,11 +677,11 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string) /* }}} */ /* {{{ curl_progress */ -static size_t curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) +static int curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { php_curl *ch = (php_curl *)clientp; php_curl_callback *t = ch->handlers.progress; - size_t rval = 0; + int rval = 0; #if PHP_CURL_DEBUG fprintf(stderr, "curl_progress() called\n"); @@ -726,11 +726,11 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double #if LIBCURL_VERSION_NUM >= 0x072000 /* {{{ curl_xferinfo */ -static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +static int curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { php_curl *ch = (php_curl *)clientp; php_curl_callback *t = ch->handlers.xferinfo; - size_t rval = 0; + int rval = 0; #if PHP_CURL_DEBUG fprintf(stderr, "curl_xferinfo() called\n"); @@ -1154,8 +1154,8 @@ static void _php_curl_set_default_options(php_curl *ch) { char *cainfo; - curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); + curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L); curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write); curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); @@ -1164,10 +1164,10 @@ static void _php_curl_set_default_options(php_curl *ch) curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_header); curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); #ifndef ZTS - curl_easy_setopt(ch->cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 1); + curl_easy_setopt(ch->cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 1L); #endif - curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120); - curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20); /* prevent infinite redirects */ + curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120L); + curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20L); /* prevent infinite redirects */ cainfo = INI_STR("openssl.cafile"); if (!(cainfo && cainfo[0] != '\0')) { @@ -2278,7 +2278,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue /* no need to build the mime structure for empty hashtables; also works around https://github.com/curl/curl/issues/6455 */ curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, ""); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0L); } else { return build_mime_structure_from_hash(ch, zvalue); } @@ -2371,7 +2371,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue case CURLOPT_POSTREDIR: lval = zval_get_long(zvalue); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, lval & CURL_REDIR_POST_ALL); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, (long) (lval & CURL_REDIR_POST_ALL)); break; /* the following options deal with files, therefore the open_basedir check @@ -2406,11 +2406,11 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue if (zend_is_true(zvalue)) { curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug); curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *)ch); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1L); } else { curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, NULL); curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, NULL); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L); } break; From 8774e96bd4d977f893f0e5abc1920ee0968c3c94 Mon Sep 17 00:00:00 2001 From: Ruoyu Zhong Date: Sun, 14 Sep 2025 21:03:06 +0800 Subject: [PATCH 21/83] Fix naming clash with libxml macro In the macOS 26 SDK, xmlFree is defined as a macro for free. This causes issues where a same-named variable is used. Renaming the variable to should_free resolves the issue. See: $ grep -B4 -A2 -n "#define xmlFree(" "Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.sdk/usr/include/libxml/globals.h" 261-#if defined(LIBXML_HAS_DEPRECATED_MEMORY_ALLOCATION_FUNCTIONS) 262-#define xmlMalloc(size) malloc(size) 263-#define xmlMallocAtomic(size) malloc(size) 264-#define xmlRealloc(ptr, size) realloc((ptr), (size)) 265:#define xmlFree(ptr) free(ptr) 266-#define xmlMemStrdup(str) strdup(str) 267-#endif Fixes: ``` In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk/usr/include/libxml/xmlIO.h:117, from /Library/Developer/CommandLineTools/SDKs/MacOSX26.sdk/usr/include/libxml/parser.h:813, from /private/tmp/php-20250914-13349-uqsk5o/php-8.4.12/ext/dom/php_dom.h:29, from /private/tmp/php-20250914-13349-uqsk5o/php-8.4.12/ext/dom/attr.c:26: /private/tmp/php-20250914-13349-uqsk5o/php-8.4.12/ext/dom/attr.c: In function 'dom_compare_value': /private/tmp/php-20250914-13349-uqsk5o/php-8.4.12/ext/dom/attr.c:208:17: error: called object 'free' is not a function or function pointer 208 | xmlFree(attr_value); | ^~~~~~~ /private/tmp/php-20250914-13349-uqsk5o/php-8.4.12/ext/dom/attr.c:204:14: note: declared here 204 | bool free; | ^~~~ make: *** [ext/dom/attr.lo] Error 1 ``` Closes GH-19832. Signed-off-by: Ruoyu Zhong --- NEWS | 3 +++ ext/dom/attr.c | 6 +++--- ext/dom/php_dom.c | 6 +++--- ext/simplexml/simplexml.c | 6 +++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 08c46ee656106..e3eadb6e41b79 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,9 @@ PHP NEWS of the curl_copy_handle() function to clone a CurlHandle. (timwolla) . Fix curl build failure on macOS+curl 8.16. (nielsdos) +- DOM: + . Fix macro name clash on macOS. (Ruoyu Zhong) + - Opcache: . Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex). (Arnaud) diff --git a/ext/dom/attr.c b/ext/dom/attr.c index dfe3abc1a885c..5a0900d657eea 100644 --- a/ext/dom/attr.c +++ b/ext/dom/attr.c @@ -201,10 +201,10 @@ PHP_METHOD(DOMAttr, isId) bool dom_compare_value(const xmlAttr *attr, const xmlChar *value) { - bool free; - xmlChar *attr_value = php_libxml_attr_value(attr, &free); + bool should_free; + xmlChar *attr_value = php_libxml_attr_value(attr, &should_free); bool result = xmlStrEqual(attr_value, value); - if (free) { + if (should_free) { xmlFree(attr_value); } return result; diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 3242529d8842c..2438a081351a0 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -2396,10 +2396,10 @@ void php_dom_get_content_into_zval(const xmlNode *nodep, zval *return_value, boo } case XML_ATTRIBUTE_NODE: { - bool free; - xmlChar *value = php_libxml_attr_value((const xmlAttr *) nodep, &free); + bool should_free; + xmlChar *value = php_libxml_attr_value((const xmlAttr *) nodep, &should_free); RETVAL_STRING_FAST((const char *) value); - if (free) { + if (should_free) { xmlFree(value); } return; diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 619f627e8532a..787c1292e0e0c 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -1529,10 +1529,10 @@ static void sxe_add_registered_namespaces(php_sxe_object *sxe, xmlNodePtr node, /* Attributes in the xmlns namespace should be treated as namespace declarations too. */ if (attr->ns && xmlStrEqual(attr->ns->href, (const xmlChar *) "http://www.w3.org/2000/xmlns/")) { const char *prefix = attr->ns->prefix ? (const char *) attr->name : ""; - bool free; - xmlChar *href = php_libxml_attr_value(attr, &free); + bool should_free; + xmlChar *href = php_libxml_attr_value(attr, &should_free); sxe_add_namespace_name_raw(return_value, prefix, (const char *) href); - if (free) { + if (should_free) { xmlFree(href); } } From 22252954efd1a8e81f735dd4ebd908b66f8fc733 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 31 Aug 2025 21:28:22 +0200 Subject: [PATCH 22/83] Fix GH-19653: Closure named argument unpacking between temporary closures can cause a crash Due to user closures, the `fbc` address isn't unique if the memory address is reused. We need to distinguish using a unique key, and we choose arg_info such that it can be reused across different functions. Closes GH-19654. --- NEWS | 2 ++ Zend/tests/closures/gh19653_1.phpt | 27 +++++++++++++++++++++++++++ Zend/tests/closures/gh19653_2.phpt | 23 +++++++++++++++++++++++ Zend/zend_execute.c | 21 +++++++++++++++------ 4 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 Zend/tests/closures/gh19653_1.phpt create mode 100644 Zend/tests/closures/gh19653_2.phpt diff --git a/NEWS b/NEWS index e40677a759f13..643763166e82c 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,8 @@ PHP NEWS . Fixed hard_timeout with --enable-zend-max-execution-timers. (Appla) . Fixed bug GH-19792 (SCCP causes UAF for return value if both warning and exception are triggered). (nielsdos) + . Fixed bug GH-19653 (Closure named argument unpacking between temporary + closures can cause a crash). (nielsdos, Arnaud, Bob) - Curl: . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead diff --git a/Zend/tests/closures/gh19653_1.phpt b/Zend/tests/closures/gh19653_1.phpt new file mode 100644 index 0000000000000..93b119eb57826 --- /dev/null +++ b/Zend/tests/closures/gh19653_1.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-19653 (Closure named argument unpacking between temporary closures can cause a crash) +--CREDITS-- +ivan-u7n +--FILE-- + +--EXPECT-- +usage1() func1() a1=a1 a2=a2 a3=m3+ +usage1() [function] func1() a1=a1 a2=m2+ a3=m3+ diff --git a/Zend/tests/closures/gh19653_2.phpt b/Zend/tests/closures/gh19653_2.phpt new file mode 100644 index 0000000000000..7eb837dd22c4d --- /dev/null +++ b/Zend/tests/closures/gh19653_2.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-19653 (Closure named argument unpacking between temporary closures can cause a crash) - eval variation +--CREDITS-- +arnaud-lb +--FILE-- + +--EXPECTF-- +int(1) + +Fatal error: Uncaught Error: Unknown named parameter $a in %s:%d +Stack trace: +#0 %s(%d): usage1(Object(Closure)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 99da3a4a051f3..4e6339ca901cb 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -5058,7 +5058,12 @@ static zend_never_inline zend_result ZEND_FASTCALL zend_quick_check_constant( static zend_always_inline uint32_t zend_get_arg_offset_by_name( zend_function *fbc, zend_string *arg_name, void **cache_slot) { - if (EXPECTED(*cache_slot == fbc)) { + /* Due to closures, the `fbc` address isn't unique if the memory address is reused. + * The argument info will be however and uniquely positions the arguments. + * We do support NULL arg_info, so we have to distinguish that from an uninitialized cache slot. */ + void *unique_id = (void *) ((uintptr_t) fbc->common.arg_info | 1); + + if (EXPECTED(*cache_slot == unique_id)) { return *(uintptr_t *)(cache_slot + 1); } @@ -5069,8 +5074,10 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( for (uint32_t i = 0; i < num_args; i++) { zend_arg_info *arg_info = &fbc->op_array.arg_info[i]; if (zend_string_equals(arg_name, arg_info->name)) { - *cache_slot = fbc; - *(uintptr_t *)(cache_slot + 1) = i; + if (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)) { + *cache_slot = unique_id; + *(uintptr_t *)(cache_slot + 1) = i; + } return i; } } @@ -5079,7 +5086,7 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i]; size_t len = strlen(arg_info->name); if (zend_string_equals_cstr(arg_name, arg_info->name, len)) { - *cache_slot = fbc; + *cache_slot = unique_id; *(uintptr_t *)(cache_slot + 1) = i; return i; } @@ -5087,8 +5094,10 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( } if (fbc->common.fn_flags & ZEND_ACC_VARIADIC) { - *cache_slot = fbc; - *(uintptr_t *)(cache_slot + 1) = fbc->common.num_args; + if (fbc->type == ZEND_INTERNAL_FUNCTION || !fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)) { + *cache_slot = unique_id; + *(uintptr_t *)(cache_slot + 1) = fbc->common.num_args; + } return fbc->common.num_args; } From a0329dbab009d984926f8817359ba5d83195d960 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Mon, 15 Sep 2025 10:38:53 +0100 Subject: [PATCH 23/83] Update timelib to 2022.14 --- ext/date/lib/parse_date.c | 213 +++++++++++++++-------------- ext/date/lib/parse_date.re | 5 +- ext/date/lib/parse_iso_intervals.c | 18 +-- ext/date/lib/timelib.h | 8 +- 4 files changed, 125 insertions(+), 119 deletions(-) diff --git a/ext/date/lib/parse_date.c b/ext/date/lib/parse_date.c index ea1602ef13b4e..d27511cad35e1 100644 --- a/ext/date/lib/parse_date.c +++ b/ext/date/lib/parse_date.c @@ -1,4 +1,4 @@ -/* Generated by re2c 1.0.3 on Wed Sep 11 17:29:29 2024 */ +/* Generated by re2c 1.0.3 on Mon Sep 15 10:38:03 2025 */ #line 1 "ext/date/lib/parse_date.re" /* * The MIT License (MIT) @@ -941,10 +941,12 @@ timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int { timelib_tzinfo *res; timelib_long retval = 0; + size_t paren_count = 0; *tz_not_found = 0; while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') { + paren_count += **ptr == '('; ++*ptr; } if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) { @@ -993,8 +995,9 @@ timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int *tz_not_found = (found == 0); retval = offset; } - while (**ptr == ')') { + while (paren_count > 0 && **ptr == ')') { ++*ptr; + paren_count--; } return retval; } @@ -1018,11 +1021,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) std: s->tok = cursor; s->len = 0; -#line 1151 "ext/date/lib/parse_date.re" +#line 1154 "ext/date/lib/parse_date.re" -#line 1026 "" +#line 1029 "ext/date/lib/parse_date.c" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -1203,23 +1206,23 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(2, *YYCURSOR); ++YYCURSOR; YYDEBUG(3, *YYCURSOR); -#line 1984 "ext/date/lib/parse_date.re" +#line 1987 "ext/date/lib/parse_date.re" { s->pos = cursor; s->line++; goto std; } -#line 1212 "" +#line 1215 "ext/date/lib/parse_date.c" yy4: YYDEBUG(4, *YYCURSOR); ++YYCURSOR; yy5: YYDEBUG(5, *YYCURSOR); -#line 1990 "ext/date/lib/parse_date.re" +#line 1993 "ext/date/lib/parse_date.re" { add_error(s, TIMELIB_ERR_UNEXPECTED_CHARACTER, "Unexpected character"); goto std; } -#line 1223 "" +#line 1226 "ext/date/lib/parse_date.c" yy6: YYDEBUG(6, *YYCURSOR); yyaccept = 0; @@ -1234,11 +1237,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy58; yy8: YYDEBUG(8, *YYCURSOR); -#line 1979 "ext/date/lib/parse_date.re" +#line 1982 "ext/date/lib/parse_date.re" { goto std; } -#line 1242 "" +#line 1245 "ext/date/lib/parse_date.c" yy9: YYDEBUG(9, *YYCURSOR); yych = *++YYCURSOR; @@ -1272,11 +1275,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(11, *YYCURSOR); ++YYCURSOR; YYDEBUG(12, *YYCURSOR); -#line 1974 "ext/date/lib/parse_date.re" +#line 1977 "ext/date/lib/parse_date.re" { goto std; } -#line 1280 "" +#line 1283 "ext/date/lib/parse_date.c" yy13: YYDEBUG(13, *YYCURSOR); yyaccept = 1; @@ -1777,7 +1780,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy20: YYDEBUG(20, *YYCURSOR); -#line 1889 "ext/date/lib/parse_date.re" +#line 1892 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("tzcorrection | tz"); @@ -1791,7 +1794,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIMEZONE; } -#line 1795 "" +#line 1798 "ext/date/lib/parse_date.c" yy21: YYDEBUG(21, *YYCURSOR); yych = *++YYCURSOR; @@ -3596,7 +3599,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy81: YYDEBUG(81, *YYCURSOR); -#line 1636 "ext/date/lib/parse_date.re" +#line 1639 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datenoyearrev"); TIMELIB_INIT; @@ -3607,7 +3610,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 3611 "" +#line 3614 "ext/date/lib/parse_date.c" yy82: YYDEBUG(82, *YYCURSOR); yych = *++YYCURSOR; @@ -4122,7 +4125,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } if (yych == '.') goto yy289; YYDEBUG(114, *YYCURSOR); -#line 1211 "ext/date/lib/parse_date.re" +#line 1214 "ext/date/lib/parse_date.re" { timelib_ull i; @@ -4147,7 +4150,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 4151 "" +#line 4154 "ext/date/lib/parse_date.c" yy115: YYDEBUG(115, *YYCURSOR); ++YYCURSOR; @@ -5873,7 +5876,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy177: YYDEBUG(177, *YYCURSOR); -#line 1377 "ext/date/lib/parse_date.re" +#line 1380 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("timetiny24 | timeshort24 | timelong24 | iso8601long"); @@ -5900,7 +5903,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIME24_WITH_ZONE; } -#line 5904 "" +#line 5907 "ext/date/lib/parse_date.c" yy178: YYDEBUG(178, *YYCURSOR); yyaccept = 4; @@ -6929,7 +6932,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy224: YYDEBUG(224, *YYCURSOR); -#line 1471 "ext/date/lib/parse_date.re" +#line 1474 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("americanshort | american"); @@ -6944,7 +6947,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_AMERICAN; } -#line 6948 "" +#line 6951 "ext/date/lib/parse_date.c" yy225: YYDEBUG(225, *YYCURSOR); yyaccept = 5; @@ -7187,7 +7190,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy431; yy251: YYDEBUG(251, *YYCURSOR); -#line 1553 "ext/date/lib/parse_date.re" +#line 1556 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datefull"); @@ -7201,7 +7204,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_FULL; } -#line 7205 "" +#line 7208 "ext/date/lib/parse_date.c" yy252: YYDEBUG(252, *YYCURSOR); yyaccept = 3; @@ -7315,7 +7318,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych == 'e') goto yy440; yy260: YYDEBUG(260, *YYCURSOR); -#line 1958 "ext/date/lib/parse_date.re" +#line 1961 "ext/date/lib/parse_date.re" { timelib_ull i; DEBUG_OUTPUT("relative"); @@ -7330,7 +7333,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 7334 "" +#line 7337 "ext/date/lib/parse_date.c" yy261: YYDEBUG(261, *YYCURSOR); yych = *++YYCURSOR; @@ -7776,7 +7779,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy471; yy290: YYDEBUG(290, *YYCURSOR); -#line 1237 "ext/date/lib/parse_date.re" +#line 1240 "ext/date/lib/parse_date.re" { timelib_sll i; timelib_ull us; @@ -7815,7 +7818,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 7819 "" +#line 7822 "ext/date/lib/parse_date.c" yy291: YYDEBUG(291, *YYCURSOR); yych = *++YYCURSOR; @@ -7840,7 +7843,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy293: YYDEBUG(293, *YYCURSOR); -#line 1799 "ext/date/lib/parse_date.re" +#line 1802 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("ago"); TIMELIB_INIT; @@ -7860,7 +7863,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_AGO; } -#line 7864 "" +#line 7867 "ext/date/lib/parse_date.c" yy294: YYDEBUG(294, *YYCURSOR); yyaccept = 7; @@ -7899,7 +7902,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy295: YYDEBUG(295, *YYCURSOR); -#line 1879 "ext/date/lib/parse_date.re" +#line 1882 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("monthtext"); TIMELIB_INIT; @@ -7908,7 +7911,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 7912 "" +#line 7915 "ext/date/lib/parse_date.c" yy296: YYDEBUG(296, *YYCURSOR); yyaccept = 7; @@ -8483,7 +8486,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy315: YYDEBUG(315, *YYCURSOR); -#line 1820 "ext/date/lib/parse_date.re" +#line 1823 "ext/date/lib/parse_date.re" { const timelib_relunit* relunit; DEBUG_OUTPUT("daytext"); @@ -8500,7 +8503,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_WEEKDAY; } -#line 8504 "" +#line 8507 "ext/date/lib/parse_date.c" yy316: YYDEBUG(316, *YYCURSOR); yych = *++YYCURSOR; @@ -8768,7 +8771,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy325: YYDEBUG(325, *YYCURSOR); -#line 1622 "ext/date/lib/parse_date.re" +#line 1625 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datetextual | datenoyear"); @@ -8781,7 +8784,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 8785 "" +#line 8788 "ext/date/lib/parse_date.c" yy326: YYDEBUG(326, *YYCURSOR); yyaccept = 10; @@ -9475,7 +9478,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy351: YYDEBUG(351, *YYCURSOR); -#line 1168 "ext/date/lib/parse_date.re" +#line 1171 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("now"); TIMELIB_INIT; @@ -9483,7 +9486,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 9487 "" +#line 9490 "ext/date/lib/parse_date.c" yy352: YYDEBUG(352, *YYCURSOR); yyaccept = 2; @@ -10986,7 +10989,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy420: YYDEBUG(420, *YYCURSOR); -#line 1405 "ext/date/lib/parse_date.re" +#line 1408 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("gnunocolon"); TIMELIB_INIT; @@ -11008,7 +11011,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_GNU_NOCOLON; } -#line 11012 "" +#line 11015 "ext/date/lib/parse_date.c" yy421: YYDEBUG(421, *YYCURSOR); yyaccept = 13; @@ -11089,7 +11092,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy422: YYDEBUG(422, *YYCURSOR); -#line 1790 "ext/date/lib/parse_date.re" +#line 1793 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("year4"); TIMELIB_INIT; @@ -11097,7 +11100,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_CLF; } -#line 11101 "" +#line 11104 "ext/date/lib/parse_date.c" yy423: YYDEBUG(423, *YYCURSOR); yyaccept = 3; @@ -11704,7 +11707,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(456, *YYCURSOR); ++YYCURSOR; YYDEBUG(457, *YYCURSOR); -#line 1339 "ext/date/lib/parse_date.re" +#line 1342 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12"); TIMELIB_INIT; @@ -11721,7 +11724,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIME12; } -#line 11725 "" +#line 11728 "ext/date/lib/parse_date.c" yy458: YYDEBUG(458, *YYCURSOR); yych = *++YYCURSOR; @@ -13048,7 +13051,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy526: YYDEBUG(526, *YYCURSOR); -#line 1177 "ext/date/lib/parse_date.re" +#line 1180 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("noon"); TIMELIB_INIT; @@ -13059,7 +13062,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 13063 "" +#line 13066 "ext/date/lib/parse_date.c" yy527: YYDEBUG(527, *YYCURSOR); yyaccept = 2; @@ -14105,7 +14108,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy567: YYDEBUG(567, *YYCURSOR); -#line 1539 "ext/date/lib/parse_date.re" +#line 1542 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("gnudateshort"); @@ -14118,7 +14121,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 14122 "" +#line 14125 "ext/date/lib/parse_date.c" yy568: YYDEBUG(568, *YYCURSOR); yyaccept = 15; @@ -14569,7 +14572,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy600: YYDEBUG(600, *YYCURSOR); -#line 1608 "ext/date/lib/parse_date.re" +#line 1611 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datenodayrev"); @@ -14582,7 +14585,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_NO_DAY; } -#line 14586 "" +#line 14589 "ext/date/lib/parse_date.c" yy601: YYDEBUG(601, *YYCURSOR); yych = *++YYCURSOR; @@ -15957,7 +15960,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(696, *YYCURSOR); ++YYCURSOR; YYDEBUG(697, *YYCURSOR); -#line 1594 "ext/date/lib/parse_date.re" +#line 1597 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datenoday"); @@ -15970,7 +15973,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_NO_DAY; } -#line 15974 "" +#line 15977 "ext/date/lib/parse_date.c" yy698: YYDEBUG(698, *YYCURSOR); yych = *++YYCURSOR; @@ -16531,7 +16534,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy722: YYDEBUG(722, *YYCURSOR); -#line 1189 "ext/date/lib/parse_date.re" +#line 1192 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("midnight | today"); TIMELIB_INIT; @@ -16540,7 +16543,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 16544 "" +#line 16547 "ext/date/lib/parse_date.c" yy723: YYDEBUG(723, *YYCURSOR); yych = *++YYCURSOR; @@ -16850,7 +16853,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy897; yy739: YYDEBUG(739, *YYCURSOR); -#line 1580 "ext/date/lib/parse_date.re" +#line 1583 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pointed date YY"); @@ -16863,7 +16866,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_FULL_POINTED; } -#line 16867 "" +#line 16870 "ext/date/lib/parse_date.c" yy740: YYDEBUG(740, *YYCURSOR); yyaccept = 15; @@ -16975,7 +16978,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy752: YYDEBUG(752, *YYCURSOR); -#line 1525 "ext/date/lib/parse_date.re" +#line 1528 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("gnudateshorter"); @@ -16988,7 +16991,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 16992 "" +#line 16995 "ext/date/lib/parse_date.c" yy753: YYDEBUG(753, *YYCURSOR); yyaccept = 18; @@ -17237,7 +17240,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy777: YYDEBUG(777, *YYCURSOR); -#line 1451 "ext/date/lib/parse_date.re" +#line 1454 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("iso8601nocolon"); @@ -17256,7 +17259,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_NOCOLON; } -#line 17260 "" +#line 17263 "ext/date/lib/parse_date.c" yy778: YYDEBUG(778, *YYCURSOR); yyaccept = 19; @@ -18484,7 +18487,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy849: YYDEBUG(849, *YYCURSOR); -#line 1928 "ext/date/lib/parse_date.re" +#line 1931 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz"); @@ -18513,7 +18516,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_SHORTDATE_WITH_TIME; } -#line 18517 "" +#line 18520 "ext/date/lib/parse_date.c" yy850: YYDEBUG(850, *YYCURSOR); yyaccept = 20; @@ -19557,7 +19560,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy926: YYDEBUG(926, *YYCURSOR); -#line 1686 "ext/date/lib/parse_date.re" +#line 1689 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pgydotd"); @@ -19570,7 +19573,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_PG_YEARDAY; } -#line 19574 "" +#line 19577 "ext/date/lib/parse_date.c" yy927: YYDEBUG(927, *YYCURSOR); yyaccept = 21; @@ -19824,7 +19827,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '7') goto yy1059; yy942: YYDEBUG(942, *YYCURSOR); -#line 1719 "ext/date/lib/parse_date.re" +#line 1722 "ext/date/lib/parse_date.re" { timelib_sll w, d; DEBUG_OUTPUT("isoweek"); @@ -19842,7 +19845,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_WEEK; } -#line 19846 "" +#line 19849 "ext/date/lib/parse_date.c" yy943: YYDEBUG(943, *YYCURSOR); yych = *++YYCURSOR; @@ -20318,7 +20321,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych == 'e') goto yy1094; yy982: YYDEBUG(982, *YYCURSOR); -#line 1862 "ext/date/lib/parse_date.re" +#line 1865 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -20334,7 +20337,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 20338 "" +#line 20341 "ext/date/lib/parse_date.c" yy983: YYDEBUG(983, *YYCURSOR); yych = *++YYCURSOR; @@ -20681,7 +20684,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1020, *YYCURSOR); ++YYCURSOR; YYDEBUG(1021, *YYCURSOR); -#line 1568 "ext/date/lib/parse_date.re" +#line 1571 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("pointed date YYYY"); TIMELIB_INIT; @@ -20692,7 +20695,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_FULL_POINTED; } -#line 20696 "" +#line 20699 "ext/date/lib/parse_date.c" yy1022: YYDEBUG(1022, *YYCURSOR); ++YYCURSOR; @@ -20721,7 +20724,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1025: YYDEBUG(1025, *YYCURSOR); -#line 1499 "ext/date/lib/parse_date.re" +#line 1502 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("iso8601date2"); @@ -20734,7 +20737,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 20738 "" +#line 20741 "ext/date/lib/parse_date.c" yy1026: YYDEBUG(1026, *YYCURSOR); yyaccept = 15; @@ -20954,7 +20957,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1043: YYDEBUG(1043, *YYCURSOR); -#line 1487 "ext/date/lib/parse_date.re" +#line 1490 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash"); TIMELIB_INIT; @@ -20965,7 +20968,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 20969 "" +#line 20972 "ext/date/lib/parse_date.c" yy1044: YYDEBUG(1044, *YYCURSOR); yyaccept = 26; @@ -21080,7 +21083,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1048: YYDEBUG(1048, *YYCURSOR); -#line 1648 "ext/date/lib/parse_date.re" +#line 1651 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datenocolon"); TIMELIB_INIT; @@ -21091,7 +21094,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_NOCOLON; } -#line 21095 "" +#line 21098 "ext/date/lib/parse_date.c" yy1049: YYDEBUG(1049, *YYCURSOR); yych = *++YYCURSOR; @@ -21161,7 +21164,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1059, *YYCURSOR); ++YYCURSOR; YYDEBUG(1060, *YYCURSOR); -#line 1700 "ext/date/lib/parse_date.re" +#line 1703 "ext/date/lib/parse_date.re" { timelib_sll w, d; DEBUG_OUTPUT("isoweekday"); @@ -21179,7 +21182,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_WEEK; } -#line 21183 "" +#line 21186 "ext/date/lib/parse_date.c" yy1061: YYDEBUG(1061, *YYCURSOR); yych = *++YYCURSOR; @@ -21242,7 +21245,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy1143; yy1070: YYDEBUG(1070, *YYCURSOR); -#line 1738 "ext/date/lib/parse_date.re" +#line 1741 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pgtextshort"); @@ -21255,7 +21258,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_PG_TEXT; } -#line 21259 "" +#line 21262 "ext/date/lib/parse_date.c" yy1071: YYDEBUG(1071, *YYCURSOR); yych = *++YYCURSOR; @@ -21728,7 +21731,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) ++YYCURSOR; yy1107: YYDEBUG(1107, *YYCURSOR); -#line 1199 "ext/date/lib/parse_date.re" +#line 1202 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("tomorrow"); TIMELIB_INIT; @@ -21739,7 +21742,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 21743 "" +#line 21746 "ext/date/lib/parse_date.c" yy1108: YYDEBUG(1108, *YYCURSOR); yyaccept = 28; @@ -22076,7 +22079,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1140, *YYCURSOR); ++YYCURSOR; YYDEBUG(1141, *YYCURSOR); -#line 1752 "ext/date/lib/parse_date.re" +#line 1755 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pgtextreverse"); @@ -22089,7 +22092,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_PG_TEXT; } -#line 22093 "" +#line 22096 "ext/date/lib/parse_date.c" yy1142: YYDEBUG(1142, *YYCURSOR); ++YYCURSOR; @@ -22133,7 +22136,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1145: YYDEBUG(1145, *YYCURSOR); -#line 1294 "ext/date/lib/parse_date.re" +#line 1297 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("backof | frontof"); TIMELIB_INIT; @@ -22155,7 +22158,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_LF_DAY_OF_MONTH; } -#line 22159 "" +#line 22162 "ext/date/lib/parse_date.c" yy1146: YYDEBUG(1146, *YYCURSOR); yyaccept = 29; @@ -22479,7 +22482,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1172: YYDEBUG(1172, *YYCURSOR); -#line 1838 "ext/date/lib/parse_date.re" +#line 1841 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -22502,7 +22505,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 22506 "" +#line 22509 "ext/date/lib/parse_date.c" yy1173: YYDEBUG(1173, *YYCURSOR); yych = *++YYCURSOR; @@ -22514,7 +22517,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) ++YYCURSOR; yy1175: YYDEBUG(1175, *YYCURSOR); -#line 1156 "ext/date/lib/parse_date.re" +#line 1159 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("yesterday"); TIMELIB_INIT; @@ -22525,7 +22528,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 22529 "" +#line 22532 "ext/date/lib/parse_date.c" yy1176: YYDEBUG(1176, *YYCURSOR); yyaccept = 31; @@ -23018,7 +23021,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1222, *YYCURSOR); ++YYCURSOR; YYDEBUG(1223, *YYCURSOR); -#line 1904 "ext/date/lib/parse_date.re" +#line 1907 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12"); TIMELIB_INIT; @@ -23041,7 +23044,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_SHORTDATE_WITH_TIME; } -#line 23045 "" +#line 23048 "ext/date/lib/parse_date.c" yy1224: YYDEBUG(1224, *YYCURSOR); yych = *++YYCURSOR; @@ -23543,7 +23546,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1268, *YYCURSOR); ++YYCURSOR; YYDEBUG(1269, *YYCURSOR); -#line 1317 "ext/date/lib/parse_date.re" +#line 1320 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -23564,7 +23567,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_WEEK_DAY_OF_MONTH; } -#line 23568 "" +#line 23571 "ext/date/lib/parse_date.c" yy1270: YYDEBUG(1270, *YYCURSOR); yyaccept = 24; @@ -23611,7 +23614,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1273, *YYCURSOR); ++YYCURSOR; YYDEBUG(1274, *YYCURSOR); -#line 1277 "ext/date/lib/parse_date.re" +#line 1280 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("firstdayof | lastdayof"); TIMELIB_INIT; @@ -23627,12 +23630,12 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_LF_DAY_OF_MONTH; } -#line 23631 "" +#line 23634 "ext/date/lib/parse_date.c" yy1275: YYDEBUG(1275, *YYCURSOR); ++YYCURSOR; YYDEBUG(1276, *YYCURSOR); -#line 1513 "ext/date/lib/parse_date.re" +#line 1516 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("iso8601datex"); TIMELIB_INIT; @@ -23643,7 +23646,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 23647 "" +#line 23650 "ext/date/lib/parse_date.c" yy1277: YYDEBUG(1277, *YYCURSOR); yych = *++YYCURSOR; @@ -23746,7 +23749,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1290, *YYCURSOR); ++YYCURSOR; YYDEBUG(1291, *YYCURSOR); -#line 1357 "ext/date/lib/parse_date.re" +#line 1360 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("mssqltime"); TIMELIB_INIT; @@ -23765,7 +23768,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIME24_WITH_ZONE; } -#line 23769 "" +#line 23772 "ext/date/lib/parse_date.c" yy1292: YYDEBUG(1292, *YYCURSOR); yych = *++YYCURSOR; @@ -24189,7 +24192,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy1331; yy1329: YYDEBUG(1329, *YYCURSOR); -#line 1660 "ext/date/lib/parse_date.re" +#line 1663 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif"); @@ -24214,7 +24217,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_XMLRPC_SOAP; } -#line 24218 "" +#line 24221 "ext/date/lib/parse_date.c" yy1330: YYDEBUG(1330, *YYCURSOR); yych = *++YYCURSOR; @@ -24584,7 +24587,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= ':') goto yy1383; yy1375: YYDEBUG(1375, *YYCURSOR); -#line 1766 "ext/date/lib/parse_date.re" +#line 1769 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("clf"); @@ -24607,7 +24610,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_CLF; } -#line 24611 "" +#line 24614 "ext/date/lib/parse_date.c" yy1376: YYDEBUG(1376, *YYCURSOR); yyaccept = 33; @@ -24839,7 +24842,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych == ':') goto yy1286; goto yy1329; } -#line 1994 "ext/date/lib/parse_date.re" +#line 1997 "ext/date/lib/parse_date.re" } diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re index d32be9bfe7be7..c40a5c07c9aa1 100644 --- a/ext/date/lib/parse_date.re +++ b/ext/date/lib/parse_date.re @@ -939,10 +939,12 @@ timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int { timelib_tzinfo *res; timelib_long retval = 0; + size_t paren_count = 0; *tz_not_found = 0; while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') { + paren_count += **ptr == '('; ++*ptr; } if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) { @@ -991,8 +993,9 @@ timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int *tz_not_found = (found == 0); retval = offset; } - while (**ptr == ')') { + while (paren_count > 0 && **ptr == ')') { ++*ptr; + paren_count--; } return retval; } diff --git a/ext/date/lib/parse_iso_intervals.c b/ext/date/lib/parse_iso_intervals.c index cdc329431ec45..a9fca1b609413 100644 --- a/ext/date/lib/parse_iso_intervals.c +++ b/ext/date/lib/parse_iso_intervals.c @@ -1,4 +1,4 @@ -/* Generated by re2c 1.0.3 on Wed Sep 11 17:29:40 2024 */ +/* Generated by re2c 1.0.3 on Mon Sep 15 10:38:16 2025 */ #line 1 "ext/date/lib/parse_iso_intervals.re" /* * The MIT License (MIT) @@ -180,7 +180,7 @@ static int scan(Scanner *s) -#line 184 "" +#line 184 "ext/date/lib/parse_iso_intervals.c" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -252,7 +252,7 @@ static int scan(Scanner *s) s->pos = cursor; s->line++; goto std; } -#line 256 "" +#line 256 "ext/date/lib/parse_iso_intervals.c" yy4: YYDEBUG(4, *YYCURSOR); ++YYCURSOR; @@ -263,7 +263,7 @@ static int scan(Scanner *s) add_error(s, "Unexpected character"); goto std; } -#line 267 "" +#line 267 "ext/date/lib/parse_iso_intervals.c" yy6: YYDEBUG(6, *YYCURSOR); ++YYCURSOR; @@ -272,7 +272,7 @@ static int scan(Scanner *s) { goto std; } -#line 276 "" +#line 276 "ext/date/lib/parse_iso_intervals.c" yy8: YYDEBUG(8, *YYCURSOR); yyaccept = 0; @@ -330,7 +330,7 @@ static int scan(Scanner *s) TIMELIB_DEINIT; return TIMELIB_PERIOD; } -#line 334 "" +#line 334 "ext/date/lib/parse_iso_intervals.c" yy11: YYDEBUG(11, *YYCURSOR); yych = *++YYCURSOR; @@ -399,7 +399,7 @@ static int scan(Scanner *s) s->have_recurrences = 1; return TIMELIB_PERIOD; } -#line 403 "" +#line 403 "ext/date/lib/parse_iso_intervals.c" yy19: YYDEBUG(19, *YYCURSOR); yych = *++YYCURSOR; @@ -917,7 +917,7 @@ static int scan(Scanner *s) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 921 "" +#line 921 "ext/date/lib/parse_iso_intervals.c" yy91: YYDEBUG(91, *YYCURSOR); yych = *++YYCURSOR; @@ -956,7 +956,7 @@ static int scan(Scanner *s) TIMELIB_DEINIT; return TIMELIB_PERIOD; } -#line 960 "" +#line 960 "ext/date/lib/parse_iso_intervals.c" } #line 321 "ext/date/lib/parse_iso_intervals.re" diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index a2c976af7ed9c..ef57a7ee783f0 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2024 Derick Rethans + * Copyright (c) 2015-2025 Derick Rethans * Copyright (c) 2018,2021 MongoDB, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -30,9 +30,9 @@ # include "timelib_config.h" #endif -#define TIMELIB_VERSION 202212 -#define TIMELIB_EXTENDED_VERSION 20221201 -#define TIMELIB_ASCII_VERSION "2022.12" +#define TIMELIB_VERSION 202214 +#define TIMELIB_EXTENDED_VERSION 20221401 +#define TIMELIB_ASCII_VERSION "2022.14" #include #include From b42bd2a359d9c907de17e9f97cc35d6feb3309f0 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Mon, 15 Sep 2025 10:42:24 +0100 Subject: [PATCH 24/83] Update NEWS --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index ab450fa128e28..10c314bf4d352 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.2.30 +- Date: + . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. + (nielsdos) + - Opcache: . Reset global pointers to prevent use-after-free in zend_jit_status(). (Florian Engelhardt) From be5784dfec962cc92fd700667969f36fd4c17023 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Mon, 15 Sep 2025 14:16:24 +0100 Subject: [PATCH 25/83] Revert "Update timelib to 2022.14" This reverts commits: - a0329dbab009d984926f8817359ba5d83195d960 - b42bd2a359d9c907de17e9f97cc35d6feb3309f0 --- NEWS | 4 - ext/date/lib/parse_date.c | 213 ++++++++++++++--------------- ext/date/lib/parse_date.re | 5 +- ext/date/lib/parse_iso_intervals.c | 18 +-- ext/date/lib/timelib.h | 8 +- 5 files changed, 119 insertions(+), 129 deletions(-) diff --git a/NEWS b/NEWS index 10c314bf4d352..ab450fa128e28 100644 --- a/NEWS +++ b/NEWS @@ -2,10 +2,6 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.2.30 -- Date: - . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. - (nielsdos) - - Opcache: . Reset global pointers to prevent use-after-free in zend_jit_status(). (Florian Engelhardt) diff --git a/ext/date/lib/parse_date.c b/ext/date/lib/parse_date.c index d27511cad35e1..ea1602ef13b4e 100644 --- a/ext/date/lib/parse_date.c +++ b/ext/date/lib/parse_date.c @@ -1,4 +1,4 @@ -/* Generated by re2c 1.0.3 on Mon Sep 15 10:38:03 2025 */ +/* Generated by re2c 1.0.3 on Wed Sep 11 17:29:29 2024 */ #line 1 "ext/date/lib/parse_date.re" /* * The MIT License (MIT) @@ -941,12 +941,10 @@ timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int { timelib_tzinfo *res; timelib_long retval = 0; - size_t paren_count = 0; *tz_not_found = 0; while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') { - paren_count += **ptr == '('; ++*ptr; } if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) { @@ -995,9 +993,8 @@ timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int *tz_not_found = (found == 0); retval = offset; } - while (paren_count > 0 && **ptr == ')') { + while (**ptr == ')') { ++*ptr; - paren_count--; } return retval; } @@ -1021,11 +1018,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) std: s->tok = cursor; s->len = 0; -#line 1154 "ext/date/lib/parse_date.re" +#line 1151 "ext/date/lib/parse_date.re" -#line 1029 "ext/date/lib/parse_date.c" +#line 1026 "" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -1206,23 +1203,23 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(2, *YYCURSOR); ++YYCURSOR; YYDEBUG(3, *YYCURSOR); -#line 1987 "ext/date/lib/parse_date.re" +#line 1984 "ext/date/lib/parse_date.re" { s->pos = cursor; s->line++; goto std; } -#line 1215 "ext/date/lib/parse_date.c" +#line 1212 "" yy4: YYDEBUG(4, *YYCURSOR); ++YYCURSOR; yy5: YYDEBUG(5, *YYCURSOR); -#line 1993 "ext/date/lib/parse_date.re" +#line 1990 "ext/date/lib/parse_date.re" { add_error(s, TIMELIB_ERR_UNEXPECTED_CHARACTER, "Unexpected character"); goto std; } -#line 1226 "ext/date/lib/parse_date.c" +#line 1223 "" yy6: YYDEBUG(6, *YYCURSOR); yyaccept = 0; @@ -1237,11 +1234,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy58; yy8: YYDEBUG(8, *YYCURSOR); -#line 1982 "ext/date/lib/parse_date.re" +#line 1979 "ext/date/lib/parse_date.re" { goto std; } -#line 1245 "ext/date/lib/parse_date.c" +#line 1242 "" yy9: YYDEBUG(9, *YYCURSOR); yych = *++YYCURSOR; @@ -1275,11 +1272,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(11, *YYCURSOR); ++YYCURSOR; YYDEBUG(12, *YYCURSOR); -#line 1977 "ext/date/lib/parse_date.re" +#line 1974 "ext/date/lib/parse_date.re" { goto std; } -#line 1283 "ext/date/lib/parse_date.c" +#line 1280 "" yy13: YYDEBUG(13, *YYCURSOR); yyaccept = 1; @@ -1780,7 +1777,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy20: YYDEBUG(20, *YYCURSOR); -#line 1892 "ext/date/lib/parse_date.re" +#line 1889 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("tzcorrection | tz"); @@ -1794,7 +1791,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIMEZONE; } -#line 1798 "ext/date/lib/parse_date.c" +#line 1795 "" yy21: YYDEBUG(21, *YYCURSOR); yych = *++YYCURSOR; @@ -3599,7 +3596,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy81: YYDEBUG(81, *YYCURSOR); -#line 1639 "ext/date/lib/parse_date.re" +#line 1636 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datenoyearrev"); TIMELIB_INIT; @@ -3610,7 +3607,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 3614 "ext/date/lib/parse_date.c" +#line 3611 "" yy82: YYDEBUG(82, *YYCURSOR); yych = *++YYCURSOR; @@ -4125,7 +4122,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } if (yych == '.') goto yy289; YYDEBUG(114, *YYCURSOR); -#line 1214 "ext/date/lib/parse_date.re" +#line 1211 "ext/date/lib/parse_date.re" { timelib_ull i; @@ -4150,7 +4147,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 4154 "ext/date/lib/parse_date.c" +#line 4151 "" yy115: YYDEBUG(115, *YYCURSOR); ++YYCURSOR; @@ -5876,7 +5873,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy177: YYDEBUG(177, *YYCURSOR); -#line 1380 "ext/date/lib/parse_date.re" +#line 1377 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("timetiny24 | timeshort24 | timelong24 | iso8601long"); @@ -5903,7 +5900,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIME24_WITH_ZONE; } -#line 5907 "ext/date/lib/parse_date.c" +#line 5904 "" yy178: YYDEBUG(178, *YYCURSOR); yyaccept = 4; @@ -6932,7 +6929,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy224: YYDEBUG(224, *YYCURSOR); -#line 1474 "ext/date/lib/parse_date.re" +#line 1471 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("americanshort | american"); @@ -6947,7 +6944,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_AMERICAN; } -#line 6951 "ext/date/lib/parse_date.c" +#line 6948 "" yy225: YYDEBUG(225, *YYCURSOR); yyaccept = 5; @@ -7190,7 +7187,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy431; yy251: YYDEBUG(251, *YYCURSOR); -#line 1556 "ext/date/lib/parse_date.re" +#line 1553 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datefull"); @@ -7204,7 +7201,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_FULL; } -#line 7208 "ext/date/lib/parse_date.c" +#line 7205 "" yy252: YYDEBUG(252, *YYCURSOR); yyaccept = 3; @@ -7318,7 +7315,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych == 'e') goto yy440; yy260: YYDEBUG(260, *YYCURSOR); -#line 1961 "ext/date/lib/parse_date.re" +#line 1958 "ext/date/lib/parse_date.re" { timelib_ull i; DEBUG_OUTPUT("relative"); @@ -7333,7 +7330,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 7337 "ext/date/lib/parse_date.c" +#line 7334 "" yy261: YYDEBUG(261, *YYCURSOR); yych = *++YYCURSOR; @@ -7779,7 +7776,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy471; yy290: YYDEBUG(290, *YYCURSOR); -#line 1240 "ext/date/lib/parse_date.re" +#line 1237 "ext/date/lib/parse_date.re" { timelib_sll i; timelib_ull us; @@ -7818,7 +7815,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 7822 "ext/date/lib/parse_date.c" +#line 7819 "" yy291: YYDEBUG(291, *YYCURSOR); yych = *++YYCURSOR; @@ -7843,7 +7840,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy293: YYDEBUG(293, *YYCURSOR); -#line 1802 "ext/date/lib/parse_date.re" +#line 1799 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("ago"); TIMELIB_INIT; @@ -7863,7 +7860,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_AGO; } -#line 7867 "ext/date/lib/parse_date.c" +#line 7864 "" yy294: YYDEBUG(294, *YYCURSOR); yyaccept = 7; @@ -7902,7 +7899,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy295: YYDEBUG(295, *YYCURSOR); -#line 1882 "ext/date/lib/parse_date.re" +#line 1879 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("monthtext"); TIMELIB_INIT; @@ -7911,7 +7908,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 7915 "ext/date/lib/parse_date.c" +#line 7912 "" yy296: YYDEBUG(296, *YYCURSOR); yyaccept = 7; @@ -8486,7 +8483,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy315: YYDEBUG(315, *YYCURSOR); -#line 1823 "ext/date/lib/parse_date.re" +#line 1820 "ext/date/lib/parse_date.re" { const timelib_relunit* relunit; DEBUG_OUTPUT("daytext"); @@ -8503,7 +8500,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_WEEKDAY; } -#line 8507 "ext/date/lib/parse_date.c" +#line 8504 "" yy316: YYDEBUG(316, *YYCURSOR); yych = *++YYCURSOR; @@ -8771,7 +8768,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy325: YYDEBUG(325, *YYCURSOR); -#line 1625 "ext/date/lib/parse_date.re" +#line 1622 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datetextual | datenoyear"); @@ -8784,7 +8781,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 8788 "ext/date/lib/parse_date.c" +#line 8785 "" yy326: YYDEBUG(326, *YYCURSOR); yyaccept = 10; @@ -9478,7 +9475,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy351: YYDEBUG(351, *YYCURSOR); -#line 1171 "ext/date/lib/parse_date.re" +#line 1168 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("now"); TIMELIB_INIT; @@ -9486,7 +9483,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 9490 "ext/date/lib/parse_date.c" +#line 9487 "" yy352: YYDEBUG(352, *YYCURSOR); yyaccept = 2; @@ -10989,7 +10986,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy420: YYDEBUG(420, *YYCURSOR); -#line 1408 "ext/date/lib/parse_date.re" +#line 1405 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("gnunocolon"); TIMELIB_INIT; @@ -11011,7 +11008,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_GNU_NOCOLON; } -#line 11015 "ext/date/lib/parse_date.c" +#line 11012 "" yy421: YYDEBUG(421, *YYCURSOR); yyaccept = 13; @@ -11092,7 +11089,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy422: YYDEBUG(422, *YYCURSOR); -#line 1793 "ext/date/lib/parse_date.re" +#line 1790 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("year4"); TIMELIB_INIT; @@ -11100,7 +11097,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_CLF; } -#line 11104 "ext/date/lib/parse_date.c" +#line 11101 "" yy423: YYDEBUG(423, *YYCURSOR); yyaccept = 3; @@ -11707,7 +11704,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(456, *YYCURSOR); ++YYCURSOR; YYDEBUG(457, *YYCURSOR); -#line 1342 "ext/date/lib/parse_date.re" +#line 1339 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12"); TIMELIB_INIT; @@ -11724,7 +11721,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIME12; } -#line 11728 "ext/date/lib/parse_date.c" +#line 11725 "" yy458: YYDEBUG(458, *YYCURSOR); yych = *++YYCURSOR; @@ -13051,7 +13048,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy526: YYDEBUG(526, *YYCURSOR); -#line 1180 "ext/date/lib/parse_date.re" +#line 1177 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("noon"); TIMELIB_INIT; @@ -13062,7 +13059,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 13066 "ext/date/lib/parse_date.c" +#line 13063 "" yy527: YYDEBUG(527, *YYCURSOR); yyaccept = 2; @@ -14108,7 +14105,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy567: YYDEBUG(567, *YYCURSOR); -#line 1542 "ext/date/lib/parse_date.re" +#line 1539 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("gnudateshort"); @@ -14121,7 +14118,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 14125 "ext/date/lib/parse_date.c" +#line 14122 "" yy568: YYDEBUG(568, *YYCURSOR); yyaccept = 15; @@ -14572,7 +14569,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy600: YYDEBUG(600, *YYCURSOR); -#line 1611 "ext/date/lib/parse_date.re" +#line 1608 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datenodayrev"); @@ -14585,7 +14582,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_NO_DAY; } -#line 14589 "ext/date/lib/parse_date.c" +#line 14586 "" yy601: YYDEBUG(601, *YYCURSOR); yych = *++YYCURSOR; @@ -15960,7 +15957,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(696, *YYCURSOR); ++YYCURSOR; YYDEBUG(697, *YYCURSOR); -#line 1597 "ext/date/lib/parse_date.re" +#line 1594 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datenoday"); @@ -15973,7 +15970,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_NO_DAY; } -#line 15977 "ext/date/lib/parse_date.c" +#line 15974 "" yy698: YYDEBUG(698, *YYCURSOR); yych = *++YYCURSOR; @@ -16534,7 +16531,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy722: YYDEBUG(722, *YYCURSOR); -#line 1192 "ext/date/lib/parse_date.re" +#line 1189 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("midnight | today"); TIMELIB_INIT; @@ -16543,7 +16540,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 16547 "ext/date/lib/parse_date.c" +#line 16544 "" yy723: YYDEBUG(723, *YYCURSOR); yych = *++YYCURSOR; @@ -16853,7 +16850,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy897; yy739: YYDEBUG(739, *YYCURSOR); -#line 1583 "ext/date/lib/parse_date.re" +#line 1580 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pointed date YY"); @@ -16866,7 +16863,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_FULL_POINTED; } -#line 16870 "ext/date/lib/parse_date.c" +#line 16867 "" yy740: YYDEBUG(740, *YYCURSOR); yyaccept = 15; @@ -16978,7 +16975,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy752: YYDEBUG(752, *YYCURSOR); -#line 1528 "ext/date/lib/parse_date.re" +#line 1525 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("gnudateshorter"); @@ -16991,7 +16988,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 16995 "ext/date/lib/parse_date.c" +#line 16992 "" yy753: YYDEBUG(753, *YYCURSOR); yyaccept = 18; @@ -17240,7 +17237,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy777: YYDEBUG(777, *YYCURSOR); -#line 1454 "ext/date/lib/parse_date.re" +#line 1451 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("iso8601nocolon"); @@ -17259,7 +17256,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_NOCOLON; } -#line 17263 "ext/date/lib/parse_date.c" +#line 17260 "" yy778: YYDEBUG(778, *YYCURSOR); yyaccept = 19; @@ -18487,7 +18484,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy849: YYDEBUG(849, *YYCURSOR); -#line 1931 "ext/date/lib/parse_date.re" +#line 1928 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz"); @@ -18516,7 +18513,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_SHORTDATE_WITH_TIME; } -#line 18520 "ext/date/lib/parse_date.c" +#line 18517 "" yy850: YYDEBUG(850, *YYCURSOR); yyaccept = 20; @@ -19560,7 +19557,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy926: YYDEBUG(926, *YYCURSOR); -#line 1689 "ext/date/lib/parse_date.re" +#line 1686 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pgydotd"); @@ -19573,7 +19570,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_PG_YEARDAY; } -#line 19577 "ext/date/lib/parse_date.c" +#line 19574 "" yy927: YYDEBUG(927, *YYCURSOR); yyaccept = 21; @@ -19827,7 +19824,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '7') goto yy1059; yy942: YYDEBUG(942, *YYCURSOR); -#line 1722 "ext/date/lib/parse_date.re" +#line 1719 "ext/date/lib/parse_date.re" { timelib_sll w, d; DEBUG_OUTPUT("isoweek"); @@ -19845,7 +19842,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_WEEK; } -#line 19849 "ext/date/lib/parse_date.c" +#line 19846 "" yy943: YYDEBUG(943, *YYCURSOR); yych = *++YYCURSOR; @@ -20321,7 +20318,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych == 'e') goto yy1094; yy982: YYDEBUG(982, *YYCURSOR); -#line 1865 "ext/date/lib/parse_date.re" +#line 1862 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -20337,7 +20334,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 20341 "ext/date/lib/parse_date.c" +#line 20338 "" yy983: YYDEBUG(983, *YYCURSOR); yych = *++YYCURSOR; @@ -20684,7 +20681,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1020, *YYCURSOR); ++YYCURSOR; YYDEBUG(1021, *YYCURSOR); -#line 1571 "ext/date/lib/parse_date.re" +#line 1568 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("pointed date YYYY"); TIMELIB_INIT; @@ -20695,7 +20692,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_FULL_POINTED; } -#line 20699 "ext/date/lib/parse_date.c" +#line 20696 "" yy1022: YYDEBUG(1022, *YYCURSOR); ++YYCURSOR; @@ -20724,7 +20721,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1025: YYDEBUG(1025, *YYCURSOR); -#line 1502 "ext/date/lib/parse_date.re" +#line 1499 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("iso8601date2"); @@ -20737,7 +20734,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 20741 "ext/date/lib/parse_date.c" +#line 20738 "" yy1026: YYDEBUG(1026, *YYCURSOR); yyaccept = 15; @@ -20957,7 +20954,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1043: YYDEBUG(1043, *YYCURSOR); -#line 1490 "ext/date/lib/parse_date.re" +#line 1487 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash"); TIMELIB_INIT; @@ -20968,7 +20965,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 20972 "ext/date/lib/parse_date.c" +#line 20969 "" yy1044: YYDEBUG(1044, *YYCURSOR); yyaccept = 26; @@ -21083,7 +21080,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1048: YYDEBUG(1048, *YYCURSOR); -#line 1651 "ext/date/lib/parse_date.re" +#line 1648 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datenocolon"); TIMELIB_INIT; @@ -21094,7 +21091,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_NOCOLON; } -#line 21098 "ext/date/lib/parse_date.c" +#line 21095 "" yy1049: YYDEBUG(1049, *YYCURSOR); yych = *++YYCURSOR; @@ -21164,7 +21161,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1059, *YYCURSOR); ++YYCURSOR; YYDEBUG(1060, *YYCURSOR); -#line 1703 "ext/date/lib/parse_date.re" +#line 1700 "ext/date/lib/parse_date.re" { timelib_sll w, d; DEBUG_OUTPUT("isoweekday"); @@ -21182,7 +21179,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_WEEK; } -#line 21186 "ext/date/lib/parse_date.c" +#line 21183 "" yy1061: YYDEBUG(1061, *YYCURSOR); yych = *++YYCURSOR; @@ -21245,7 +21242,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy1143; yy1070: YYDEBUG(1070, *YYCURSOR); -#line 1741 "ext/date/lib/parse_date.re" +#line 1738 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pgtextshort"); @@ -21258,7 +21255,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_PG_TEXT; } -#line 21262 "ext/date/lib/parse_date.c" +#line 21259 "" yy1071: YYDEBUG(1071, *YYCURSOR); yych = *++YYCURSOR; @@ -21731,7 +21728,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) ++YYCURSOR; yy1107: YYDEBUG(1107, *YYCURSOR); -#line 1202 "ext/date/lib/parse_date.re" +#line 1199 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("tomorrow"); TIMELIB_INIT; @@ -21742,7 +21739,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 21746 "ext/date/lib/parse_date.c" +#line 21743 "" yy1108: YYDEBUG(1108, *YYCURSOR); yyaccept = 28; @@ -22079,7 +22076,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1140, *YYCURSOR); ++YYCURSOR; YYDEBUG(1141, *YYCURSOR); -#line 1755 "ext/date/lib/parse_date.re" +#line 1752 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pgtextreverse"); @@ -22092,7 +22089,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_PG_TEXT; } -#line 22096 "ext/date/lib/parse_date.c" +#line 22093 "" yy1142: YYDEBUG(1142, *YYCURSOR); ++YYCURSOR; @@ -22136,7 +22133,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1145: YYDEBUG(1145, *YYCURSOR); -#line 1297 "ext/date/lib/parse_date.re" +#line 1294 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("backof | frontof"); TIMELIB_INIT; @@ -22158,7 +22155,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_LF_DAY_OF_MONTH; } -#line 22162 "ext/date/lib/parse_date.c" +#line 22159 "" yy1146: YYDEBUG(1146, *YYCURSOR); yyaccept = 29; @@ -22482,7 +22479,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1172: YYDEBUG(1172, *YYCURSOR); -#line 1841 "ext/date/lib/parse_date.re" +#line 1838 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -22505,7 +22502,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 22509 "ext/date/lib/parse_date.c" +#line 22506 "" yy1173: YYDEBUG(1173, *YYCURSOR); yych = *++YYCURSOR; @@ -22517,7 +22514,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) ++YYCURSOR; yy1175: YYDEBUG(1175, *YYCURSOR); -#line 1159 "ext/date/lib/parse_date.re" +#line 1156 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("yesterday"); TIMELIB_INIT; @@ -22528,7 +22525,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 22532 "ext/date/lib/parse_date.c" +#line 22529 "" yy1176: YYDEBUG(1176, *YYCURSOR); yyaccept = 31; @@ -23021,7 +23018,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1222, *YYCURSOR); ++YYCURSOR; YYDEBUG(1223, *YYCURSOR); -#line 1907 "ext/date/lib/parse_date.re" +#line 1904 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12"); TIMELIB_INIT; @@ -23044,7 +23041,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_SHORTDATE_WITH_TIME; } -#line 23048 "ext/date/lib/parse_date.c" +#line 23045 "" yy1224: YYDEBUG(1224, *YYCURSOR); yych = *++YYCURSOR; @@ -23546,7 +23543,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1268, *YYCURSOR); ++YYCURSOR; YYDEBUG(1269, *YYCURSOR); -#line 1320 "ext/date/lib/parse_date.re" +#line 1317 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -23567,7 +23564,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_WEEK_DAY_OF_MONTH; } -#line 23571 "ext/date/lib/parse_date.c" +#line 23568 "" yy1270: YYDEBUG(1270, *YYCURSOR); yyaccept = 24; @@ -23614,7 +23611,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1273, *YYCURSOR); ++YYCURSOR; YYDEBUG(1274, *YYCURSOR); -#line 1280 "ext/date/lib/parse_date.re" +#line 1277 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("firstdayof | lastdayof"); TIMELIB_INIT; @@ -23630,12 +23627,12 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_LF_DAY_OF_MONTH; } -#line 23634 "ext/date/lib/parse_date.c" +#line 23631 "" yy1275: YYDEBUG(1275, *YYCURSOR); ++YYCURSOR; YYDEBUG(1276, *YYCURSOR); -#line 1516 "ext/date/lib/parse_date.re" +#line 1513 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("iso8601datex"); TIMELIB_INIT; @@ -23646,7 +23643,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 23650 "ext/date/lib/parse_date.c" +#line 23647 "" yy1277: YYDEBUG(1277, *YYCURSOR); yych = *++YYCURSOR; @@ -23749,7 +23746,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1290, *YYCURSOR); ++YYCURSOR; YYDEBUG(1291, *YYCURSOR); -#line 1360 "ext/date/lib/parse_date.re" +#line 1357 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("mssqltime"); TIMELIB_INIT; @@ -23768,7 +23765,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIME24_WITH_ZONE; } -#line 23772 "ext/date/lib/parse_date.c" +#line 23769 "" yy1292: YYDEBUG(1292, *YYCURSOR); yych = *++YYCURSOR; @@ -24192,7 +24189,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy1331; yy1329: YYDEBUG(1329, *YYCURSOR); -#line 1663 "ext/date/lib/parse_date.re" +#line 1660 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif"); @@ -24217,7 +24214,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_XMLRPC_SOAP; } -#line 24221 "ext/date/lib/parse_date.c" +#line 24218 "" yy1330: YYDEBUG(1330, *YYCURSOR); yych = *++YYCURSOR; @@ -24587,7 +24584,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= ':') goto yy1383; yy1375: YYDEBUG(1375, *YYCURSOR); -#line 1769 "ext/date/lib/parse_date.re" +#line 1766 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("clf"); @@ -24610,7 +24607,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_CLF; } -#line 24614 "ext/date/lib/parse_date.c" +#line 24611 "" yy1376: YYDEBUG(1376, *YYCURSOR); yyaccept = 33; @@ -24842,7 +24839,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych == ':') goto yy1286; goto yy1329; } -#line 1997 "ext/date/lib/parse_date.re" +#line 1994 "ext/date/lib/parse_date.re" } diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re index c40a5c07c9aa1..d32be9bfe7be7 100644 --- a/ext/date/lib/parse_date.re +++ b/ext/date/lib/parse_date.re @@ -939,12 +939,10 @@ timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int { timelib_tzinfo *res; timelib_long retval = 0; - size_t paren_count = 0; *tz_not_found = 0; while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') { - paren_count += **ptr == '('; ++*ptr; } if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) { @@ -993,9 +991,8 @@ timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int *tz_not_found = (found == 0); retval = offset; } - while (paren_count > 0 && **ptr == ')') { + while (**ptr == ')') { ++*ptr; - paren_count--; } return retval; } diff --git a/ext/date/lib/parse_iso_intervals.c b/ext/date/lib/parse_iso_intervals.c index a9fca1b609413..cdc329431ec45 100644 --- a/ext/date/lib/parse_iso_intervals.c +++ b/ext/date/lib/parse_iso_intervals.c @@ -1,4 +1,4 @@ -/* Generated by re2c 1.0.3 on Mon Sep 15 10:38:16 2025 */ +/* Generated by re2c 1.0.3 on Wed Sep 11 17:29:40 2024 */ #line 1 "ext/date/lib/parse_iso_intervals.re" /* * The MIT License (MIT) @@ -180,7 +180,7 @@ static int scan(Scanner *s) -#line 184 "ext/date/lib/parse_iso_intervals.c" +#line 184 "" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -252,7 +252,7 @@ static int scan(Scanner *s) s->pos = cursor; s->line++; goto std; } -#line 256 "ext/date/lib/parse_iso_intervals.c" +#line 256 "" yy4: YYDEBUG(4, *YYCURSOR); ++YYCURSOR; @@ -263,7 +263,7 @@ static int scan(Scanner *s) add_error(s, "Unexpected character"); goto std; } -#line 267 "ext/date/lib/parse_iso_intervals.c" +#line 267 "" yy6: YYDEBUG(6, *YYCURSOR); ++YYCURSOR; @@ -272,7 +272,7 @@ static int scan(Scanner *s) { goto std; } -#line 276 "ext/date/lib/parse_iso_intervals.c" +#line 276 "" yy8: YYDEBUG(8, *YYCURSOR); yyaccept = 0; @@ -330,7 +330,7 @@ static int scan(Scanner *s) TIMELIB_DEINIT; return TIMELIB_PERIOD; } -#line 334 "ext/date/lib/parse_iso_intervals.c" +#line 334 "" yy11: YYDEBUG(11, *YYCURSOR); yych = *++YYCURSOR; @@ -399,7 +399,7 @@ static int scan(Scanner *s) s->have_recurrences = 1; return TIMELIB_PERIOD; } -#line 403 "ext/date/lib/parse_iso_intervals.c" +#line 403 "" yy19: YYDEBUG(19, *YYCURSOR); yych = *++YYCURSOR; @@ -917,7 +917,7 @@ static int scan(Scanner *s) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 921 "ext/date/lib/parse_iso_intervals.c" +#line 921 "" yy91: YYDEBUG(91, *YYCURSOR); yych = *++YYCURSOR; @@ -956,7 +956,7 @@ static int scan(Scanner *s) TIMELIB_DEINIT; return TIMELIB_PERIOD; } -#line 960 "ext/date/lib/parse_iso_intervals.c" +#line 960 "" } #line 321 "ext/date/lib/parse_iso_intervals.re" diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index ef57a7ee783f0..a2c976af7ed9c 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2025 Derick Rethans + * Copyright (c) 2015-2024 Derick Rethans * Copyright (c) 2018,2021 MongoDB, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -30,9 +30,9 @@ # include "timelib_config.h" #endif -#define TIMELIB_VERSION 202214 -#define TIMELIB_EXTENDED_VERSION 20221401 -#define TIMELIB_ASCII_VERSION "2022.14" +#define TIMELIB_VERSION 202212 +#define TIMELIB_EXTENDED_VERSION 20221201 +#define TIMELIB_ASCII_VERSION "2022.12" #include #include From f045716288af47499fe5c87104e0d3db0570072d Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 15 Sep 2025 22:07:38 +0200 Subject: [PATCH 26/83] Fix incorrect HASH_FLAG_HAS_EMPTY_IND flag on userland array Fixes GH-19839 Closes GH-19851 --- NEWS | 2 ++ Zend/tests/gh19839.phpt | 18 ++++++++++++++++++ Zend/zend_hash.c | 5 ++++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/gh19839.phpt diff --git a/NEWS b/NEWS index 028617dfae40f..0891acce266bf 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,8 @@ PHP NEWS exception are triggered). (nielsdos) . Fixed bug GH-19653 (Closure named argument unpacking between temporary closures can cause a crash). (nielsdos, Arnaud, Bob) + . Fixed bug GH-19839 (Incorrect HASH_FLAG_HAS_EMPTY_IND flag on userland + array). (ilutov) - Date: . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. diff --git a/Zend/tests/gh19839.phpt b/Zend/tests/gh19839.phpt new file mode 100644 index 0000000000000..cc589ce0605f1 --- /dev/null +++ b/Zend/tests/gh19839.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-19839: Incorrect HASH_FLAG_HAS_EMPTY_IND flag on userland array +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 07d5bed6d7655..fdc05ff8cbfae 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -2466,6 +2466,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) target->nTableSize = HT_MIN_SIZE; HT_SET_DATA_ADDR(target, &uninitialized_bucket); } else if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) { + ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_HAS_EMPTY_IND)); HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK; target->nTableMask = source->nTableMask; target->nNumUsed = source->nNumUsed; @@ -2482,6 +2483,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) memcpy(HT_GET_DATA_ADDR(target), HT_GET_DATA_ADDR(source), HT_USED_SIZE(source)); } } else if (HT_IS_PACKED(source)) { + ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_HAS_EMPTY_IND)); HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK; target->nTableMask = HT_MIN_MASK; target->nNumUsed = source->nNumUsed; @@ -2501,7 +2503,8 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) zend_array_dup_packed_elements(source, target, 1); } } else { - HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK; + /* Indirects are removed during duplication, remove HASH_FLAG_HAS_EMPTY_IND accordingly. */ + HT_FLAGS(target) = HT_FLAGS(source) & (HASH_FLAG_MASK & ~HASH_FLAG_HAS_EMPTY_IND); target->nTableMask = source->nTableMask; target->nNextFreeElement = source->nNextFreeElement; target->nInternalPointer = From 75217c16c89639fe3b01e43d1cdc4a701063347d Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com> Date: Tue, 16 Sep 2025 15:05:45 +0200 Subject: [PATCH 27/83] Fix GH-19801: address leak when calling var_dump() with recursion in __debugInfo() (#19837) --- NEWS | 2 ++ .../debug_zval_dump_gh19801_memory_leak.phpt | 32 +++++++++++++++++++ .../var_dump_gh19801_memory_leak.phpt | 30 +++++++++++++++++ ext/standard/var.c | 4 +-- 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/general_functions/debug_zval_dump_gh19801_memory_leak.phpt create mode 100644 ext/standard/tests/general_functions/var_dump_gh19801_memory_leak.phpt diff --git a/NEWS b/NEWS index 0891acce266bf..b3cdd047e509f 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,8 @@ PHP NEWS . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). (nielsdos) . Fixed bug GH-19701 (Serialize/deserialize loses some data). (nielsdos) + . Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()). + (alexandre-daubois) - Zip: . Fixed bug GH-19688 (Remove pattern overflow in zip addGlob()). (nielsdos) diff --git a/ext/standard/tests/general_functions/debug_zval_dump_gh19801_memory_leak.phpt b/ext/standard/tests/general_functions/debug_zval_dump_gh19801_memory_leak.phpt new file mode 100644 index 0000000000000..8d65cd6d18587 --- /dev/null +++ b/ext/standard/tests/general_functions/debug_zval_dump_gh19801_memory_leak.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-19801 (debug_zval_dump() leak with __debugInfo() that modifies circular references) +--FILE-- +a = null; + gc_collect_cycles(); + return []; + } + }, +]; + +$b = new stdClass; +$b->a = &$a; + +debug_zval_dump($b); +?> +--EXPECTF-- +object(stdClass)#2 (1) refcount(%d){ + ["a"]=> + reference refcount(%d) { + array(1) refcount(%d){ + [0]=> + object(class@anonymous)#1 (0) refcount(%d){ + } + } + } +} diff --git a/ext/standard/tests/general_functions/var_dump_gh19801_memory_leak.phpt b/ext/standard/tests/general_functions/var_dump_gh19801_memory_leak.phpt new file mode 100644 index 0000000000000..f0522916de4e6 --- /dev/null +++ b/ext/standard/tests/general_functions/var_dump_gh19801_memory_leak.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-19801 (var_dump() memory leak with __debugInfo() that modifies circular references) +--FILE-- +a = null; + gc_collect_cycles(); + return []; + } + }, +]; + +$b = new stdClass; +$b->a = &$a; + +var_dump($b); +?> +--EXPECTF-- +object(stdClass)#2 (1) { + ["a"]=> + &array(1) { + [0]=> + object(class@anonymous)#1 (0) { + } + } +} diff --git a/ext/standard/var.c b/ext/standard/var.c index c6e280d15ab93..795fb366c6013 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -139,7 +139,7 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */ } ZEND_HASH_FOREACH_END(); if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) { GC_UNPROTECT_RECURSION(myht); - GC_DELREF(myht); + GC_DTOR_NO_REF(myht); } if (level > 1) { php_printf("%*c", level-1, ' '); @@ -336,7 +336,7 @@ PHPAPI void php_debug_zval_dump(zval *struc, int level) /* {{{ */ } ZEND_HASH_FOREACH_END(); if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) { GC_UNPROTECT_RECURSION(myht); - GC_DELREF(myht); + GC_DTOR_NO_REF(myht); } if (level > 1) { php_printf("%*c", level - 1, ' '); From 6eb3faef3bc1df238b824630dbdcd95c27ddb2f8 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 16 Sep 2025 13:39:35 +0200 Subject: [PATCH 28/83] Fix use-of-uninitialized-value in zend_get_arg_offset_by_name() Don't access fbc->op_array.refcount on internal function. Don't attempt to cache ZEND_ACC_USER_ARG_INFO at all, which is only used in zend_get_closure_invoke_method(). This may reuse arg_info from a temporary closure, and hence caching would also be unsafe. Also avoid populating the cache slot for variadic parameters, where the ZEND_ACC_USER_ARG_INFO is set for the same reason. Closes GH-19856 --- Zend/zend_execute.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4e6339ca901cb..d44af38c64c63 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -5072,9 +5072,9 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) || EXPECTED(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { for (uint32_t i = 0; i < num_args; i++) { - zend_arg_info *arg_info = &fbc->op_array.arg_info[i]; + zend_arg_info *arg_info = &fbc->common.arg_info[i]; if (zend_string_equals(arg_name, arg_info->name)) { - if (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)) { + if (fbc->type == ZEND_USER_FUNCTION && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) { *cache_slot = unique_id; *(uintptr_t *)(cache_slot + 1) = i; } @@ -5094,7 +5094,10 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( } if (fbc->common.fn_flags & ZEND_ACC_VARIADIC) { - if (fbc->type == ZEND_INTERNAL_FUNCTION || !fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)) { + if ((fbc->type == ZEND_USER_FUNCTION + && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) + || (fbc->type == ZEND_INTERNAL_FUNCTION + && !(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO))) { *cache_slot = unique_id; *(uintptr_t *)(cache_slot + 1) = fbc->common.num_args; } From 7a1bb711272540acc880215c51d818cd0c4f61b4 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 15 Aug 2025 11:06:53 +0200 Subject: [PATCH 29/83] Fix GH-19480: error_log php.ini cannot be unset when open_basedir is configured Since the ini message handlers already check for basedir, we need to drop the basedir check from ini_set. Then we also fix the exceptional case for the empty string: it should bypass the basedir check. Furthermore, there was a regression introduced with the error_log "syslog" check in ddfe269a (inverted check), so we fix that as well. Closes GH-19487 --- NEWS | 2 ++ ext/standard/basic_functions.c | 4 +--- main/main.c | 12 +++++++----- tests/security/error_log_special_values.phpt | 13 +++++++++++++ 4 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 tests/security/error_log_special_values.phpt diff --git a/NEWS b/NEWS index b3cdd047e509f..ace911f500c44 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,8 @@ PHP NEWS closures can cause a crash). (nielsdos, Arnaud, Bob) . Fixed bug GH-19839 (Incorrect HASH_FLAG_HAS_EMPTY_IND flag on userland array). (ilutov) + . Fixed bug GH-19480 (error_log php.ini cannot be unset when open_basedir is + configured). (nielsdos) - Date: . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index d2a99fccc95fe..02b63beb59d03 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -2037,10 +2037,8 @@ PHP_FUNCTION(ini_set) #define _CHECK_PATH(var, var_len, ini) php_ini_check_path(var, var_len, ini, sizeof(ini)) /* open basedir check */ if (PG(open_basedir)) { - if (_CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "error_log") || - _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.class.path") || + if (_CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.class.path") || _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.home") || - _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "mail.log") || _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.library.path") || _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "vpopmail.directory")) { if (php_check_open_basedir(ZSTR_VAL(new_value_str))) { diff --git a/main/main.c b/main/main.c index 60e970b76ad76..e7797a4f89e4a 100644 --- a/main/main.c +++ b/main/main.c @@ -625,12 +625,13 @@ static PHP_INI_MH(OnUpdateErrorLog) { /* Only do the safemode/open_basedir check at runtime */ if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && - new_value && zend_string_equals_literal(new_value, "syslog")) { + new_value && !zend_string_equals_literal(new_value, "syslog") && ZSTR_LEN(new_value) > 0) { if (PG(open_basedir) && php_check_open_basedir(ZSTR_VAL(new_value))) { return FAILURE; } } - OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); + char **p = (char **) ZEND_INI_GET_ADDR(); + *p = new_value && ZSTR_LEN(new_value) > 0 ? ZSTR_VAL(new_value) : NULL; return SUCCESS; } /* }}} */ @@ -638,13 +639,14 @@ static PHP_INI_MH(OnUpdateErrorLog) /* {{{ PHP_INI_MH */ static PHP_INI_MH(OnUpdateMailLog) { - /* Only do the safemode/open_basedir check at runtime */ - if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && new_value) { + /* Only do the open_basedir check at runtime */ + if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && new_value && ZSTR_LEN(new_value) > 0) { if (PG(open_basedir) && php_check_open_basedir(ZSTR_VAL(new_value))) { return FAILURE; } } - OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); + char **p = (char **) ZEND_INI_GET_ADDR(); + *p = new_value && ZSTR_LEN(new_value) > 0 ? ZSTR_VAL(new_value) : NULL; return SUCCESS; } /* }}} */ diff --git a/tests/security/error_log_special_values.phpt b/tests/security/error_log_special_values.phpt new file mode 100644 index 0000000000000..949adb5e2538a --- /dev/null +++ b/tests/security/error_log_special_values.phpt @@ -0,0 +1,13 @@ +--TEST-- +Setting error_log to special values with open_basedir enabled +--INI-- +open_basedir=foo +error_log= +--FILE-- + +--EXPECT-- +string(0) "" +string(6) "syslog" From 933e087843a29f8b1d5c2a62c3081ec04ada5933 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 19 Sep 2025 12:29:46 +0100 Subject: [PATCH 30/83] Fix GH-19885: dba_fetch() overflow on skip argument. close GH-19887 --- NEWS | 3 +++ ext/dba/dba.c | 5 +++++ ext/dba/tests/gh19885.phpt | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 ext/dba/tests/gh19885.phpt diff --git a/NEWS b/NEWS index ace911f500c44..fc3c0662b9328 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,9 @@ PHP NEWS . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. (nielsdos) +- DBA: + . Fixed GH-19885 (dba_fetch() overflow on skip argument). (David Carlier) + - Curl: . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead of the curl_copy_handle() function to clone a CurlHandle. (timwolla) diff --git a/ext/dba/dba.c b/ext/dba/dba.c index 9affb5aa6fc19..1474573f3e4de 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -984,6 +984,11 @@ PHP_FUNCTION(dba_fetch) ZEND_PARSE_PARAMETERS_END(); } + if (ZEND_LONG_EXCEEDS_INT(skip)) { + zend_argument_value_error(3, "must be between %d and %d", INT_MIN, INT_MAX); + RETURN_THROWS(); + } + DBA_FETCH_RESOURCE(info, id); if (key_ht) { diff --git a/ext/dba/tests/gh19885.phpt b/ext/dba/tests/gh19885.phpt new file mode 100644 index 0000000000000..987aea4f175a2 --- /dev/null +++ b/ext/dba/tests/gh19885.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-19885 (dba_fetch() segfault on large skip values) +--EXTENSIONS-- +dba +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +try { + dba_fetch("https://redirect.github.com/1", $db, PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} +// negative skip needs to remain acceptable albeit corrected down the line +var_dump(dba_fetch("https://redirect.github.com/1", $db, -1000000)); +?> +--EXPECTF-- +dba_fetch(): Argument #3 ($skip) must be between -%d and %d +dba_fetch(): Argument #3 ($skip) must be between -%d and %d + +Notice: dba_fetch(): Handler cdb accepts only skip values greater than or equal to zero, using skip=0 in %s on line %d +string(1) "1" From 266cb7d892d65e1ddcacc643853897033558a5d1 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 22 Sep 2025 10:55:59 +0200 Subject: [PATCH 31/83] Fix curl 8.16.0 compilation with zts --- ext/curl/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 67ec5aabc3911..2f0181504198b 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1178,7 +1178,7 @@ static void _php_curl_set_default_options(php_curl *ch) } #ifdef ZTS - curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1L); #endif } /* }}} */ From 32c919b474b19c1a0c23510b427cc4c50f8d5347 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 15 Sep 2025 11:30:23 +0200 Subject: [PATCH 32/83] Handle references after FETCH_OBJ_R with REG destination zend_jit_fetch_obj_r_slow_ex() may be used by the function JIT, which doesn't rely on guards to handle references. Therefore it must deref the property value. Other variants of zend_jit_fetch_obj_*_slow_ex can not be used used in function JIT. Fixes GH-19831 Closes GH-19838 --- NEWS | 1 + ext/opcache/jit/zend_jit_helpers.c | 9 ++++-- ext/opcache/tests/jit/gh19831_001.phpt | 33 ++++++++++++++++++++++ ext/opcache/tests/jit/gh19831_002.phpt | 39 ++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 ext/opcache/tests/jit/gh19831_001.phpt create mode 100644 ext/opcache/tests/jit/gh19831_002.phpt diff --git a/NEWS b/NEWS index d3a0da9d242a9..be73b28b8223e 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,7 @@ PHP NEWS - Opcache: . Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex). (Arnaud) + . Fixed bug GH-19831 (function JIT may not deref property value). (Arnaud) - Soap: . Fixed bug GH-19784 (SoapServer memory leak). (nielsdos) diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 0f5e1b11c3938..a98b9ebc77606 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1965,8 +1965,13 @@ static zval* ZEND_FASTCALL zend_jit_fetch_obj_r_slow_ex(zend_object *zobj) void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS); retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, result); - if (retval == result && UNEXPECTED(Z_ISREF_P(retval))) { - zend_unwrap_reference(retval); + if (UNEXPECTED(Z_ISREF_P(retval))) { + if (retval == result) { + zend_unwrap_reference(retval); + } else { + retval = Z_REFVAL_P(retval); + } + ZEND_ASSERT(!Z_REFCOUNTED_P(retval)); } return retval; } diff --git a/ext/opcache/tests/jit/gh19831_001.phpt b/ext/opcache/tests/jit/gh19831_001.phpt new file mode 100644 index 0000000000000..c83ca6daa50d5 --- /dev/null +++ b/ext/opcache/tests/jit/gh19831_001.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-19831 001: fetch obj slow R REG + reference +--CREDITS-- +dktapps +--ENV-- +RT_COND=1 +--INI-- +opcache.jit=1203 +--FILE-- +layers; + } +} + +$t = new Test(); +$a = &$t->layers; +var_dump($t->getLayers()); + +?> +--EXPECT-- +int(1) diff --git a/ext/opcache/tests/jit/gh19831_002.phpt b/ext/opcache/tests/jit/gh19831_002.phpt new file mode 100644 index 0000000000000..25b596a3decb3 --- /dev/null +++ b/ext/opcache/tests/jit/gh19831_002.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-19831 002: fetch obj slow R REG + __get + reference +--CREDITS-- +dktapps +--ENV-- +RT_COND=1 +--INI-- +opcache.jit=1203 +--FILE-- +layers; + } +} + +$t = new Test(); +unset($t->layers); +var_dump($t->getLayers()); + +?> +--EXPECT-- +int(1) From ef202cc4b783e8cef681ea08445014d11db6edf7 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 22 Sep 2025 19:31:06 +0300 Subject: [PATCH 33/83] Update IR IR commit: 503018483d8333a3cfb25ab89a1eadefbee665bc --- ext/opcache/jit/ir/LICENSE | 1 + ext/opcache/jit/ir/ir.c | 108 +++++----- ext/opcache/jit/ir/ir.h | 19 +- ext/opcache/jit/ir/ir_aarch64.dasc | 137 +++++++++++-- ext/opcache/jit/ir/ir_builder.h | 2 + ext/opcache/jit/ir/ir_cfg.c | 77 +++----- ext/opcache/jit/ir/ir_dump.c | 6 + ext/opcache/jit/ir/ir_emit.c | 20 +- ext/opcache/jit/ir/ir_fold.h | 13 +- ext/opcache/jit/ir/ir_gcm.c | 168 ++++++---------- ext/opcache/jit/ir/ir_ra.c | 8 +- ext/opcache/jit/ir/ir_save.c | 50 +++-- ext/opcache/jit/ir/ir_sccp.c | 8 +- ext/opcache/jit/ir/ir_x86.dasc | 305 ++++++++++++++++++++++++++--- 14 files changed, 624 insertions(+), 298 deletions(-) diff --git a/ext/opcache/jit/ir/LICENSE b/ext/opcache/jit/ir/LICENSE index c43a12a770f8f..340f9c37225f6 100644 --- a/ext/opcache/jit/ir/LICENSE +++ b/ext/opcache/jit/ir/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2022 Zend by Perforce +Copyright (c) 2025 Dmitry Stogov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ext/opcache/jit/ir/ir.c b/ext/opcache/jit/ir/ir.c index 9d1b698761fbb..81621ce11bd36 100644 --- a/ext/opcache/jit/ir/ir.c +++ b/ext/opcache/jit/ir/ir.c @@ -227,6 +227,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted #define ir_op_flag_d0 ir_op_flag_d #define ir_op_flag_d1 (ir_op_flag_d | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_d1X1 (ir_op_flag_d | 1 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_d1X2 (ir_op_flag_d | 1 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_d2 (ir_op_flag_d | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_d2C (ir_op_flag_d | IR_OP_FLAG_COMMUTATIVE | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_d3 (ir_op_flag_d | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) @@ -270,6 +271,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted #define ir_op_flag_s3 (ir_op_flag_s | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_x1 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_x2 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_x2X1 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 2 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_x3 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_xN (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | IR_OP_FLAG_VAR_INPUTS) #define ir_op_flag_a1 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_ALLOC | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) @@ -392,6 +394,8 @@ void ir_init(ir_ctx *ctx, uint32_t flags, ir_ref consts_limit, ir_ref insns_limi ctx->insns_limit = insns_limit; ctx->consts_count = -(IR_TRUE - 1); ctx->consts_limit = consts_limit; + ctx->const_hash = ctx->_const_hash; + ctx->const_hash_mask = IR_CONST_HASH_SIZE - 1; ctx->fold_cse_limit = IR_UNUSED + 1; ctx->flags = flags; @@ -414,6 +418,9 @@ void ir_free(ir_ctx *ctx) { ir_insn *buf = ctx->ir_base - ctx->consts_limit; ir_mem_free(buf); + if (ctx->value_params) { + ir_mem_free(ctx->value_params); + } if (ctx->strtab.data) { ir_strtab_free(&ctx->strtab); } @@ -468,6 +475,10 @@ void ir_free(ir_ctx *ctx) ir_list_free((ir_list*)ctx->osr_entry_loads); ir_mem_free(ctx->osr_entry_loads); } + + if (ctx->const_hash_mask != IR_CONST_HASH_SIZE - 1) { + ir_mem_free(ctx->const_hash); + } } ir_ref ir_unique_const_addr(ir_ctx *ctx, uintptr_t addr) @@ -479,72 +490,64 @@ ir_ref ir_unique_const_addr(ir_ctx *ctx, uintptr_t addr) insn->val.u64 = addr; /* don't insert into constants chain */ insn->prev_const = IR_UNUSED; -#if 0 - insn->prev_const = ctx->prev_const_chain[IR_ADDR]; - ctx->prev_const_chain[IR_ADDR] = ref; -#endif -#if 0 - ir_insn *prev_insn, *next_insn; - ir_ref next; - - prev_insn = NULL; - next = ctx->prev_const_chain[IR_ADDR]; - while (next) { - next_insn = &ctx->ir_base[next]; - if (UNEXPECTED(next_insn->val.u64 >= addr)) { - break; - } - prev_insn = next_insn; - next = next_insn->prev_const; - } - - if (prev_insn) { - insn->prev_const = prev_insn->prev_const; - prev_insn->prev_const = ref; - } else { - insn->prev_const = ctx->prev_const_chain[IR_ADDR]; - ctx->prev_const_chain[IR_ADDR] = ref; - } -#endif return ref; } +IR_ALWAYS_INLINE uintptr_t ir_const_hash(ir_val val, uint32_t optx) +{ + return (val.u64 ^ (val.u64 >> 32) ^ optx); +} + +static IR_NEVER_INLINE void ir_const_hash_rehash(ir_ctx *ctx) +{ + ir_insn *insn; + ir_ref ref; + uintptr_t hash; + + if (ctx->const_hash_mask != IR_CONST_HASH_SIZE - 1) { + ir_mem_free(ctx->const_hash); + } + ctx->const_hash_mask = (ctx->const_hash_mask + 1) * 2 - 1; + ctx->const_hash = ir_mem_calloc(ctx->const_hash_mask + 1, sizeof(ir_ref)); + for (ref = IR_TRUE - 1; ref > -ctx->consts_count; ref--) { + insn = &ctx->ir_base[ref]; + hash = ir_const_hash(insn->val, insn->optx) & ctx->const_hash_mask; + insn->prev_const = ctx->const_hash[hash]; + ctx->const_hash[hash] = ref; + } +} + ir_ref ir_const_ex(ir_ctx *ctx, ir_val val, uint8_t type, uint32_t optx) { - ir_insn *insn, *prev_insn; + ir_insn *insn; ir_ref ref, prev; + uintptr_t hash; if (type == IR_BOOL) { return val.u64 ? IR_TRUE : IR_FALSE; } else if (type == IR_ADDR && val.u64 == 0) { return IR_NULL; } - prev_insn = NULL; - ref = ctx->prev_const_chain[type]; + + hash = ir_const_hash(val, optx) & ctx->const_hash_mask; + ref = ctx->const_hash[hash]; while (ref) { insn = &ctx->ir_base[ref]; - if (UNEXPECTED(insn->val.u64 >= val.u64)) { - if (insn->val.u64 == val.u64) { - if (insn->optx == optx) { - return ref; - } - } else { - break; - } + if (insn->val.u64 == val.u64 && insn->optx == optx) { + return ref; } - prev_insn = insn; ref = insn->prev_const; } - if (prev_insn) { - prev = prev_insn->prev_const; - prev_insn->prev_const = -ctx->consts_count; - } else { - prev = ctx->prev_const_chain[type]; - ctx->prev_const_chain[type] = -ctx->consts_count; + if ((uintptr_t)ctx->consts_count > ctx->const_hash_mask) { + ir_const_hash_rehash(ctx); + hash = ir_const_hash(val, optx) & ctx->const_hash_mask; } + prev = ctx->const_hash[hash]; + ctx->const_hash[hash] = -ctx->consts_count; + ref = ir_next_const(ctx); insn = &ctx->ir_base[ref]; insn->prev_const = prev; @@ -2092,10 +2095,10 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_vload_i(ir_ctx *ctx, ir_ref ref, ir_typ if (insn->type == type) { return ref; /* load forwarding (L2L) */ } else if (ir_type_size[insn->type] == ir_type_size[type]) { - return ir_fold1(ctx, IR_OPT(IR_BITCAST, type), ref); /* load forwarding with bitcast (L2L) */ + return ref; /* load forwarding with bitcast (L2L) */ } else if (ir_type_size[insn->type] > ir_type_size[type] && IR_IS_TYPE_INT(type) && IR_IS_TYPE_INT(insn->type)) { - return ir_fold1(ctx, IR_OPT(IR_TRUNC, type), ref); /* partial load forwarding (L2L) */ + return ref; /* partial load forwarding (L2L) */ } } } else if (insn->op == IR_VSTORE) { @@ -2105,10 +2108,10 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_vload_i(ir_ctx *ctx, ir_ref ref, ir_typ if (type2 == type) { return insn->op3; /* store forwarding (S2L) */ } else if (ir_type_size[type2] == ir_type_size[type]) { - return ir_fold1(ctx, IR_OPT(IR_BITCAST, type), insn->op3); /* store forwarding with bitcast (S2L) */ + return insn->op3; /* store forwarding with bitcast (S2L) */ } else if (ir_type_size[type2] > ir_type_size[type] && IR_IS_TYPE_INT(type) && IR_IS_TYPE_INT(type2)) { - return ir_fold1(ctx, IR_OPT(IR_TRUNC, type), insn->op3); /* partial store forwarding (S2L) */ + return insn->op3; /* partial store forwarding (S2L) */ } else { break; } @@ -3214,6 +3217,13 @@ ir_ref _ir_VA_ARG(ir_ctx *ctx, ir_type type, ir_ref list) return ctx->control = ir_emit2(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list); } +ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size) +{ + IR_ASSERT(ctx->control); + IR_ASSERT(size <= 0x7fffffff); + return ctx->control = ir_emit3(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list, (ir_ref)size); +} + ir_ref _ir_BLOCK_BEGIN(ir_ctx *ctx) { IR_ASSERT(ctx->control); diff --git a/ext/opcache/jit/ir/ir.h b/ext/opcache/jit/ir/ir.h index 9575348ff5450..52cbc06b153ee 100644 --- a/ext/opcache/jit/ir/ir.h +++ b/ext/opcache/jit/ir/ir.h @@ -310,6 +310,8 @@ typedef enum _ir_type { _(PHI, pN, reg, def, def) /* SSA Phi function */ \ _(COPY, d1X1, def, opt, ___) /* COPY (last foldable op) */ \ _(PI, p2, reg, def, ___) /* e-SSA Pi constraint ??? */ \ + _(ARGVAL, d1X2, def, num, num) /* pass struct arg by value */ \ + /* (op2 - size, op3 - align) */ \ /* (USE, RENAME) */ \ \ /* data ops */ \ @@ -343,7 +345,8 @@ typedef enum _ir_type { _(VA_START, x2, src, def, ___) /* va_start(va_list) */ \ _(VA_END, x2, src, def, ___) /* va_end(va_list) */ \ _(VA_COPY, x3, src, def, def) /* va_copy(dst, stc) */ \ - _(VA_ARG, x2, src, def, ___) /* va_arg(va_list) */ \ + _(VA_ARG, x2X1, src, def, opt) /* va_arg(va_list) */ \ + /* op3 - (size<<3)+log2(align) */ \ \ /* guards */ \ _(GUARD, c3, src, def, def) /* IF without second successor */ \ @@ -583,12 +586,22 @@ typedef struct _ir_code_buffer { void *pos; } ir_code_buffer; +typedef struct { + int size; + int align; + int offset; +} ir_value_param; + +#define IR_CONST_HASH_SIZE 64 + struct _ir_ctx { ir_insn *ir_base; /* two directional array - instructions grow down, constants grow up */ ir_ref insns_count; /* number of instructions stored in instructions buffer */ ir_ref insns_limit; /* size of allocated instructions buffer (it's extended when overflow) */ ir_ref consts_count; /* number of constants stored in constants buffer */ ir_ref consts_limit; /* size of allocated constants buffer (it's extended when overflow) */ + uintptr_t const_hash_mask; + ir_ref *const_hash; uint32_t flags; /* IR context flags (see IR_* defines above) */ uint32_t flags2; /* IR context private flags (see IR_* defines in ir_private.h) */ ir_type ret_type; /* Function return type */ @@ -596,6 +609,7 @@ struct _ir_ctx { int32_t status; /* non-zero error code (see IR_ERROR_... macros), app may use negative codes */ ir_ref fold_cse_limit; /* CSE finds identical insns backward from "insn_count" to "fold_cse_limit" */ ir_insn fold_insn; /* temporary storage for folding engine */ + ir_value_param *value_params; /* information about "by-val" struct parameters */ ir_hashtab *binding; ir_use_list *use_lists; /* def->use lists for each instruction */ ir_ref *use_edges; /* the actual uses: use = ctx->use_edges[ctx->use_lists[def].refs + n] */ @@ -655,7 +669,7 @@ struct _ir_ctx { ir_loader *loader; ir_strtab strtab; ir_ref prev_insn_chain[IR_LAST_FOLDABLE_OP + 1]; - ir_ref prev_const_chain[IR_LAST_TYPE]; + ir_ref _const_hash[IR_CONST_HASH_SIZE]; }; /* Basic IR Construction API (implementation in ir.c) */ @@ -896,6 +910,7 @@ int ir_load_llvm_asm(ir_loader *loader, const char *filename); #define IR_SAVE_SAFE_NAMES (1<<5) /* add '@' prefix to symbol names */ void ir_print_proto(const ir_ctx *ctx, ir_ref proto, FILE *f); +void ir_print_proto_ex(uint8_t flags, ir_type ret_type, uint32_t params_count, const uint8_t *param_types, FILE *f); void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f); /* IR debug dump API (implementation in ir_dump.c) */ diff --git a/ext/opcache/jit/ir/ir_aarch64.dasc b/ext/opcache/jit/ir/ir_aarch64.dasc index 3a2c3687042cd..4cdf4d68c2179 100644 --- a/ext/opcache/jit/ir/ir_aarch64.dasc +++ b/ext/opcache/jit/ir/ir_aarch64.dasc @@ -574,6 +574,10 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co constraints->tmp_regs[n] = IR_TMP_REG(3, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n++; break; + case IR_ARGVAL: + constraints->tmp_regs[0] = IR_SCRATCH_REG(IR_REG_SCRATCH, IR_DEF_SUB_REF - IR_SUB_REFS_COUNT, IR_USE_SUB_REF); + n = 1; + break; case IR_CALL: insn = &ctx->ir_base[ref]; constraints->def_reg = (IR_IS_TYPE_INT(insn->type)) ? IR_REG_INT_RET1 : IR_REG_FP_RET1; @@ -1103,6 +1107,8 @@ binop_fp: } } return IR_SKIPPED | IR_NOP; + case IR_ARGVAL: + return IR_FUSED | IR_ARGVAL; case IR_NOP: return IR_SKIPPED | IR_NOP; default: @@ -1386,6 +1392,12 @@ static void ir_emit_load_mem(ir_ctx *ctx, ir_type type, ir_reg reg, ir_mem mem) } } +static int32_t ir_local_offset(ir_ctx *ctx, ir_insn *insn) +{ + IR_ASSERT(insn->op == IR_VAR || insn->op == IR_ALLOCA || insn->op == IR_VADDR); + return IR_SPILL_POS_TO_OFFSET(insn->op3); +} + static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) { ir_backend_data *data = ctx->data; @@ -1399,8 +1411,7 @@ static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) if (var_insn->op == IR_VADDR) { var_insn = &ctx->ir_base[var_insn->op1]; } - IR_ASSERT(var_insn->op == IR_VAR || var_insn->op == IR_ALLOCA); - offset = IR_SPILL_POS_TO_OFFSET(var_insn->op3); + offset = ir_local_offset(ctx, var_insn); if (aarch64_may_encode_imm12(offset)) { | add Rx(reg), Rx(base), #offset } else { @@ -3858,7 +3869,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) IR_ASSERT(!IR_IS_SYM_CONST(ctx->ir_base[addr_insn->op2].op)); if (ir_rule(ctx, addr_insn->op1) == IR_STATIC_ALLOCA) { reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[addr_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[addr_insn->op1]); offset += ctx->ir_base[addr_insn->op2].val.i32; return IR_MEM_BO(reg, offset); } else { @@ -3876,7 +3887,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) } else { IR_ASSERT(addr_insn->op == IR_ALLOCA || addr_insn->op == IR_VADDR); reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[ref].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[ref]); return IR_MEM_BO(reg, offset); } } @@ -4265,7 +4276,7 @@ static void ir_emit_va_start(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (ctx->flags & IR_USE_FRAME_POINTER) { @@ -4297,7 +4308,7 @@ static void ir_emit_va_start(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (ctx->flags & IR_USE_FRAME_POINTER) { @@ -4364,7 +4375,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op2_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + op2_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (op3_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op3_reg)) { @@ -4375,7 +4386,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op3) == IR_STATIC_ALLOCA); op3_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op3_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op3].op3); + op3_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op3]); } | ldr Rx(tmp_reg), [Rx(op3_reg), #op3_offset] | str Rx(tmp_reg), [Rx(op2_reg), #op2_offset] @@ -4397,7 +4408,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op2_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + op2_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (op3_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op3_reg)) { @@ -4408,7 +4419,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op3) == IR_STATIC_ALLOCA); op3_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op3_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op3].op3); + op3_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op3]); } | ldr Rx(tmp_reg), [Rx(op3_reg), #op3_offset] | str Rx(tmp_reg), [Rx(op2_reg), #op2_offset] @@ -4446,7 +4457,7 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ct, &ctx->ir_base[insn->op2]); } | ldr Rx(tmp_reg), [Rx(op2_reg), #offset] ir_emit_load_mem(ctx, type, def_reg, IR_MEM_BO(tmp_reg, 0)); @@ -4478,7 +4489,7 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (IR_IS_TYPE_INT(type)) { | ldr Rw(tmp_reg), [Rx(op2_reg), #(offset+offsetof(ir_va_list, gr_offset))] @@ -4741,7 +4752,7 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) } } -static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) +static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn, int32_t *copy_stack_ptr) { int j, n; ir_type type; @@ -4749,7 +4760,7 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) int fp_param = 0; int int_reg_params_count = IR_REG_INT_ARGS; int fp_reg_params_count = IR_REG_FP_ARGS; - int32_t used_stack = 0; + int32_t used_stack = 0, copy_stack = 0; #ifdef __APPLE__ const ir_proto_t *proto = ir_call_proto(ctx, insn); int last_named_input = (proto && (proto->flags & IR_VARARG_FUNC)) ? proto->params_count + 2 : insn->inputs_count; @@ -4757,7 +4768,16 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) n = insn->inputs_count; for (j = 3; j <= n; j++) { - type = ctx->ir_base[ir_insn_op(insn, j)].type; + ir_insn *arg = &ctx->ir_base[ir_insn_op(insn, j)]; + type = arg->type; + if (arg->op == IR_ARGVAL) { + int size = arg->op2; + int align = arg->op3; + copy_stack += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack = IR_ALIGNED_SIZE(copy_stack, align); + type = IR_ADDR; + } #ifdef __APPLE__ if (j > last_named_input) { used_stack += IR_MAX(sizeof(void*), ir_type_size[type]); @@ -4777,7 +4797,9 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) } } - return used_stack; + copy_stack = IR_ALIGNED_SIZE(copy_stack, 16); + *copy_stack_ptr = copy_stack; + return used_stack + copy_stack; } static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg tmp_reg) @@ -4796,7 +4818,7 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg int fp_reg_params_count = IR_REG_FP_ARGS; const int8_t *int_reg_params = _ir_int_reg_params; const int8_t *fp_reg_params = _ir_fp_reg_params; - int32_t used_stack, stack_offset = 0; + int32_t used_stack, copy_stack = 0, stack_offset = 0, copy_stack_offset = 0; ir_copy *copies; bool do_pass3 = 0; /* For temporaries we may use any scratch registers except for registers used for parameters */ @@ -4815,7 +4837,7 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg // TODO: support for preallocated stack used_stack = 0; } else { - used_stack = ir_call_used_stack(ctx, insn); + used_stack = ir_call_used_stack(ctx, insn, ©_stack); /* Stack must be 16 byte aligned */ used_stack = IR_ALIGNED_SIZE(used_stack, 16); if (ctx->fixed_call_stack_size && used_stack <= ctx->fixed_call_stack_size) { @@ -4838,6 +4860,48 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg int last_named_input = (proto && (proto->flags & IR_VARARG_FUNC)) ? proto->params_count + 2 : insn->inputs_count; #endif + if (copy_stack) { + /* Copy struct arguments */ + for (j = 3; j <= n; j++) { + arg = ir_insn_op(insn, j); + src_reg = ir_get_alocated_reg(ctx, def, j); + arg_insn = &ctx->ir_base[arg]; + type = arg_insn->type; + + if (arg_insn->op == IR_ARGVAL) { + /* make a stack copy */ + void *addr = memcpy; + int size = arg_insn->op2; + int align = arg_insn->op3; + + copy_stack_offset += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack_offset = IR_ALIGNED_SIZE(copy_stack_offset, align); + src_reg = ctx->regs[arg][1]; + + | add Rx(IR_REG_INT_ARG1), sp, #(used_stack - copy_stack_offset) + if (src_reg != IR_REG_NONE) { + if (IR_REG_SPILLED(src_reg)) { + src_reg = IR_REG_NUM(src_reg); + ir_emit_load(ctx, IR_ADDR, src_reg, arg_insn->op1); + } + | mov Rx(IR_REG_INT_ARG2), Rx(src_reg) + } else { + ir_emit_load(ctx, IR_ADDR, IR_REG_INT_ARG2, arg_insn->op1); + } + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_ARG3, size); + + if (aarch64_may_use_b(ctx->code_buffer, addr)) { + | bl &addr + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, (intptr_t)addr); + | blr Rx(IR_REG_INT_TMP) + } + } + } + copy_stack_offset = 0; + } + /* 1. move all register arguments that should be passed through stack * and collect arguments that should be passed through registers */ copies = ir_mem_malloc((n - 2) * sizeof(ir_copy)); @@ -4846,8 +4910,13 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg src_reg = ir_get_alocated_reg(ctx, def, j); arg_insn = &ctx->ir_base[arg]; type = arg_insn->type; + #ifdef __APPLE__ if (j > last_named_input) { + if (arg_insn->op == IR_ARGVAL) { + do_pass3 = 1; + continue; + } dst_reg = IR_REG_NONE; /* pass argument through stack */ } else #endif @@ -4858,6 +4927,10 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg dst_reg = IR_REG_NONE; /* pass argument through stack */ } int_param++; + if (arg_insn->op == IR_ARGVAL) { + do_pass3 = 1; + continue; + } } else { IR_ASSERT(IR_IS_TYPE_FP(type)); if (fp_param < fp_reg_params_count) { @@ -4914,6 +4987,31 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg src_reg = ir_get_alocated_reg(ctx, def, j); arg_insn = &ctx->ir_base[arg]; type = arg_insn->type; + if (arg_insn->op == IR_ARGVAL) { + /* pass pointer to the copy on stack */ + int size = arg_insn->op2; + int align = arg_insn->op3; + + copy_stack_offset += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack_offset = IR_ALIGNED_SIZE(copy_stack_offset, align); +#ifdef __APPLE__ + if (j > last_named_input) { + | add Rx(tmp_reg), sp, #(used_stack - copy_stack_offset) + ir_emit_store_mem_int(ctx, IR_ADDR, IR_MEM_BO(IR_REG_STACK_POINTER, stack_offset), tmp_reg); + } else +#endif + if (int_param < int_reg_params_count) { + dst_reg = int_reg_params[int_param]; + | add Rx(dst_reg), sp, #(used_stack - copy_stack_offset) + } else { + | add Rx(tmp_reg), sp, #(used_stack - copy_stack_offset) + ir_emit_store_mem_int(ctx, IR_ADDR, IR_MEM_BO(IR_REG_STACK_POINTER, stack_offset), tmp_reg); + stack_offset += sizeof(void*); + } + int_param++; + continue; + } #ifdef __APPLE__ if (j > last_named_input) { dst_reg = IR_REG_NONE; /* pass argument through stack */ @@ -5896,7 +5994,8 @@ static void ir_preallocate_call_stack(ir_ctx *ctx) for (i = 1, insn = ctx->ir_base + 1; i < ctx->insns_count;) { if (insn->op == IR_CALL) { - call_stack_size = ir_call_used_stack(ctx, insn); + int32_t copy_stack; + call_stack_size = ir_call_used_stack(ctx, insn, ©_stack); if (call_stack_size > peak_call_stack_size) { peak_call_stack_size = call_stack_size; } diff --git a/ext/opcache/jit/ir/ir_builder.h b/ext/opcache/jit/ir/ir_builder.h index 358ae241e2dd4..c1dcffdbaa084 100644 --- a/ext/opcache/jit/ir/ir_builder.h +++ b/ext/opcache/jit/ir/ir_builder.h @@ -586,6 +586,7 @@ extern "C" { #define ir_VA_END(_list) _ir_VA_END(_ir_CTX, _list) #define ir_VA_COPY(_dst, _src) _ir_VA_COPY(_ir_CTX, _dst, _src) #define ir_VA_ARG(_list, _type) _ir_VA_ARG(_ir_CTX, _type, _list) +#define ir_VA_ARG_EX(_list, _type, size) _ir_VA_ARG_EX(_ir_CTX, _type, _list, size) #define ir_START() _ir_START(_ir_CTX) #define ir_ENTRY(_src, _num) _ir_ENTRY(_ir_CTX, (_src), (_num)) @@ -661,6 +662,7 @@ void _ir_VA_START(ir_ctx *ctx, ir_ref list); void _ir_VA_END(ir_ctx *ctx, ir_ref list); void _ir_VA_COPY(ir_ctx *ctx, ir_ref dst, ir_ref src); ir_ref _ir_VA_ARG(ir_ctx *ctx, ir_type type, ir_ref list); +ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size); void _ir_START(ir_ctx *ctx); void _ir_ENTRY(ir_ctx *ctx, ir_ref src, ir_ref num); void _ir_BEGIN(ir_ctx *ctx, ir_ref src); diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c index 13d66a7130283..00923387bb21c 100644 --- a/ext/opcache/jit/ir/ir_cfg.c +++ b/ext/opcache/jit/ir/ir_cfg.c @@ -605,7 +605,7 @@ static void compute_postnum(const ir_ctx *ctx, uint32_t *cur, uint32_t b) /* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by * Cooper, Harvey and Kennedy. */ -static int ir_build_dominators_tree_slow(ir_ctx *ctx) +static IR_NEVER_INLINE int ir_build_dominators_tree_slow(ir_ctx *ctx) { uint32_t blocks_count, b, postnum; ir_block *blocks, *bb; @@ -690,28 +690,13 @@ static int ir_build_dominators_tree_slow(ir_ctx *ctx) /* Build dominators tree */ blocks[1].idom = 0; blocks[1].dom_depth = 0; - for (b = 2, bb = &blocks[2]; b <= blocks_count; b++, bb++) { - uint32_t idom = bb->idom; - ir_block *idom_bb = &blocks[idom]; + /* Construct children lists sorted by block number */ + for (b = blocks_count, bb = &blocks[b]; b >= 2; b--, bb--) { + ir_block *idom_bb = &blocks[bb->idom]; bb->dom_depth = 0; - /* Sort by block number to traverse children in pre-order */ - if (idom_bb->dom_child == 0) { - idom_bb->dom_child = b; - } else if (b < idom_bb->dom_child) { - bb->dom_next_child = idom_bb->dom_child; - idom_bb->dom_child = b; - } else { - int child = idom_bb->dom_child; - ir_block *child_bb = &blocks[child]; - - while (child_bb->dom_next_child > 0 && b > child_bb->dom_next_child) { - child = child_bb->dom_next_child; - child_bb = &blocks[child]; - } - bb->dom_next_child = child_bb->dom_next_child; - child_bb->dom_next_child = b; - } + bb->dom_next_child = idom_bb->dom_child; + idom_bb->dom_child = b; } /* Recalculate dom_depth for all blocks */ @@ -769,6 +754,7 @@ int ir_build_dominators_tree(ir_ctx *ctx) ctx->flags2 &= ~IR_NO_LOOPS; // IR_ASSERT(k > 1 && "Wrong blocks order: BB is before its single predecessor"); if (UNEXPECTED(k <= 1)) { +slow_case: ir_list_free(&worklist); return ir_build_dominators_tree_slow(ctx); } @@ -780,7 +766,9 @@ int ir_build_dominators_tree(ir_ctx *ctx) if (idom < b) { break; } - IR_ASSERT(k > 0); + if (UNEXPECTED(k == 0)) { + goto slow_case; + } ir_list_push(&worklist, idom); } } @@ -808,25 +796,14 @@ int ir_build_dominators_tree(ir_ctx *ctx) } bb->idom = idom; idom_bb = &blocks[idom]; - bb->dom_depth = idom_bb->dom_depth + 1; - /* Sort by block number to traverse children in pre-order */ - if (idom_bb->dom_child == 0) { - idom_bb->dom_child = b; - } else if (b < idom_bb->dom_child) { - bb->dom_next_child = idom_bb->dom_child; - idom_bb->dom_child = b; - } else { - int child = idom_bb->dom_child; - ir_block *child_bb = &blocks[child]; + } - while (child_bb->dom_next_child > 0 && b > child_bb->dom_next_child) { - child = child_bb->dom_next_child; - child_bb = &blocks[child]; - } - bb->dom_next_child = child_bb->dom_next_child; - child_bb->dom_next_child = b; - } + /* Construct children lists sorted by block number */ + for (b = blocks_count, bb = &blocks[b]; b >= 2; b--, bb--) { + ir_block *idom_bb = &blocks[bb->idom]; + bb->dom_next_child = idom_bb->dom_child; + idom_bb->dom_child = b; } blocks[1].idom = 0; @@ -945,23 +922,13 @@ static int ir_build_dominators_tree_iterative(ir_ctx *ctx) ir_block *idom_bb = &blocks[idom]; bb->dom_depth = idom_bb->dom_depth + 1; - /* Sort by block number to traverse children in pre-order */ - if (idom_bb->dom_child == 0) { - idom_bb->dom_child = b; - } else if (b < idom_bb->dom_child) { - bb->dom_next_child = idom_bb->dom_child; - idom_bb->dom_child = b; - } else { - int child = idom_bb->dom_child; - ir_block *child_bb = &blocks[child]; + } - while (child_bb->dom_next_child > 0 && b > child_bb->dom_next_child) { - child = child_bb->dom_next_child; - child_bb = &blocks[child]; - } - bb->dom_next_child = child_bb->dom_next_child; - child_bb->dom_next_child = b; - } + /* Construct children lists sorted by block number */ + for (b = blocks_count, bb = &blocks[b]; b >= 2; b--, bb--) { + ir_block *idom_bb = &blocks[bb->idom]; + bb->dom_next_child = idom_bb->dom_child; + idom_bb->dom_child = b; } return 1; diff --git a/ext/opcache/jit/ir/ir_dump.c b/ext/opcache/jit/ir/ir_dump.c index 54fddf50ac066..a501d261f30a7 100644 --- a/ext/opcache/jit/ir/ir_dump.c +++ b/ext/opcache/jit/ir/ir_dump.c @@ -660,6 +660,12 @@ void ir_dump_codegen(const ir_ctx *ctx, FILE *f) } if (first) { fprintf(f, ";"); + } else if (ctx->value_params + && insn->op == IR_PARAM + && ctx->value_params[insn->op3 - 1].align) { + fprintf(f, ") ByVal(%d, %d);", + ctx->value_params[insn->op3 - 1].size, + ctx->value_params[insn->op3 - 1].align); } else { fprintf(f, ");"); } diff --git a/ext/opcache/jit/ir/ir_emit.c b/ext/opcache/jit/ir/ir_emit.c index fab9f56228d80..c9e65229c3969 100644 --- a/ext/opcache/jit/ir/ir_emit.c +++ b/ext/opcache/jit/ir/ir_emit.c @@ -167,11 +167,24 @@ static ir_reg ir_get_param_reg(const ir_ctx *ctx, ir_ref ref) if (insn->op == IR_PARAM) { if (IR_IS_TYPE_INT(insn->type)) { if (use == ref) { +#if defined(IR_TARGET_X64) || defined(IR_TARGET_X86) + if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { + /* struct passed by value on stack */ + return IR_REG_NONE; + } else +#endif if (int_param < int_reg_params_count) { return int_reg_params[int_param]; } else { return IR_REG_NONE; } +#if defined(IR_TARGET_X64) || defined(IR_TARGET_X86) + } else { + if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { + /* struct passed by value on stack */ + continue; + } +#endif } int_param++; #ifdef _WIN64 @@ -222,9 +235,12 @@ static int ir_get_args_regs(const ir_ctx *ctx, const ir_insn *insn, int8_t *regs n = insn->inputs_count; n = IR_MIN(n, IR_MAX_REG_ARGS + 2); for (j = 3; j <= n; j++) { - type = ctx->ir_base[ir_insn_op(insn, j)].type; + ir_insn *arg = &ctx->ir_base[ir_insn_op(insn, j)]; + type = arg->type; if (IR_IS_TYPE_INT(type)) { - if (int_param < int_reg_params_count) { + if (arg->op == IR_ARGVAL) { + continue; + } else if (int_param < int_reg_params_count) { regs[j] = int_reg_params[int_param]; count = j + 1; } else { diff --git a/ext/opcache/jit/ir/ir_fold.h b/ext/opcache/jit/ir/ir_fold.h index 2f5be6ca2e00b..7ae6ca539da8d 100644 --- a/ext/opcache/jit/ir/ir_fold.h +++ b/ext/opcache/jit/ir/ir_fold.h @@ -1703,6 +1703,11 @@ IR_FOLD(SUB_OV(_, C_ADDR)) { if (op2_insn->val.u64 == 0) { /* a +/- 0 => a */ + if (op1_insn->type != IR_OPT_TYPE(opt)) { + opt = IR_BITCAST | (opt & IR_OPT_TYPE_MASK); + op2 = IR_UNUSED; + IR_FOLD_RESTART; + } IR_FOLD_COPY(op1); } IR_FOLD_NEXT; @@ -1721,6 +1726,12 @@ IR_FOLD(ADD(C_ADDR, _)) { if (op1_insn->val.u64 == 0) { /* 0 + a => a */ + if (op2_insn->type != IR_OPT_TYPE(opt)) { + opt = IR_BITCAST | (opt & IR_OPT_TYPE_MASK); + op1 = op2; + op2 = IR_UNUSED; + IR_FOLD_RESTART; + } IR_FOLD_COPY(op2); } IR_FOLD_NEXT; @@ -2927,7 +2938,7 @@ IR_FOLD(SUB(C_ADDR, SUB)) /* c1 - (x - c2) => (c1 + c2) - x */ val.u64 = op1_insn->val.u64 + ctx->ir_base[op2_insn->op2].val.u64; op2 = op2_insn->op1; - op1 = ir_const(ctx, val, op1_insn->op1); + op1 = ir_const(ctx, val, op1_insn->type); IR_FOLD_RESTART; } else if (IR_IS_CONST_REF(op2_insn->op1) && !IR_IS_SYM_CONST(ctx->ir_base[op2_insn->op1].op)) { /* c1 - (c2 - x) => x + (c1 - c2) */ diff --git a/ext/opcache/jit/ir/ir_gcm.c b/ext/opcache/jit/ir/ir_gcm.c index 3ea78850249ab..c170fa47476ec 100644 --- a/ext/opcache/jit/ir/ir_gcm.c +++ b/ext/opcache/jit/ir/ir_gcm.c @@ -792,21 +792,26 @@ IR_ALWAYS_INLINE bool ir_is_good_bb_order(ir_ctx *ctx, uint32_t b, ir_block *bb, ir_ref *p = insn->ops + 1; if (n == 1) { - return *p < start; + return ctx->cfg_map[*p] < b; } else { IR_ASSERT(n > 1); for (; n > 0; p++, n--) { ir_ref input = *p; - if (input < start) { - /* ordered */ - } else if ((bb->flags & IR_BB_LOOP_HEADER) - && (ctx->cfg_map[input] == b || ctx->cfg_blocks[ctx->cfg_map[input]].loop_header == b)) { - /* back-edge of reducible loop */ - } else if ((bb->flags & IR_BB_IRREDUCIBLE_LOOP) - && (ctx->cfg_blocks[ctx->cfg_map[input]].loop_header == ctx->cfg_blocks[b].loop_header)) { - /* closing edge of irreducible loop */ - } else { - return 0; + + if (!IR_IS_CONST_REF(input)) { + uint32_t input_b = ctx->cfg_map[input]; + + if (input_b < b) { + /* ordered */ + } else if ((bb->flags & IR_BB_LOOP_HEADER) + && (input_b == b || ctx->cfg_blocks[input_b].loop_header == b)) { + /* back-edge of reducible loop */ + } else if ((bb->flags & IR_BB_IRREDUCIBLE_LOOP) + && (ctx->cfg_blocks[input_b].loop_header == bb->loop_header)) { + /* closing edge of irreducible loop */ + } else { + return 0; + } } } return 1; @@ -925,121 +930,54 @@ int ir_schedule(ir_ctx *ctx) ir_ref *_xlat; ir_ref *edges; ir_ref prev_b_end; - uint32_t b, prev_b; + uint32_t b; uint32_t *_blocks = ctx->cfg_map; ir_ref *_next = ir_mem_malloc(ctx->insns_count * sizeof(ir_ref)); ir_ref *_prev = ir_mem_malloc(ctx->insns_count * sizeof(ir_ref)); - ir_ref _move_down = 0; ir_block *bb; ir_insn *insn, *new_insn; ir_use_list *lists, *use_list, *new_list; bool bad_bb_order = 0; + /* Create a double-linked list of nodes ordered by BB, respecting BB->start and BB->end */ IR_ASSERT(_blocks[1] == 1); - prev_b = 1; - prev_b_end = ctx->cfg_blocks[1].end; + + /* link BB boundaries */ _prev[1] = 0; - _prev[prev_b_end] = 0; - for (i = 2, j = 1; i < ctx->insns_count; i++) { - b = _blocks[i]; - IR_ASSERT((int32_t)b >= 0); - if (b == prev_b && i <= prev_b_end) { - /* add to the end of the list */ - _next[j] = i; - _prev[i] = j; - j = i; - } else if (b > prev_b) { - bb = &ctx->cfg_blocks[b]; - if (i == bb->start) { - if (bb->end > bb->start) { - prev_b = b; - prev_b_end = bb->end; - /* add to the end of the list */ - _next[j] = i; - _prev[i] = j; - j = i; - } else { - prev_b = 0; - prev_b_end = 0; - k = bb->end; - while (_blocks[_prev[k]] == b) { - k = _prev[k]; - } - /* insert before "k" */ - _prev[i] = _prev[k]; - _next[i] = k; - _next[_prev[k]] = i; - _prev[k] = i; - } - if (!ir_is_good_bb_order(ctx, b, bb, i)) { - bad_bb_order = 1; - } - } else if (i != bb->end) { - /* move down late (see the following loop) */ - _next[i] = _move_down; - _move_down = i; - } else { - prev_b = 0; - prev_b_end = 0; - if (bb->start > bb->end) { - /* add to the end of the list */ - _next[j] = i; - _prev[i] = j; - j = i; - } else { - k = bb->start; - while (_blocks[_next[k]] == b) { - k = _next[k]; - } - /* insert after "k" */ - _next[i] = _next[k]; - _prev[i] = k; - _prev[_next[k]] = i; - _next[k] = i; - } - } - } else if (b) { - bb = &ctx->cfg_blocks[b]; - IR_ASSERT(i != bb->start); - if (i > bb->end) { - /* move up, insert before the end of the already scheduled BB */ - k = bb->end; - } else { - IR_ASSERT(i > bb->start); - /* move up, insert at the end of the block */ - k = ctx->cfg_blocks[b + 1].start; - } - /* insert before "k" */ - _prev[i] = _prev[k]; - _next[i] = k; - _next[_prev[k]] = i; - _prev[k] = i; + prev_b_end = ctx->cfg_blocks[1].end; + _next[1] = prev_b_end; + _prev[prev_b_end] = 1; + for (b = 2, bb = ctx->cfg_blocks + 2; b <= ctx->cfg_blocks_count; b++, bb++) { + _next[prev_b_end] = bb->start; + _prev[bb->start] = prev_b_end; + _next[bb->start] = bb->end; + _prev[bb->end] = bb->start; + prev_b_end = bb->end; + if (!ir_is_good_bb_order(ctx, b, bb, bb->start)) { + bad_bb_order = 1; } } - _next[j] = 0; + _next[prev_b_end] = 0; - while (_move_down) { - i = _move_down; - _move_down = _next[i]; + /* insert intermediate BB nodes */ + for (i = 2, j = 1; i < ctx->insns_count; i++) { b = _blocks[i]; + if (!b) continue; bb = &ctx->cfg_blocks[b]; - k = _next[bb->start]; - - if (bb->flags & (IR_BB_HAS_PHI|IR_BB_HAS_PI|IR_BB_HAS_PARAM|IR_BB_HAS_VAR)) { - /* insert after the start of the block and all PARAM, VAR, PI, PHI */ - insn = &ctx->ir_base[k]; - while (insn->op == IR_PHI || insn->op == IR_PARAM || insn->op == IR_VAR || insn->op == IR_PI) { - k = _next[k]; - insn = &ctx->ir_base[k]; - } + if (i != bb->start && i != bb->end) { + /* insert before "end" */ + ir_ref n = bb->end; + ir_ref p = _prev[n]; + _prev[i] = p; + _next[i] = n; + _next[p] = i; + _prev[n] = i; } + } - /* insert before "k" */ - _prev[i] = _prev[k]; - _next[i] = k; - _next[_prev[k]] = i; - _prev[k] = i; + if (bad_bb_order) { + ir_fix_bb_order(ctx, _prev, _next); } #ifdef IR_DEBUG @@ -1051,10 +989,6 @@ int ir_schedule(ir_ctx *ctx) } #endif - if (bad_bb_order) { - ir_fix_bb_order(ctx, _prev, _next); - } - _xlat = ir_mem_calloc((ctx->consts_count + ctx->insns_count), sizeof(ir_ref)); _xlat += ctx->consts_count; _xlat[IR_TRUE] = IR_TRUE; @@ -1168,7 +1102,11 @@ int ir_schedule(ir_ctx *ctx) if (end->op == IR_IF) { /* Move condition closer to IF */ input = end->op2; - if (input > 0 && _blocks[input] == b && !_xlat[input] && _prev[j] != input) { + if (input > 0 + && _blocks[input] == b + && !_xlat[input] + && _prev[j] != input + && (!(ir_op_flags[ctx->ir_base[input].op] & IR_OP_FLAG_CONTROL) || end->op1 == input)) { if (input == i) { i = _next[i]; insn = &ctx->ir_base[i]; @@ -1188,6 +1126,7 @@ int ir_schedule(ir_ctx *ctx) ir_ref n, j, *p, input; restart: + IR_ASSERT(_blocks[i] == b); n = insn->inputs_count; for (j = n, p = insn->ops + 1; j > 0; p++, j--) { input = *p; @@ -1221,6 +1160,7 @@ int ir_schedule(ir_ctx *ctx) } _xlat[i] = insns_count; insns_count += ir_insn_inputs_to_len(n); + IR_ASSERT(_next[i] != IR_UNUSED); i = _next[i]; insn = &ctx->ir_base[i]; } @@ -1274,6 +1214,7 @@ int ir_schedule(ir_ctx *ctx) new_ctx.insns_count = insns_count; new_ctx.flags2 = ctx->flags2; new_ctx.ret_type = ctx->ret_type; + new_ctx.value_params = ctx->value_params; new_ctx.mflags = ctx->mflags; new_ctx.spill_base = ctx->spill_base; new_ctx.fixed_stack_red_zone = ctx->fixed_stack_red_zone; @@ -1511,6 +1452,7 @@ int ir_schedule(ir_ctx *ctx) new_ctx.cfg_edges = ctx->cfg_edges; ctx->cfg_blocks = NULL; ctx->cfg_edges = NULL; + ctx->value_params = NULL; ir_code_buffer *saved_code_buffer = ctx->code_buffer; ir_free(ctx); diff --git a/ext/opcache/jit/ir/ir_ra.c b/ext/opcache/jit/ir/ir_ra.c index 0c0e8dec3b47a..21c7ee3ac64e5 100644 --- a/ext/opcache/jit/ir/ir_ra.c +++ b/ext/opcache/jit/ir/ir_ra.c @@ -1193,7 +1193,7 @@ static void ir_add_fusion_ranges(ir_ctx *ctx, ir_ref ref, ir_ref input, ir_block n = IR_INPUT_EDGES_COUNT(flags); j = 1; p = insn->ops + j; - if (flags & IR_OP_FLAG_CONTROL) { + if (flags & (IR_OP_FLAG_CONTROL|IR_OP_FLAG_PINNED)) { j++; p++; } @@ -1340,7 +1340,7 @@ int ir_compute_live_ranges(ir_ctx *ctx) || (ctx->rules[ref] & IR_RULE_MASK) == IR_ALLOCA) && ctx->use_lists[ref].count > 0) { insn = &ctx->ir_base[ref]; - if (insn->op != IR_VADDR) { + if (insn->op != IR_VADDR && insn->op != IR_PARAM) { insn->op3 = ctx->vars; ctx->vars = ref; } @@ -1630,6 +1630,10 @@ static void ir_vregs_join(ir_ctx *ctx, uint32_t r1, uint32_t r2) if (ctx->ir_base[IR_LIVE_POS_TO_REF(ctx->live_intervals[r1]->use_pos->pos)].op != IR_VLOAD) { ctx->live_intervals[r1]->flags &= ~IR_LIVE_INTERVAL_MEM_LOAD; } + if (ival->flags & IR_LIVE_INTERVAL_MEM_PARAM) { + IR_ASSERT(!(ctx->live_intervals[r1]->flags & IR_LIVE_INTERVAL_MEM_PARAM)); + ctx->live_intervals[r1]->flags |= IR_LIVE_INTERVAL_MEM_PARAM; + } ctx->live_intervals[r2] = NULL; // TODO: remember to reuse ??? diff --git a/ext/opcache/jit/ir/ir_save.c b/ext/opcache/jit/ir/ir_save.c index ea787f162ec1f..5ba986fadd481 100644 --- a/ext/opcache/jit/ir/ir_save.c +++ b/ext/opcache/jit/ir/ir_save.c @@ -10,31 +10,35 @@ void ir_print_proto(const ir_ctx *ctx, ir_ref func_proto, FILE *f) { - ir_ref j; - if (func_proto) { const ir_proto_t *proto = (const ir_proto_t *)ir_get_str(ctx, func_proto); + ir_print_proto_ex(proto->flags, proto->ret_type, proto->params_count, proto->param_types, f); + } else { + fprintf(f, "(): int32_t"); + } +} - fprintf(f, "("); - if (proto->params_count > 0) { - fprintf(f, "%s", ir_type_cname[proto->param_types[0]]); - for (j = 1; j < proto->params_count; j++) { - fprintf(f, ", %s", ir_type_cname[proto->param_types[j]]); - } - if (proto->flags & IR_VARARG_FUNC) { - fprintf(f, ", ..."); - } - } else if (proto->flags & IR_VARARG_FUNC) { - fprintf(f, "..."); +void ir_print_proto_ex(uint8_t flags, ir_type ret_type, uint32_t params_count, const uint8_t *param_types, FILE *f) +{ + uint32_t j; + + fprintf(f, "("); + if (params_count > 0) { + fprintf(f, "%s", ir_type_cname[param_types[0]]); + for (j = 1; j < params_count; j++) { + fprintf(f, ", %s", ir_type_cname[param_types[j]]); } - fprintf(f, "): %s", ir_type_cname[proto->ret_type]); - if (proto->flags & IR_FASTCALL_FUNC) { - fprintf(f, " __fastcall"); - } else if (proto->flags & IR_BUILTIN_FUNC) { - fprintf(f, " __builtin"); + if (flags & IR_VARARG_FUNC) { + fprintf(f, ", ..."); } - } else { - fprintf(f, "(): int32_t"); + } else if (flags & IR_VARARG_FUNC) { + fprintf(f, "..."); + } + fprintf(f, "): %s", ir_type_cname[ret_type]); + if (flags & IR_FASTCALL_FUNC) { + fprintf(f, " __fastcall"); + } else if (flags & IR_BUILTIN_FUNC) { + fprintf(f, " __builtin"); } } @@ -280,6 +284,12 @@ void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f) } if (first) { fprintf(f, ";"); + } else if (ctx->value_params + && insn->op == IR_PARAM + && ctx->value_params[insn->op3 - 1].align) { + fprintf(f, ") ByVal(%d, %d);", + ctx->value_params[insn->op3 - 1].size, + ctx->value_params[insn->op3 - 1].align); } else { fprintf(f, ");"); } diff --git a/ext/opcache/jit/ir/ir_sccp.c b/ext/opcache/jit/ir/ir_sccp.c index 58de0d726f781..48659cd4bd71b 100644 --- a/ext/opcache/jit/ir/ir_sccp.c +++ b/ext/opcache/jit/ir/ir_sccp.c @@ -1875,6 +1875,7 @@ static ir_ref ir_ext_const(ir_ctx *ctx, ir_insn *val_insn, ir_op op, ir_type typ case IR_I8: case IR_U8: case IR_BOOL: + case IR_CHAR: if (op == IR_SEXT) { new_val.i64 = (int64_t)val_insn->val.i8; } else { @@ -1928,7 +1929,7 @@ static ir_ref ir_ext_ref(ir_ctx *ctx, ir_ref var_ref, ir_ref src_ref, ir_op op, return ref; } -static uint32_t _ir_estimated_control(ir_ctx *ctx, ir_ref val) +static uint32_t _ir_estimated_control(ir_ctx *ctx, ir_ref val, ir_ref loop) { ir_insn *insn; ir_ref n, *p, input, result, ctrl; @@ -1953,7 +1954,8 @@ static uint32_t _ir_estimated_control(ir_ctx *ctx, ir_ref val) result = 1; for (; n > 0; p++, n--) { input = *p; - ctrl = _ir_estimated_control(ctx, input); + ctrl = _ir_estimated_control(ctx, input, loop); + if (ctrl >= loop) return ctrl; if (ctrl > result) { // TODO: check dominance depth instead of order result = ctrl; } @@ -1963,7 +1965,7 @@ static uint32_t _ir_estimated_control(ir_ctx *ctx, ir_ref val) static bool ir_is_loop_invariant(ir_ctx *ctx, ir_ref ref, ir_ref loop) { - ref = _ir_estimated_control(ctx, ref); + ref = _ir_estimated_control(ctx, ref, loop); return ref < loop; // TODO: check dominance instead of order } diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index e5c038fce8e5a..781c8e5269c29 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -1387,6 +1387,12 @@ op2_const: constraints->tmp_regs[n] = IR_TMP_REG(3, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n++; break; + case IR_ARGVAL: + constraints->tmp_regs[0] = IR_SCRATCH_REG(IR_REG_RSI, IR_DEF_SUB_REF - IR_SUB_REFS_COUNT, IR_USE_SUB_REF); + constraints->tmp_regs[1] = IR_SCRATCH_REG(IR_REG_RDI, IR_DEF_SUB_REF - IR_SUB_REFS_COUNT, IR_USE_SUB_REF); + constraints->tmp_regs[2] = IR_SCRATCH_REG(IR_REG_RCX, IR_DEF_SUB_REF - IR_SUB_REFS_COUNT, IR_USE_SUB_REF); + n = 3; + break; case IR_CALL: insn = &ctx->ir_base[ref]; if (IR_IS_TYPE_INT(insn->type)) { @@ -2431,6 +2437,11 @@ binop_fp: case IR_VAR: return IR_SKIPPED | IR_VAR; case IR_PARAM: +#ifndef _WIN64 + if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { + return IR_STATIC_ALLOCA; + } +#endif return ctx->use_lists[ref].count > 0 ? IR_PARAM : IR_SKIPPED | IR_PARAM; case IR_ALLOCA: /* alloca() may be used only in functions */ @@ -2976,6 +2987,8 @@ store_int: } } return IR_SKIPPED | IR_NOP; + case IR_ARGVAL: + return IR_FUSED | IR_ARGVAL; case IR_NOP: return IR_SKIPPED | IR_NOP; default: @@ -3153,6 +3166,17 @@ static void ir_emit_load_mem(ir_ctx *ctx, ir_type type, ir_reg reg, ir_mem mem) } } +static int32_t ir_local_offset(ir_ctx *ctx, ir_insn *insn) +{ + if (insn->op != IR_PARAM) { + IR_ASSERT(insn->op == IR_VAR || insn->op == IR_ALLOCA || insn->op == IR_VADDR); + return IR_SPILL_POS_TO_OFFSET(insn->op3); + } else { + IR_ASSERT(ctx->value_params && ctx->value_params[insn->op3 - 1].align); + return IR_SPILL_POS_TO_OFFSET(ctx->value_params[insn->op3 - 1].offset); + } +} + static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) { ir_backend_data *data = ctx->data; @@ -3166,8 +3190,7 @@ static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) if (var_insn->op == IR_VADDR) { var_insn = &ctx->ir_base[var_insn->op1]; } - IR_ASSERT(var_insn->op == IR_VAR || var_insn->op == IR_ALLOCA); - offset = IR_SPILL_POS_TO_OFFSET(var_insn->op3); + offset = ir_local_offset(ctx, var_insn); if (offset == 0) { | mov Ra(reg), Ra(base) } else { @@ -3385,7 +3408,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) case IR_LEA_OB: offset_insn = insn; if (ir_rule(ctx, insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; } else { @@ -3407,12 +3430,12 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) break; case IR_LEA_IB: if (ir_rule(ctx, insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = ref * sizeof(ir_ref) + 2; } else if (ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = ref * sizeof(ir_ref) + 1; @@ -3428,12 +3451,12 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) offset_insn = op1_insn; scale = 1; if (ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = insn->op1 * sizeof(ir_ref) + 1; } else if (ir_rule(ctx, op1_insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op1_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op1_insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = ref * sizeof(ir_ref) + 2; @@ -3447,12 +3470,12 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) offset_insn = op2_insn; scale = 1; if (ir_rule(ctx, insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = insn->op2 * sizeof(ir_ref) + 1; } else if (ir_rule(ctx, op2_insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op2_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op2_insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = ref * sizeof(ir_ref) + 1; @@ -3479,12 +3502,12 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) offset_insn = insn; scale = 1; if (ir_rule(ctx, op1_insn->op2) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op1_insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op1_insn->op2]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = insn->op1 * sizeof(ir_ref) + 1; } else if (ir_rule(ctx, op1_insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op1_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op1_insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = insn->op1 * sizeof(ir_ref) + 2; @@ -3500,7 +3523,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) op2_insn = &ctx->ir_base[insn->op2]; scale = ctx->ir_base[op2_insn->op2].val.i32; if (ir_rule(ctx, op1_insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op1_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op1_insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; } else { @@ -3514,7 +3537,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) op2_insn = &ctx->ir_base[insn->op2]; offset_insn = op2_insn; if (ir_rule(ctx, op2_insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op2_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op2_insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; } else { @@ -3523,7 +3546,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) break; case IR_LEA_B_SI: if (ir_rule(ctx, insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; } else { @@ -3537,7 +3560,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) case IR_LEA_SI_B: index_reg_ref = insn->op1 * sizeof(ir_ref) + 1; if (ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; } else { @@ -3580,7 +3603,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) offset_insn = NULL; break; case IR_ALLOCA: - offset = IR_SPILL_POS_TO_OFFSET(insn->op3); + offset = ir_local_offset(ctx, insn); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = index_reg_ref = IR_UNUSED; scale = 1; @@ -8306,7 +8329,7 @@ static void ir_emit_va_start(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (ctx->flags & IR_USE_FRAME_POINTER) { @@ -8340,7 +8363,7 @@ static void ir_emit_va_start(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (ctx->flags & IR_USE_FRAME_POINTER) { @@ -8407,7 +8430,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op2_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + op2_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (op3_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op3_reg)) { @@ -8418,7 +8441,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op3) == IR_STATIC_ALLOCA); op3_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op3_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op3].op3); + op3_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op3]); } | mov Ra(tmp_reg), aword [Ra(op3_reg)+op3_offset] | mov aword [Ra(op2_reg)+op2_offset], Ra(tmp_reg) @@ -8441,7 +8464,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op2_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + op2_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (op3_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op3_reg)) { @@ -8452,7 +8475,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op3) == IR_STATIC_ALLOCA); op3_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op3_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op3].op3); + op3_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op3]); } | mov Rd(tmp_reg), dword [Ra(op3_reg)+(op3_offset+offsetof(ir_va_list, gp_offset))] | mov dword [Ra(op2_reg)+(op2_offset+offsetof(ir_va_list, gp_offset))], Rd(tmp_reg) @@ -8493,11 +8516,29 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } | mov Ra(tmp_reg), aword [Ra(op2_reg)+offset] +#ifdef _WIN64 ir_emit_load_mem(ctx, type, def_reg, IR_MEM_B(tmp_reg)); | add Ra(tmp_reg), IR_MAX(ir_type_size[type], sizeof(void*)) +#else + if (!insn->op3) { + ir_emit_load_mem(ctx, type, def_reg, IR_MEM_B(tmp_reg)); + | add Ra(tmp_reg), IR_MAX(ir_type_size[type], sizeof(void*)) + } else { + IR_ASSERT(type == IR_ADDR); + int align = 1U << (insn->op3 & 0x7); + int size = (uint32_t)insn->op3 >> 3; + + if (align > (int)sizeof(void*)) { + | add Ra(tmp_reg), (align-1) + | and Ra(tmp_reg), ~(align-1) + } + | mov Ra(def_reg), Ra(tmp_reg) + | add Ra(tmp_reg), IR_ALIGNED_SIZE(size, sizeof(void*)) + } +#endif | mov aword [Ra(op2_reg)+offset], Ra(tmp_reg) if (IR_REG_SPILLED(ctx->regs[def][0])) { ir_emit_store(ctx, type, def, def_reg); @@ -8526,9 +8567,23 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } - if (IR_IS_TYPE_INT(type)) { + if (insn->op3) { + /* long struct arguemnt */ + IR_ASSERT(type == IR_ADDR); + int align = 1U << (insn->op3 & 0x7); + int size = (uint32_t)insn->op3 >> 3; + + | mov Ra(tmp_reg), aword [Ra(op2_reg)+(offset+offsetof(ir_va_list, overflow_arg_area))] + if (align > (int)sizeof(void*)) { + | add Ra(tmp_reg), (align-1) + | and Ra(tmp_reg), ~(align-1) + } + | mov Ra(def_reg), Ra(tmp_reg) + | add Ra(tmp_reg), IR_ALIGNED_SIZE(size, sizeof(void*)) + | mov aword [Ra(op2_reg)+(offset+offsetof(ir_va_list, overflow_arg_area))], Ra(tmp_reg) + } else if (IR_IS_TYPE_INT(type)) { | mov Rd(tmp_reg), dword [Ra(op2_reg)+(offset+offsetof(ir_va_list, gp_offset))] | cmp Rd(tmp_reg), sizeof(void*)*IR_REG_INT_ARGS | jge >1 @@ -8847,7 +8902,7 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) } } -static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) +static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn, int *copy_stack_ptr) { int j, n; ir_type type; @@ -8856,6 +8911,9 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) int int_reg_params_count = IR_REG_INT_ARGS; int fp_reg_params_count = IR_REG_FP_ARGS; int32_t used_stack = 0; +#ifdef _WIN64 + int32_t copy_stack = 0; +#endif #ifdef IR_HAVE_FASTCALL if (sizeof(void*) == 4 && ir_is_fastcall(ctx, insn)) { @@ -8866,8 +8924,26 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) n = insn->inputs_count; for (j = 3; j <= n; j++) { - type = ctx->ir_base[ir_insn_op(insn, j)].type; + ir_insn *arg = &ctx->ir_base[ir_insn_op(insn, j)]; + type = arg->type; if (IR_IS_TYPE_INT(type)) { + if (arg->op == IR_ARGVAL) { + int size = arg->op2; + int align = arg->op3; + +#ifdef _WIN64 + copy_stack += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack = IR_ALIGNED_SIZE(copy_stack, align); + type = IR_ADDR; +#else + align = IR_MAX((int)sizeof(void*), align); + used_stack = IR_ALIGNED_SIZE(used_stack, align); + used_stack += size; + used_stack = IR_ALIGNED_SIZE(used_stack, sizeof(void*)); + continue; +#endif + } if (int_param >= int_reg_params_count) { used_stack += IR_MAX(sizeof(void*), ir_type_size[type]); } @@ -8892,6 +8968,14 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) /* Reserved "home space" or "shadow store" for register arguments (used in Windows64 ABI) */ used_stack += IR_SHADOW_ARGS; +#ifdef _WIN64 + copy_stack = IR_ALIGNED_SIZE(copy_stack, 16); + used_stack += copy_stack; + *copy_stack_ptr = copy_stack; +#else + *copy_stack_ptr = 0; +#endif + return used_stack; } @@ -8911,7 +8995,7 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg int fp_reg_params_count = IR_REG_FP_ARGS; const int8_t *int_reg_params = _ir_int_reg_params; const int8_t *fp_reg_params = _ir_fp_reg_params; - int32_t used_stack, stack_offset = IR_SHADOW_ARGS; + int32_t used_stack, copy_stack = 0, stack_offset = IR_SHADOW_ARGS; ir_copy *copies; bool do_pass3 = 0; /* For temporaries we may use any scratch registers except for registers used for parameters */ @@ -8942,9 +9026,13 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg #endif ) { // TODO: support for preallocated stack +#ifdef _WIN64 + used_stack = ir_call_used_stack(ctx, insn, ©_stack); +#else used_stack = 0; +#endif } else { - used_stack = ir_call_used_stack(ctx, insn); + used_stack = ir_call_used_stack(ctx, insn, ©_stack); if (IR_SHADOW_ARGS && insn->op == IR_TAILCALL && used_stack == IR_SHADOW_ARGS) { @@ -8967,6 +9055,46 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg } } +#ifdef _WIN64 +|.if X64 + if (copy_stack) { + /* Copy struct arguments */ + int copy_stack_offset = 0; + + for (j = 3; j <= n; j++) { + arg = ir_insn_op(insn, j); + src_reg = ir_get_alocated_reg(ctx, def, j); + arg_insn = &ctx->ir_base[arg]; + type = arg_insn->type; + + if (arg_insn->op == IR_ARGVAL) { + /* make a stack copy */ + int size = arg_insn->op2; + int align = arg_insn->op3; + + copy_stack_offset += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack_offset = IR_ALIGNED_SIZE(copy_stack_offset, align); + src_reg = ctx->regs[arg][1]; + + | lea rdi, [rsp + (used_stack - copy_stack_offset)] + if (src_reg != IR_REG_NONE) { + if (IR_REG_SPILLED(src_reg)) { + src_reg = IR_REG_NUM(src_reg); + ir_emit_load(ctx, IR_ADDR, src_reg, arg_insn->op1); + } + | mov rsi, Ra(src_reg) + } else { + ir_emit_load(ctx, IR_ADDR, IR_REG_RSI, arg_insn->op1); + } + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_RCX, size); + | rep; movsb + } + } + } +|.endif +#endif + /* 1. move all register arguments that should be passed through stack * and collect arguments that should be passed through registers */ copies = ir_mem_malloc((n - 2) * sizeof(ir_copy)); @@ -8976,6 +9104,55 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg arg_insn = &ctx->ir_base[arg]; type = arg_insn->type; if (IR_IS_TYPE_INT(type)) { +#ifndef _WIN64 + if (arg_insn->op == IR_ARGVAL) { + int size = arg_insn->op2; + int align = arg_insn->op3; + align = IR_MAX((int)sizeof(void*), align); + stack_offset = IR_ALIGNED_SIZE(stack_offset, align); + if (size) { + src_reg = ctx->regs[arg][1]; + if (src_reg != IR_REG_NONE) { + if (IR_REG_SPILLED(src_reg)) { + src_reg = IR_REG_NUM(src_reg); + ir_emit_load(ctx, IR_ADDR, src_reg, arg_insn->op1); + } + if (src_reg != IR_REG_RSI) { + |.if X64 + | mov rsi, Ra(src_reg) + |.else + | mov esi, Ra(src_reg) + |.endif + } + } else { + ir_emit_load(ctx, IR_ADDR, IR_REG_RSI, arg_insn->op1); + } + if (stack_offset == 0) { + |.if X64 + | mov rdi, rsp + |.else + | mov edi, esp + |.endif + } else { + |.if X64 + | lea rdi, [rsp+stack_offset] + |.else + | lea edi, [esp+stack_offset] + |.endif + } + |.if X64 + | mov rcx, size + | rep; movsb + |.else + | mov ecx, size + | rep; movsb + |.endif + } + stack_offset += size; + stack_offset = IR_ALIGNED_SIZE(stack_offset, sizeof(void*)); + continue; + } +#endif if (int_param < int_reg_params_count) { dst_reg = int_reg_params[int_param]; } else { @@ -8985,6 +9162,10 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg #ifdef _WIN64 /* WIN64 calling convention use common couter for int and fp registers */ fp_param++; + if (arg_insn->op == IR_ARGVAL) { + do_pass3 = 3; + continue; + } #endif } else { IR_ASSERT(IR_IS_TYPE_FP(type)); @@ -9037,6 +9218,10 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg /* 3. move the remaining memory and immediate values */ if (do_pass3) { +#ifdef _WIN64 + int copy_stack_offset = 0; +#endif + stack_offset = IR_SHADOW_ARGS; int_param = 0; fp_param = 0; @@ -9046,6 +9231,37 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg arg_insn = &ctx->ir_base[arg]; type = arg_insn->type; if (IR_IS_TYPE_INT(type)) { + if (arg_insn->op == IR_ARGVAL) { + int size = arg_insn->op2; + int align = arg_insn->op3; + +#ifndef _WIN64 + align = IR_MAX((int)sizeof(void*), align); + stack_offset = IR_ALIGNED_SIZE(stack_offset, align); + stack_offset += size; + stack_offset = IR_ALIGNED_SIZE(stack_offset, sizeof(void*)); + continue; +#else +|.if X64 + /* pass pointer to the copy on stack */ + copy_stack_offset += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack_offset = IR_ALIGNED_SIZE(copy_stack_offset, align); + if (int_param < int_reg_params_count) { + dst_reg = int_reg_params[int_param]; + | lea Ra(dst_reg), [rsp + (used_stack - copy_stack_offset)] + } else { + | lea Ra(tmp_reg), [rsp + (used_stack - copy_stack_offset)] + ir_emit_store_mem_int(ctx, IR_ADDR, IR_MEM_BO(IR_REG_STACK_POINTER, stack_offset), tmp_reg); + stack_offset += sizeof(void*); + } + int_param++; + /* WIN64 calling convention use common couter for int and fp registers */ + fp_param++; + continue; +|.endif +#endif + } if (int_param < int_reg_params_count) { dst_reg = int_reg_params[int_param]; } else { @@ -9161,6 +9377,9 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg } } } + if (insn->op == IR_CALL && (ctx->flags & IR_PREALLOCATED_STACK)) { + used_stack = 0; + } #endif #ifdef IR_REG_VARARG_FP_REGS /* set hidden argument to specify the number of vector registers used */ @@ -10230,7 +10449,16 @@ static void ir_emit_load_params(ir_ctx *ctx) insn = &ctx->ir_base[use]; if (insn->op == IR_PARAM) { if (IR_IS_TYPE_INT(insn->type)) { - if (int_param_num < int_reg_params_count) { + if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { + /* struct passed by value on stack */ + size_t align = ctx->value_params[insn->op3 - 1].align; + + align = IR_MAX(sizeof(void*), align); + stack_offset = IR_ALIGNED_SIZE(stack_offset, align); + stack_offset += ctx->value_params[insn->op3 - 1].size; + stack_offset = IR_ALIGNED_SIZE(stack_offset, sizeof(void*)); + continue; + } else if (int_param_num < int_reg_params_count) { src_reg = int_reg_params[int_param_num]; } else { src_reg = IR_REG_NONE; @@ -10358,6 +10586,19 @@ static void ir_fix_param_spills(ir_ctx *ctx) insn = &ctx->ir_base[use]; if (insn->op == IR_PARAM) { if (IR_IS_TYPE_INT(insn->type)) { +#ifndef _WIN64 + if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { + /* struct passed by value on stack */ + size_t align = ctx->value_params[insn->op3 - 1].align; + + align = IR_MAX(sizeof(void*), align); + stack_offset = IR_ALIGNED_SIZE(stack_offset, align); + ctx->value_params[insn->op3 - 1].offset = stack_start + stack_offset; + stack_offset += ctx->value_params[insn->op3 - 1].size; + stack_offset = IR_ALIGNED_SIZE(stack_offset, sizeof(void*)); + continue; + } +#endif if (int_param_num < int_reg_params_count) { src_reg = int_reg_params[int_param_num]; } else { @@ -10618,13 +10859,13 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) static void ir_preallocate_call_stack(ir_ctx *ctx) { - int call_stack_size, peak_call_stack_size = 0; + int call_stack_size, copy_stack, peak_call_stack_size = 0; ir_ref i, n; ir_insn *insn; for (i = 1, insn = ctx->ir_base + 1; i < ctx->insns_count;) { if (insn->op == IR_CALL) { - call_stack_size = ir_call_used_stack(ctx, insn); + call_stack_size = ir_call_used_stack(ctx, insn, ©_stack); if (call_stack_size > peak_call_stack_size #ifdef IR_HAVE_FASTCALL && !ir_is_fastcall(ctx, insn) /* fast call functions restore stack pointer */ From 1302b9f6da16303ad3f3d23f98061c75261d6bcf Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 22 Sep 2025 20:14:21 +0300 Subject: [PATCH 34/83] Update IR IR commit: 425ca45ffed99b6d3085c6a7f7c9d4fb3c2b5737 --- ext/opcache/jit/ir/ir_aarch64.dasc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/jit/ir/ir_aarch64.dasc b/ext/opcache/jit/ir/ir_aarch64.dasc index 4cdf4d68c2179..2caabb3ebebbb 100644 --- a/ext/opcache/jit/ir/ir_aarch64.dasc +++ b/ext/opcache/jit/ir/ir_aarch64.dasc @@ -4457,7 +4457,7 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = ir_local_offset(ct, &ctx->ir_base[insn->op2]); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } | ldr Rx(tmp_reg), [Rx(op2_reg), #offset] ir_emit_load_mem(ctx, type, def_reg, IR_MEM_BO(tmp_reg, 0)); From a885372a081e76975ce8696e918c35fe1a4864d3 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 23 Sep 2025 14:14:09 +0200 Subject: [PATCH 35/83] Fix more curl 8.16 issues The CURLOPT_FOLLOWLOCATION seems like a gcc bug, where the integer extension of bool to long is lost, but I was unable to reproduce on godbolt.org. --- ext/curl/interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 2f0181504198b..bf8703278b5d0 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1689,7 +1689,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue lval = zval_get_long(zvalue); if (lval == 1) { php_error_docref(NULL, E_NOTICE, "CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead"); - error = curl_easy_setopt(ch->cp, option, 2); + error = curl_easy_setopt(ch->cp, option, 2L); break; } ZEND_FALLTHROUGH; @@ -2260,7 +2260,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue case CURLOPT_FOLLOWLOCATION: lval = zend_is_true(zvalue); - error = curl_easy_setopt(ch->cp, option, lval); + error = curl_easy_setopt(ch->cp, option, (long) lval); break; case CURLOPT_HEADERFUNCTION: From c6a6cb79f9ea30d97602fd5d01fe2c1ad9659a4f Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 22 Sep 2025 19:46:17 +0200 Subject: [PATCH 36/83] [skip ci] Skip php_cli_server_pdeathsig.phpt if pgrep is missing Fixes GH-19911 Closes GH-19923 --- sapi/cli/tests/php_cli_server_pdeathsig.phpt | 1 + 1 file changed, 1 insertion(+) diff --git a/sapi/cli/tests/php_cli_server_pdeathsig.phpt b/sapi/cli/tests/php_cli_server_pdeathsig.phpt index 8b70bbcad4927..66a76a84ccaf6 100644 --- a/sapi/cli/tests/php_cli_server_pdeathsig.phpt +++ b/sapi/cli/tests/php_cli_server_pdeathsig.phpt @@ -9,6 +9,7 @@ if (!(str_contains(PHP_OS, 'Linux') || str_contains(PHP_OS, 'FreeBSD'))) { die('skip PDEATHSIG is only supported on Linux and FreeBSD'); } if (@file_exists('/.dockerenv')) die("skip Broken in Docker"); +if (!shell_exec("which pgrep")) die("skip Missing pgrep command"); ?> --FILE-- Date: Tue, 23 Sep 2025 23:41:42 +0200 Subject: [PATCH 37/83] [skip ci] Add 8.6 to nightly_matrix.php --- .github/nightly_matrix.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/nightly_matrix.php b/.github/nightly_matrix.php index 311176ef5e53e..846c60e8fd56d 100644 --- a/.github/nightly_matrix.php +++ b/.github/nightly_matrix.php @@ -1,7 +1,8 @@ 'master', 'version' => [8, 5]], + ['ref' => 'master', 'version' => [8, 6]], + ['ref' => 'PHP-8.5', 'version' => [8, 5]], ['ref' => 'PHP-8.4', 'version' => [8, 4]], ['ref' => 'PHP-8.3', 'version' => [8, 3]], ['ref' => 'PHP-8.2', 'version' => [8, 2]], From 6118c19ab8b5758bae98332dd9c9ca1131df5b4a Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 25 Sep 2025 12:19:27 +0200 Subject: [PATCH 38/83] zip is now 1.22.7 --- ext/zip/php_zip.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index 99409d3fc89d4..e08905e319450 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -39,7 +39,7 @@ extern zend_module_entry zip_module_entry; /* Additionnal flags not from libzip */ #define ZIP_FL_OPEN_FILE_NOW (1u<<30) -#define PHP_ZIP_VERSION "1.22.6" +#define PHP_ZIP_VERSION "1.22.7" #ifdef HAVE_LIBZIP_VERSION #define LIBZIP_VERSION_STR zip_libzip_version() From f9dc073b04b4b2bcfd46d39f140d2747ac5f8c2f Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 27 Sep 2025 12:43:28 +0200 Subject: [PATCH 39/83] Add stream filter convert.* tests (#19976) The convert.* filters are quite untested so this tries to improve it. Closes GH-19976 --- ext/standard/tests/filters/convert.phpt | 130 ++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 ext/standard/tests/filters/convert.phpt diff --git a/ext/standard/tests/filters/convert.phpt b/ext/standard/tests/filters/convert.phpt new file mode 100644 index 0000000000000..a99579b5dd8fe --- /dev/null +++ b/ext/standard/tests/filters/convert.phpt @@ -0,0 +1,130 @@ +--TEST-- +convert stream filter tests +--FILE-- + 20, 'line-break-chars' => "\n"); +test_roundtrip("Long text that will be wrapped", "convert.base64-encode", "convert.base64-decode", $opts); + +$opts2 = array('binary' => true); +test_roundtrip("Text\t\r\n", "convert.quoted-printable-encode", "convert.quoted-printable-decode", $opts2); + +$fp = tmpfile(); +fwrite($fp, "Test"); +rewind($fp); +stream_filter_prepend($fp, 'convert.base64-encode', STREAM_FILTER_READ); +$result = stream_get_contents($fp); +fclose($fp); +var_dump($result === base64_encode("Test")); + +?> +--EXPECTF-- +Filter: convert.base64-encode +Original: Hello World! +Encoded: SGVsbG8gV29ybGQh +Decoded: Hello World! +bool(true) + +Filter: convert.base64-encode +Original (hex): 414243ff +Encoded: QUJD/w== +Decoded (hex): 414243ff +bool(true) + +Filter: convert.base64-encode +Original: +Encoded: +Decoded: +bool(true) + +Filter: convert.base64-encode +Original: A +Encoded: QQ== +Decoded: A +bool(true) + +Filter: convert.quoted-printable-encode +Original: Hello World! +Encoded: Hello World! +Decoded: Hello World! +bool(true) + +Filter: convert.quoted-printable-encode +Original: Line1 +Line2 +Encoded: Line1=0D=0ALine2 +Decoded: Line1 +Line2 +bool(true) + +Filter: convert.base64-encode +Original: Long text that will be wrapped +Encoded: TG9uZyB0ZXh0IHRoYXQg +d2lsbCBiZSB3cmFwcGVk +Decoded: Long text that will be wrapped +bool(true) + +Filter: convert.quoted-printable-encode +Original: Text + +Encoded: Text=09=0D=0A +Decoded: Text + +bool(true) + +bool(true) From 107075605db6f1b15ce275e345d1c6de04a464b0 Mon Sep 17 00:00:00 2001 From: Shivam Mathur Date: Sun, 28 Sep 2025 09:54:13 +0530 Subject: [PATCH 40/83] Change master branch check to 8.6 --- .github/scripts/windows/find-target-branch.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/windows/find-target-branch.bat b/.github/scripts/windows/find-target-branch.bat index a0b47f2488946..44b0bde1ec8ca 100644 --- a/.github/scripts/windows/find-target-branch.bat +++ b/.github/scripts/windows/find-target-branch.bat @@ -3,6 +3,6 @@ for /f "usebackq tokens=3" %%i in (`findstr PHP_MAJOR_VERSION main\php_version.h`) do set BRANCH=%%i for /f "usebackq tokens=3" %%i in (`findstr PHP_MINOR_VERSION main\php_version.h`) do set BRANCH=%BRANCH%.%%i -if /i "%BRANCH%" equ "8.5" ( +if /i "%BRANCH%" equ "8.6" ( set BRANCH=master ) From d94846c3b4d6cfc2564b59aebcb9abe6b347ace6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 29 Sep 2025 15:18:03 +0200 Subject: [PATCH 41/83] Fix GH-19988: zend_string_init with NULL pointer in simplexml (UB) Normally, simplexml cannot import document nodes, but xsl allows to circumvent this. A document does not have a name, so we return the empty string in that case. While we could add an explicit check, we might as well switch the macro to a form that would be more optimal anyway as many tag names can be single characters. The test was added in xsl because adding it in simplexml would break out-of-tree builds of simplexml. Closes GH-19990. --- NEWS | 4 ++++ ext/simplexml/simplexml.c | 2 +- ext/xsl/tests/gh19988.phpt | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 ext/xsl/tests/gh19988.phpt diff --git a/NEWS b/NEWS index fc3c0662b9328..249ef8f98ed8e 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,10 @@ PHP NEWS of the curl_copy_handle() function to clone a CurlHandle. (timwolla) . Fix curl build failure on macOS+curl 8.16. (nielsdos) +- SimpleXML: + . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). + (nielsdos) + - Soap: . Fixed bug GH-19784 (SoapServer memory leak). (nielsdos) diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 11f497a6673ea..6eae5650340c5 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -1661,7 +1661,7 @@ PHP_METHOD(SimpleXMLElement, getName) node = php_sxe_get_first_node(sxe, node); if (node) { namelen = xmlStrlen(node->name); - RETURN_STRINGL((char*)node->name, namelen); + RETURN_STRINGL_FAST((const char *) node->name, namelen); } else { RETURN_EMPTY_STRING(); } diff --git a/ext/xsl/tests/gh19988.phpt b/ext/xsl/tests/gh19988.phpt new file mode 100644 index 0000000000000..174af282f9c09 --- /dev/null +++ b/ext/xsl/tests/gh19988.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-19988 (zend_string_init with NULL pointer in simplexml (UB)) +--EXTENSIONS-- +simplexml +xsl +--CREDITS-- +YuanchengJiang +--FILE-- +load(__DIR__ . '/53965/collection.xsl'); +$processor->importStylesheet($dom); +$result = $processor->transformToDoc($sxe, SimpleXMLElement::class); +var_dump($result->getName()); +?> +--EXPECT-- +string(0) "" From 190f427198b2e789f33f8ba8a225a9140458786a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 29 Sep 2025 16:05:33 +0200 Subject: [PATCH 42/83] Fix NEWS order --- NEWS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 249ef8f98ed8e..3955fab9ed22d 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,11 @@ PHP NEWS . Fixed bug GH-19480 (error_log php.ini cannot be unset when open_basedir is configured). (nielsdos) +- Curl: + . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead + of the curl_copy_handle() function to clone a CurlHandle. (timwolla) + . Fix curl build failure on macOS+curl 8.16. (nielsdos) + - Date: . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. (nielsdos) @@ -22,11 +27,6 @@ PHP NEWS - DBA: . Fixed GH-19885 (dba_fetch() overflow on skip argument). (David Carlier) -- Curl: - . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead - of the curl_copy_handle() function to clone a CurlHandle. (timwolla) - . Fix curl build failure on macOS+curl 8.16. (nielsdos) - - SimpleXML: . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). (nielsdos) From 3ee56f68edb2d1c2a3dc3713d499af42f37b00ad Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 23 Sep 2025 19:02:14 +0100 Subject: [PATCH 43/83] Fix GH-19932: Zip::setEncryptionName()/setEncryptionIndex() memory leak. On successive usage, the password is copied as much but the older address is never freed. Thus, we are hinting a password reset to address it. close GH-19936 --- NEWS | 2 ++ ext/zip/php_zip.c | 10 ++++++++++ ext/zip/tests/gh19932.phpt | 25 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 ext/zip/tests/gh19932.phpt diff --git a/NEWS b/NEWS index 3955fab9ed22d..26860c58ee94e 100644 --- a/NEWS +++ b/NEWS @@ -43,6 +43,8 @@ PHP NEWS - Zip: . Fixed bug GH-19688 (Remove pattern overflow in zip addGlob()). (nielsdos) + . Fixed bug GH-19932 (Memory leak in zip setEncryptionName()/setEncryptionIndex()). + (David Carlier) 25 Sep 2025, PHP 8.3.26 diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 3613fb0f7ca7a..552d3a7571de5 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -2380,6 +2380,11 @@ PHP_METHOD(ZipArchive, setEncryptionName) RETURN_FALSE; } + if (UNEXPECTED(zip_file_set_encryption(intern, idx, ZIP_EM_NONE, NULL) < 0)) { + php_error_docref(NULL, E_WARNING, "password reset failed"); + RETURN_FALSE; + } + if (zip_file_set_encryption(intern, idx, (zip_uint16_t)method, password)) { RETURN_FALSE; } @@ -2403,6 +2408,11 @@ PHP_METHOD(ZipArchive, setEncryptionIndex) ZIP_FROM_OBJECT(intern, self); + if (UNEXPECTED(zip_file_set_encryption(intern, index, ZIP_EM_NONE, NULL) < 0)) { + php_error_docref(NULL, E_WARNING, "password reset failed"); + RETURN_FALSE; + } + if (zip_file_set_encryption(intern, index, (zip_uint16_t)method, password)) { RETURN_FALSE; } diff --git a/ext/zip/tests/gh19932.phpt b/ext/zip/tests/gh19932.phpt new file mode 100644 index 0000000000000..760fa1c9e766d --- /dev/null +++ b/ext/zip/tests/gh19932.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-19932 (ZipArchive::setEncryptionName()/setEncryptionIndex() memory leak) +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +open(__DIR__ . "/gh19932.zip", ZipArchive::CREATE); +$zip->addFromString("test.txt", "test"); +$zip->setEncryptionName("test.txt", ZipArchive::EM_AES_256, "password"); +$zip->setEncryptionName("test.txt", ZipArchive::EM_AES_256, "password"); +$zip->setEncryptionIndex("0", ZipArchive::EM_AES_256, "password"); +$zip->setEncryptionIndex("0", ZipArchive::EM_AES_256, "password"); +$zip->close(); +echo "OK"; +?> +--CLEAN-- + +--EXPECT-- +OK + From 93bac8cb1a7230ac9d34a7cb8f392aeaa621723d Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 22 Sep 2025 19:02:46 +0100 Subject: [PATCH 44/83] Fix GH-19922: gzopen() double free close GH-19924 --- NEWS | 3 +++ ext/zlib/tests/gh19922.phpt | 12 ++++++++++++ main/streams/streams.c | 9 +-------- 3 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 ext/zlib/tests/gh19922.phpt diff --git a/NEWS b/NEWS index 26860c58ee94e..a7f5149c5e215 100644 --- a/NEWS +++ b/NEWS @@ -46,6 +46,9 @@ PHP NEWS . Fixed bug GH-19932 (Memory leak in zip setEncryptionName()/setEncryptionIndex()). (David Carlier) +- Zlib: + . Fixed bug GH-19922 (Double free on gzopen). (David Carlier) + 25 Sep 2025, PHP 8.3.26 - Core: diff --git a/ext/zlib/tests/gh19922.phpt b/ext/zlib/tests/gh19922.phpt new file mode 100644 index 0000000000000..71644512e6656 --- /dev/null +++ b/ext/zlib/tests/gh19922.phpt @@ -0,0 +1,12 @@ +--TEST-- +GH-19922 (gzopen double free on debug build and unseekable stream) +--EXTENSIONS-- +zlib +--FILE-- + +--EXPECTF-- + +Warning: gzopen(php://output): could not make seekable - php://output in %s on line %d +bool(false) diff --git a/main/streams/streams.c b/main/streams/streams.c index 7a1b521108257..6dc073cd0baa3 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -2219,7 +2219,6 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod int persistent = options & STREAM_OPEN_PERSISTENT; zend_string *path_str = NULL; zend_string *resolved_path = NULL; - char *copy_of_path = NULL; if (opened_path) { if (options & STREAM_OPEN_FOR_ZEND_STREAM) { @@ -2296,8 +2295,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (stream->orig_path) { pefree(stream->orig_path, persistent); } - copy_of_path = pestrdup(path, persistent); - stream->orig_path = copy_of_path; + stream->orig_path = pestrdup(path, persistent); #if ZEND_DEBUG stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; @@ -2356,11 +2354,6 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod } } php_stream_tidy_wrapper_error_log(wrapper); -#if ZEND_DEBUG - if (stream == NULL && copy_of_path != NULL) { - pefree(copy_of_path, persistent); - } -#endif if (resolved_path) { zend_string_release_ex(resolved_path, 0); } From e029f8f45b6d3f1a468d5e8d476e99c01fffdada Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 25 Sep 2025 19:49:38 +0100 Subject: [PATCH 45/83] Fix GH-19955: imagefttext() memory leak close GH-19968 --- NEWS | 3 +++ ext/gd/libgd/gdkanji.c | 2 ++ ext/gd/tests/gh19955.phpt | 15 +++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 ext/gd/tests/gh19955.phpt diff --git a/NEWS b/NEWS index a7f5149c5e215..58d9ebc69ad07 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,9 @@ PHP NEWS - DBA: . Fixed GH-19885 (dba_fetch() overflow on skip argument). (David Carlier) +- GD: + . FIxed GH-19955 (imagefttext() memory leak). (David Carlier) + - SimpleXML: . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). (nielsdos) diff --git a/ext/gd/libgd/gdkanji.c b/ext/gd/libgd/gdkanji.c index 21bc2280982a8..ef769f89badda 100644 --- a/ext/gd/libgd/gdkanji.c +++ b/ext/gd/libgd/gdkanji.c @@ -368,6 +368,8 @@ do_convert (unsigned char *to, unsigned char *from, const char *code) else error ("something happen"); strcpy ((char *) to, (const char *) from); + if (iconv_close (cd) != 0) + error ("iconv_close() error"); return; } diff --git a/ext/gd/tests/gh19955.phpt b/ext/gd/tests/gh19955.phpt new file mode 100644 index 0000000000000..a4b58e403cf40 --- /dev/null +++ b/ext/gd/tests/gh19955.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-19955: (imagefttext() memory leak) +--EXTENSIONS-- +gd +--CREDITS-- +YuanchengJiang +--FILE-- + +--EXPECT-- +OK From 21c2c07a249ff03d0d1dff3b06545dbd3322e86f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:06:21 +0200 Subject: [PATCH 46/83] Fix GH-19998: ext/standard/tests/file/bug46347.phpt sometimes fails: racy in parallel The same test file name is already used in parse_ini_file.phpt. Closes GH-20000. --- ext/standard/tests/file/bug46347.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/file/bug46347.phpt b/ext/standard/tests/file/bug46347.phpt index 903a6e35cc2b2..e337ade9eb8fb 100644 --- a/ext/standard/tests/file/bug46347.phpt +++ b/ext/standard/tests/file/bug46347.phpt @@ -8,14 +8,14 @@ $str = <<< EOF part1.*.part2 = 1 EOF; -$file = __DIR__ . '/parse.ini'; +$file = __DIR__ . '/bug46347.ini'; file_put_contents($file, $str); var_dump(parse_ini_file($file)); ?> --CLEAN-- --EXPECT-- array(1) { From 6db12e7cd89fa4643f42a884e8323a3aef1b52cc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 7 Sep 2025 21:08:10 +0200 Subject: [PATCH 47/83] Fix bug #67563: mysqli compiled with mysqlnd does not take ipv6 adress as parameter In the past, when libmysqlclient could be used, it accepted ipv6 addresses as hostname without enclosing it first in brackets. However, in mysqlnd this never worked. In the past this caused a discrepancy between the two implementations. Nowadays, mysqli only works with mysqlnd so we don't even have to cater to libmysqlclient. However, a plain ipv6 address should still work as a hostname. Also for people migrating to newer PHP versions it's nice if this keeps working. The solution is to check if we're dealing with an ipv6 address not yet enclosed in brackets. In that case we add the brackets automatically. Closes GH-19750. --- NEWS | 4 ++++ ext/mysqli/tests/bug67563.phpt | 40 ++++++++++++++++++++++++++++++++ ext/mysqlnd/mysqlnd_connection.c | 18 +++++++++++++- 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 ext/mysqli/tests/bug67563.phpt diff --git a/NEWS b/NEWS index 58d9ebc69ad07..067cceab3be13 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,10 @@ PHP NEWS - GD: . FIxed GH-19955 (imagefttext() memory leak). (David Carlier) +- MySQLnd: + . Fixed bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress + as parameter). (nielsdos) + - SimpleXML: . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). (nielsdos) diff --git a/ext/mysqli/tests/bug67563.phpt b/ext/mysqli/tests/bug67563.phpt new file mode 100644 index 0000000000000..72a51ecdccd2c --- /dev/null +++ b/ext/mysqli/tests/bug67563.phpt @@ -0,0 +1,40 @@ +--TEST-- +Fix bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress as parameter) +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--INI-- +max_execution_time=240 +--FILE-- +close(); + } +} + +print "done!"; +?> +--EXPECT-- +done! diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index d46029889fad0..a97f2820a3190 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -513,6 +513,16 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn, } /* }}} */ +/* ipv6 addresses have at least two colons, which is how we can differentiate between domain names and addresses */ +static bool mysqlnd_fast_is_ipv6_address(const char *s) +{ + const char *first_colon = strchr(s, ':'); + if (!first_colon) { + return false; + } + return strchr(first_colon + 1, ':') != NULL; +} + /* {{{ mysqlnd_conn_data::get_scheme */ static MYSQLND_STRING MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING *socket_or_pipe, unsigned int port, bool * unix_socket, bool * named_pipe) @@ -542,7 +552,13 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_ if (!port) { port = 3306; } - transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port); + + /* ipv6 addresses are in the format [address]:port */ + if (hostname.s[0] != '[' && mysqlnd_fast_is_ipv6_address(hostname.s)) { + transport.l = mnd_sprintf(&transport.s, 0, "tcp://[%s]:%u", hostname.s, port); + } else { + transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port); + } } DBG_INF_FMT("transport=%s", transport.s? transport.s:"OOM"); DBG_RETURN(transport); From d9ee711baf7ce799b3b1cd8d7c726aa62bbb878d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:21:47 +0200 Subject: [PATCH 48/83] Fix NEWS formatting --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 067cceab3be13..d1d7335c132e6 100644 --- a/NEWS +++ b/NEWS @@ -28,7 +28,7 @@ PHP NEWS . Fixed GH-19885 (dba_fetch() overflow on skip argument). (David Carlier) - GD: - . FIxed GH-19955 (imagefttext() memory leak). (David Carlier) + . Fixed GH-19955 (imagefttext() memory leak). (David Carlier) - MySQLnd: . Fixed bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress From c617afd6d6e46ebbb896bab96761df2168be82d1 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:55:33 +0200 Subject: [PATCH 49/83] Fix GH-20009: XMLReader leak on RelaxNG schema failure Closes GH-20014. --- NEWS | 3 +++ ext/xmlreader/php_xmlreader.c | 1 + 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index d1d7335c132e6..c7765950f8cc8 100644 --- a/NEWS +++ b/NEWS @@ -48,6 +48,9 @@ PHP NEWS . Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()). (alexandre-daubois) +- XMLReader: + . Fixed bug GH-20009 (XMLReader leak on RelaxNG schema failure). (nielsdos) + - Zip: . Fixed bug GH-19688 (Remove pattern overflow in zip addGlob()). (nielsdos) . Fixed bug GH-19932 (Memory leak in zip setEncryptionName()/setEncryptionIndex()). diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index dca1898f1c044..7a50b660fb59d 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -508,6 +508,7 @@ static void php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAMETERS, int t RETURN_TRUE; } else { + xmlRelaxNGFree(schema); php_error_docref(NULL, E_WARNING, "Schema contains errors"); RETURN_FALSE; } From 7c859268c0306ccf237d18ef8cae97e60b1ac278 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:48:42 +0200 Subject: [PATCH 50/83] Fix memory leak and invalid continuation after tar header writing fails Closes GH-20003. --- NEWS | 4 ++ ext/phar/tar.c | 17 ++++---- .../tests/tar_flush_too_long_filename.phpt | 41 +++++++++++++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 ext/phar/tests/tar_flush_too_long_filename.phpt diff --git a/NEWS b/NEWS index c7765950f8cc8..9f32973e94a23 100644 --- a/NEWS +++ b/NEWS @@ -34,6 +34,10 @@ PHP NEWS . Fixed bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress as parameter). (nielsdos) +- Phar: + . Fix memory leak and invalid continuation after tar header writing fails. + (nielsdos) + - SimpleXML: . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). (nielsdos) diff --git a/ext/phar/tar.c b/ext/phar/tar.c index 687ca34c173c9..e675b86943217 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -1211,7 +1211,16 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int } zend_hash_apply_with_argument(&phar->manifest, phar_tar_writeheaders, (void *) &pass); - /* TODO: memory leak and incorrect continuation if phar_tar_writeheaders fails? */ + + if (error && *error) { + if (closeoldfile) { + php_stream_close(oldfile); + } + + /* on error in the hash iterator above, error is set */ + php_stream_close(newfile); + return EOF; + } /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */ if (!phar->is_data || phar->sig_flags) { @@ -1294,12 +1303,6 @@ int phar_tar_flush(phar_archive_data *phar, char *user_stub, zend_long len, int php_stream_close(oldfile); } - /* on error in the hash iterator above, error is set */ - if (error && *error) { - php_stream_close(newfile); - return EOF; - } - if (phar->fp && pass.free_fp) { php_stream_close(phar->fp); } diff --git a/ext/phar/tests/tar_flush_too_long_filename.phpt b/ext/phar/tests/tar_flush_too_long_filename.phpt new file mode 100644 index 0000000000000..be18f5e481688 --- /dev/null +++ b/ext/phar/tests/tar_flush_too_long_filename.phpt @@ -0,0 +1,41 @@ +--TEST-- +Tar flush with too long file name +--EXTENSIONS-- +phar +--SKIPIF-- + +--INI-- +phar.require_hash=0 +--FILE-- +addEmptyDir('blah1/'); +$phar->setSignatureAlgorithm(Phar::OPENSSL, "randomcrap"); +try { + $phar->addEmptyDir('blah2/' . str_repeat('X', 1000)); +} catch (PharException $e) { + echo $e->getMessage(); +} + +?> +--CLEAN-- + +--EXPECTF-- +tar-based phar "%s" cannot be created, filename "%s" is too long for tar file format From 9bd9e3a1f93a5b7bd624857e94eb6961797cfd90 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 1 Oct 2025 10:54:41 +0200 Subject: [PATCH 51/83] Fix build --- ext/phar/tar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/phar/tar.c b/ext/phar/tar.c index c6cc50ce8bf62..63a9cdbf88fd1 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -1172,13 +1172,13 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def zend_hash_apply_with_argument(&phar->manifest, phar_tar_writeheaders, (void *) &pass); if (error && *error) { - if (closeoldfile) { + if (must_close_old_file) { php_stream_close(oldfile); } /* on error in the hash iterator above, error is set */ php_stream_close(newfile); - return EOF; + return; } /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */ From b7fdfb71478f55d6ebf6c812c467ed260ad26d80 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sat, 26 Jul 2025 17:22:28 +0200 Subject: [PATCH 52/83] Fix GH-19248: Use strerror_r instead of strerror in main Or on Windows it is going to use either FormatMessageW or strerror_s for compatibility with previous error messages. It also needs to accomodate for GNU and BSD versions of strerror_r returning different type. Closes GH-19251 --- NEWS | 4 ++ main/network.c | 76 +++++++++++++++++++++++++++++++----- main/php_network.h | 5 +++ main/rfc1867.c | 3 +- main/streams/plain_wrapper.c | 47 +++++++++++++++------- main/streams/streams.c | 3 +- main/streams/xp_socket.c | 3 +- 7 files changed, 114 insertions(+), 27 deletions(-) diff --git a/NEWS b/NEWS index 9f32973e94a23..e60af85379c82 100644 --- a/NEWS +++ b/NEWS @@ -52,6 +52,10 @@ PHP NEWS . Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()). (alexandre-daubois) +- Streams: + . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). + (Jakub Zelenka) + - XMLReader: . Fixed bug GH-20009 (XMLReader leak on RelaxNG schema failure). (nielsdos) diff --git a/main/network.c b/main/network.c index 8de81a6271a2f..7102dad20798f 100644 --- a/main/network.c +++ b/main/network.c @@ -1052,6 +1052,28 @@ PHPAPI int php_sockaddr_size(php_sockaddr_storage *addr) } /* }}} */ +#ifdef PHP_WIN32 +char *php_socket_strerror_s(long err, char *buf, size_t bufsize) +{ + if (buf == NULL) { + char ebuf[1024]; + errno_t res = strerror_s(ebuf, sizeof(ebuf), err); + if (res == 0) { + buf = estrdup(ebuf); + } else { + buf = estrdup("Unknown error"); + } + } else { + errno_t res = strerror_s(buf, bufsize, err); + if (res != 0) { + strncpy(buf, "Unknown error", bufsize); + buf[bufsize?(bufsize-1):0] = 0; + } + } + return buf; +} +#endif + /* Given a socket error code, if buf == NULL: * emallocs storage for the error message and returns * else @@ -1061,16 +1083,40 @@ PHPAPI int php_sockaddr_size(php_sockaddr_storage *addr) PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize) { #ifndef PHP_WIN32 - char *errstr; - - errstr = strerror(err); +# ifdef HAVE_STRERROR_R + if (buf == NULL) { + char ebuf[1024]; +# ifdef STRERROR_R_CHAR_P + char *errstr = strerror_r(err, ebuf, sizeof(ebuf)); + buf = estrdup(errstr); +# else + errno_t res = strerror_r(err, ebuf, sizeof(ebuf)); + if (res == 0) { + buf = estrdup(ebuf); + } else { + buf = estrdup("Unknown error"); + } +# endif + } else { +# ifdef STRERROR_R_CHAR_P + buf = strerror_r(err, buf, bufsize); +# else + errno_t res = strerror_r(err, buf, bufsize); + if (res != 0) { + strncpy(buf, "Unknown error", bufsize); + buf[bufsize?(bufsize-1):0] = 0; + } +# endif + } +# else + char *errstr = strerror(err); if (buf == NULL) { buf = estrdup(errstr); } else { strncpy(buf, errstr, bufsize); buf[bufsize?(bufsize-1):0] = 0; } - return buf; +# endif #else char *sysbuf = php_win32_error_to_msg(err); if (!sysbuf[0]) { @@ -1085,9 +1131,8 @@ PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize) } php_win32_error_msg_free(sysbuf); - - return buf; #endif + return buf; } /* }}} */ @@ -1095,9 +1140,22 @@ PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize) PHPAPI zend_string *php_socket_error_str(long err) { #ifndef PHP_WIN32 - char *errstr; - - errstr = strerror(err); +# ifdef HAVE_STRERROR_R + char ebuf[1024]; +# ifdef STRERROR_R_CHAR_P + char *errstr = strerror_r(err, ebuf, sizeof(ebuf)); +# else + const char *errstr; + errno_t res = strerror_r(err, ebuf, sizeof(ebuf)); + if (res == 0) { + errstr = ebuf; + } else { + errstr = "Unknown error"; + } +# endif +# else + char *errstr = strerror(err); +# endif return zend_string_init(errstr, strlen(errstr), 0); #else zend_string *ret; diff --git a/main/php_network.h b/main/php_network.h index 6fc210b56837b..6b6e486fe48fc 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -66,6 +66,11 @@ * unless buf is not NULL. * Also works sensibly for win32 */ BEGIN_EXTERN_C() +#ifdef PHP_WIN32 +char *php_socket_strerror_s(long err, char *buf, size_t bufsize); +#else +#define php_socket_strerror_s php_socket_strerror +#endif PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize); PHPAPI zend_string *php_socket_error_str(long err); END_EXTERN_C() diff --git a/main/rfc1867.c b/main/rfc1867.c index cbdf8aaa07c5d..43c4185677a45 100644 --- a/main/rfc1867.c +++ b/main/rfc1867.c @@ -1038,7 +1038,8 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ if (wlen == (size_t)-1) { /* write failed */ #if DEBUG_FILE_UPLOAD - sapi_module.sapi_error(E_NOTICE, "write() failed - %s", strerror(errno)); + char errstr[256]; + sapi_module.sapi_error(E_NOTICE, "write() failed - %s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); #endif cancel_upload = PHP_UPLOAD_ERROR_F; } else if (wlen < blen) { diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 1d5b7cfdac40d..9ac049f746847 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -368,7 +368,9 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun return bytes_written; } if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { - php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", count, errno, strerror(errno)); + char errstr[256]; + php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", + count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } } else { @@ -444,7 +446,9 @@ static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count) /* TODO: Should this be treated as a proper error or not? */ } else { if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { - php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", count, errno, strerror(errno)); + char errstr[256]; + php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", + count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } /* TODO: Remove this special-case? */ @@ -1278,7 +1282,9 @@ static int php_plain_files_unlink(php_stream_wrapper *wrapper, const char *url, ret = VCWD_UNLINK(url); if (ret == -1) { if (options & REPORT_ERRORS) { - php_error_docref1(NULL, url, E_WARNING, "%s", strerror(errno)); + char errstr[256]; + php_error_docref1(NULL, url, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1324,6 +1330,7 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f if (ret == -1) { #ifndef PHP_WIN32 + char errstr[256]; # ifdef EXDEV if (errno == EXDEV) { zend_stat_t sb; @@ -1344,7 +1351,8 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f * access to the file in the meantime. */ if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } @@ -1352,7 +1360,8 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f if (success) { if (VCWD_CHMOD(url_to, sb.st_mode)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } @@ -1363,10 +1372,12 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f VCWD_UNLINK(url_from); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); } # if !defined(ZTS) && !defined(TSRM_WIN32) umask(oldmask); @@ -1379,7 +1390,8 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f #ifdef PHP_WIN32 php_win32_docref2_from_error(GetLastError(), url_from, url_to); #else - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); #endif return 0; } @@ -1449,11 +1461,12 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i if (!p) { p = buf; } + char errstr[256]; while (true) { int ret = VCWD_MKDIR(buf, (mode_t) mode); if (ret < 0 && errno != EEXIST) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); + php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1473,7 +1486,7 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i /* issue a warning to client when the last directory was created failed */ if (ret < 0) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); + php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1492,15 +1505,16 @@ static int php_plain_files_rmdir(php_stream_wrapper *wrapper, const char *url, i return 0; } + char errstr[256]; #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT)); + php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif if (VCWD_RMDIR(url) < 0) { - php_error_docref1(NULL, url, E_WARNING, "%s", strerror(errno)); + php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } @@ -1519,10 +1533,11 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url #endif mode_t mode; int ret = 0; + char errstr[256]; #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT)); + php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif @@ -1541,7 +1556,8 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url if (VCWD_ACCESS(url, F_OK) != 0) { FILE *file = VCWD_FOPEN(url, "w"); if (file == NULL) { - php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, strerror(errno)); + php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, + php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } fclose(file); @@ -1584,7 +1600,8 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url return 0; } if (ret == -1) { - php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", strerror(errno)); + php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } php_clear_stat_cache(0, NULL, 0); diff --git a/main/streams/streams.c b/main/streams/streams.c index 6dc073cd0baa3..46fd85e05e35d 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -203,7 +203,8 @@ static void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const free_msg = 1; } else { if (wrapper == &php_plain_files_wrapper) { - msg = strerror(errno); /* TODO: not ts on linux */ + char errstr[256]; + msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); } else { msg = "operation failed"; } diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index b1d89bc44cb2b..ef07f5f486606 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -674,9 +674,10 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t * if (sock->socket == SOCK_ERR) { if (xparam->want_errortext) { + char errstr[256]; xparam->outputs.error_text = strpprintf(0, "Failed to create unix%s socket %s", stream->ops == &php_stream_unix_socket_ops ? "" : "datagram", - strerror(errno)); + php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return -1; } From 9fc14a90c6d16f447d50612e0626fbcefcc89343 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:24:58 +0200 Subject: [PATCH 53/83] Fix GH-16319: protect fiber backtrace with null filename from crashing (#19973) --- ext/zend_test/observer.c | 4 +- .../tests/observer_fiber_backtrace_crash.phpt | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 ext/zend_test/tests/observer_fiber_backtrace_crash.phpt diff --git a/ext/zend_test/observer.c b/ext/zend_test/observer.c index 9d0fb27c8a559..bff1d6ff93424 100644 --- a/ext/zend_test/observer.c +++ b/ext/zend_test/observer.c @@ -155,7 +155,7 @@ static void observer_show_init(zend_function *fbc) php_printf("%*s\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(fbc->common.function_name)); } } else { - php_printf("%*s\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(fbc->op_array.filename)); + php_printf("%*s\n", 2 * ZT_G(observer_nesting_depth), "", fbc->op_array.filename ? ZSTR_VAL(fbc->op_array.filename) : "[no active file]"); } } @@ -178,7 +178,7 @@ static void observer_show_init_backtrace(zend_execute_data *execute_data) php_printf("%*s%s()\n", indent, "", ZSTR_VAL(fbc->common.function_name)); } } else { - php_printf("%*s{main} %s\n", indent, "", ZSTR_VAL(fbc->op_array.filename)); + php_printf("%*s{main} %s\n", indent, "", fbc->op_array.filename ? ZSTR_VAL(fbc->op_array.filename) : "[no active file]"); } } while ((ex = ex->prev_execute_data) != NULL); php_printf("%*s-->\n", 2 * ZT_G(observer_nesting_depth), ""); diff --git a/ext/zend_test/tests/observer_fiber_backtrace_crash.phpt b/ext/zend_test/tests/observer_fiber_backtrace_crash.phpt new file mode 100644 index 0000000000000..f19005179ac26 --- /dev/null +++ b/ext/zend_test/tests/observer_fiber_backtrace_crash.phpt @@ -0,0 +1,48 @@ +--TEST-- +GH-16319 (Fiber backtrace with null filename should not crash) +--EXTENSIONS-- +zend_test +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.show_init_backtrace=1 +zend_test.observer.show_output=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_opcode=0 +opcache.jit=0 +--FILE-- +start(); +echo "Test completed without crash\n"; +?> +--EXPECTF-- + + + + + + + + + + + + + <{closure}> + + +Test completed without crash + From 0ffa337a5405906b680677aadc5b63eb5cde4606 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 29 Jun 2025 01:01:45 +0200 Subject: [PATCH 54/83] Fix GH-17345: Bug #35916 was not completely fixed Change the reproducer code in `bug35916.phpt` from `stream_bucket_append` to `stream_bucket_prepend` and you have the same bug. Furthermore, even in the append case the check is incorrect because the bucket can already be in the brigade at a position other than the tail. To solve this properly, unlink the brigade first and also use that as a condition to manage the refcount. Closes GH-18973. --- NEWS | 1 + ext/standard/tests/filters/gh17345.phpt | 49 +++++++++++++++++++++++++ ext/standard/user_filters.c | 14 ++++--- main/streams/filter.c | 1 + 4 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 ext/standard/tests/filters/gh17345.phpt diff --git a/NEWS b/NEWS index e60af85379c82..952d122fb9172 100644 --- a/NEWS +++ b/NEWS @@ -55,6 +55,7 @@ PHP NEWS - Streams: . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). (Jakub Zelenka) + . Fixed bug GH-17345 (Bug #35916 was not completely fixed). (nielsdos) - XMLReader: . Fixed bug GH-20009 (XMLReader leak on RelaxNG schema failure). (nielsdos) diff --git a/ext/standard/tests/filters/gh17345.phpt b/ext/standard/tests/filters/gh17345.phpt new file mode 100644 index 0000000000000..f64659390a14f --- /dev/null +++ b/ext/standard/tests/filters/gh17345.phpt @@ -0,0 +1,49 @@ +--TEST-- +GH-17345 (Bug #35916 was not completely fixed) +--FILE-- +data = strtoupper($bucket->data); + $consumed += $bucket->datalen; + stream_bucket_prepend($out, $bucket); + // Interleave new bucket + stream_bucket_prepend($out, clone $bucket); + stream_bucket_prepend($out, $bucket); + } + return PSFS_PASS_ON; + } + + function onCreate(): bool + { + echo "fffffffffff\n"; + return true; + } + + function onClose(): void + { + echo "hello\n"; + } +} + +stream_filter_register("strtoupper", "strtoupper_filter"); +$fp=fopen($file, "w"); +stream_filter_append($fp, "strtoupper"); +fread($fp, 1024); +fwrite($fp, "Thank you\n"); +fclose($fp); +readfile($file); +unlink($file); +?> +--EXPECTF-- +fffffffffff + +Notice: fread(): Read of 8192 bytes failed with errno=9 Bad file descriptor in %s on line %d +hello +THANK YOU diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index acef5146fa25e..962eaaba7d6b0 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -404,17 +404,19 @@ static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS) memcpy(bucket->buf, Z_STRVAL_P(pzdata), bucket->buflen); } + /* If the bucket is already on a brigade we have to unlink it first to keep the + * linked list consistent. Furthermore, we can transfer the refcount in that case. */ + if (bucket->brigade) { + php_stream_bucket_unlink(bucket); + } else { + bucket->refcount++; + } + if (append) { php_stream_bucket_append(brigade, bucket); } else { php_stream_bucket_prepend(brigade, bucket); } - /* This is a hack necessary to accommodate situations where bucket is appended to the stream - * multiple times. See bug35916.phpt for reference. - */ - if (bucket->refcount == 1) { - bucket->refcount++; - } } /* }}} */ diff --git a/main/streams/filter.c b/main/streams/filter.c index a1d63c15f0a52..1ce03d41513cd 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -173,6 +173,7 @@ PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_st PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket) { + /* TODO: this was added as a bad workaround for bug #35916 and should be removed in the future. */ if (brigade->tail == bucket) { return; } From 71f8c399623c146fc991e5c67cd8fd897259abef Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 1 Oct 2025 12:58:50 +0200 Subject: [PATCH 55/83] Fix test --- ext/zend_test/tests/observer_fiber_backtrace_crash.phpt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/zend_test/tests/observer_fiber_backtrace_crash.phpt b/ext/zend_test/tests/observer_fiber_backtrace_crash.phpt index f19005179ac26..3d3629af45bdb 100644 --- a/ext/zend_test/tests/observer_fiber_backtrace_crash.phpt +++ b/ext/zend_test/tests/observer_fiber_backtrace_crash.phpt @@ -34,15 +34,15 @@ echo "Test completed without crash\n"; {main} %s --> - + - <{closure}> - + <{closure:%s:%d}> + Test completed without crash From b1d487a2763b2314f921926f4223d406477a43f4 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:01:31 +0200 Subject: [PATCH 56/83] Fix GH-20022: docker-php-ext-install DOM failed Closes GH-20023. --- NEWS | 1 + ext/dom/lexbor/lexbor/selectors-adapted/selectors.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 30910ba6b9693..0cf43531c13b1 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,7 @@ PHP NEWS - DOM: . Fix macro name clash on macOS. (Ruoyu Zhong) + . Fixed bug GH-20022 (docker-php-ext-install DOM failed). (nielsdos) - GD: . Fixed GH-19955 (imagefttext() memory leak). (David Carlier) diff --git a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c index 2bb41e43bec5d..4e094f632ef79 100644 --- a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c +++ b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c @@ -13,7 +13,7 @@ #include #include -#include "ext/dom/lexbor/lexbor/selectors-adapted/selectors.h" +#include "lexbor/selectors-adapted/selectors.h" #include "../../../namespace_compat.h" #include "../../../domexception.h" #include "../../../php_dom.h" From 26ca363a1357e63d6aa70dffab0e699fffbbef83 Mon Sep 17 00:00:00 2001 From: Guillaume Outters Date: Wed, 1 Oct 2025 16:18:20 +0200 Subject: [PATCH 57/83] Fix broken build on *BSD with MSAN The #if to declare instrumented versions of strlcpy and strlcat was too inclusive on *BSD systems where Clang already provides instrumented strong symbols, resulting in "duplicate symbol" at link-time. Fix GH-20002 Closes GH-20032 --- NEWS | 1 + Zend/zend_string.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 952d122fb9172..36da15cf32b7e 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ PHP NEWS array). (ilutov) . Fixed bug GH-19480 (error_log php.ini cannot be unset when open_basedir is configured). (nielsdos) + . Fixed bug GH-20002 (Broken build on *BSD with MSAN). (outtersg) - Curl: . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead diff --git a/Zend/zend_string.c b/Zend/zend_string.c index dfe059359aa53..e9ebee08f4393 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -505,8 +505,10 @@ ZEND_API zend_string *zend_string_concat3( return res; } -/* strlcpy and strlcat are not intercepted by msan, so we need to do it ourselves. */ -#if __has_feature(memory_sanitizer) +/* strlcpy and strlcat are not always intercepted by msan, so we need to do it + * ourselves. Apply a simple heuristic to determine the platforms that need it. + * See https://github.com/php/php-src/issues/20002. */ +#if __has_feature(memory_sanitizer) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) static size_t (*libc_strlcpy)(char *__restrict, const char *__restrict, size_t); size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t n) { From 175afc408543c06f8933cc1b1b7f1d5f327a22de Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 4 Sep 2025 18:38:38 +0100 Subject: [PATCH 58/83] Fix GH-19705: do not flush/write buffer on non writeable stream. Co-authored-by: Jakub Zelenka close GH-19708 --- NEWS | 2 ++ ext/standard/tests/streams/gh19705.phpt | 11 +++++++++++ main/streams/streams.c | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/streams/gh19705.phpt diff --git a/NEWS b/NEWS index 36da15cf32b7e..0e2726e14b5d9 100644 --- a/NEWS +++ b/NEWS @@ -57,6 +57,8 @@ PHP NEWS . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). (Jakub Zelenka) . Fixed bug GH-17345 (Bug #35916 was not completely fixed). (nielsdos) + . Fixed bug GH-19705 (segmentation when attempting to flush on non seekable + stream. (bukka/David Carlier) - XMLReader: . Fixed bug GH-20009 (XMLReader leak on RelaxNG schema failure). (nielsdos) diff --git a/ext/standard/tests/streams/gh19705.phpt b/ext/standard/tests/streams/gh19705.phpt new file mode 100644 index 0000000000000..d34cfc700596d --- /dev/null +++ b/ext/standard/tests/streams/gh19705.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-19705 segmentation fault with non writable stream at stream_filter_append call. +--EXTENSIONS-- +zlib +--FILE-- + +--EXPECTF-- +resource(%d) of type (stream filter) diff --git a/main/streams/streams.c b/main/streams/streams.c index 46fd85e05e35d..11eed3e80c61e 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1295,7 +1295,7 @@ PHPAPI int _php_stream_flush(php_stream *stream, int closing) { int ret = 0; - if (stream->writefilters.head) { + if (stream->writefilters.head && stream->ops->write) { _php_stream_write_filtered(stream, NULL, 0, closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC ); } From a3c14d6ab403246ebe310f763bd7d8c7a7fe45cb Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 2 Oct 2025 13:42:11 +0200 Subject: [PATCH 59/83] Do not use errno_t as it is not defined on musl (#20037) --- main/network.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/network.c b/main/network.c index 7102dad20798f..fecec0545e8f0 100644 --- a/main/network.c +++ b/main/network.c @@ -1090,7 +1090,7 @@ PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize) char *errstr = strerror_r(err, ebuf, sizeof(ebuf)); buf = estrdup(errstr); # else - errno_t res = strerror_r(err, ebuf, sizeof(ebuf)); + int res = (int) strerror_r(err, ebuf, sizeof(ebuf)); if (res == 0) { buf = estrdup(ebuf); } else { @@ -1101,7 +1101,7 @@ PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize) # ifdef STRERROR_R_CHAR_P buf = strerror_r(err, buf, bufsize); # else - errno_t res = strerror_r(err, buf, bufsize); + int res = (int) strerror_r(err, buf, bufsize); if (res != 0) { strncpy(buf, "Unknown error", bufsize); buf[bufsize?(bufsize-1):0] = 0; @@ -1146,7 +1146,7 @@ PHPAPI zend_string *php_socket_error_str(long err) char *errstr = strerror_r(err, ebuf, sizeof(ebuf)); # else const char *errstr; - errno_t res = strerror_r(err, ebuf, sizeof(ebuf)); + int res = (int) strerror_r(err, ebuf, sizeof(ebuf)); if (res == 0) { errstr = ebuf; } else { From 288d698ce4cc9d73300e757382c250a1ea1876eb Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 2 Oct 2025 19:20:49 +0200 Subject: [PATCH 60/83] Prevent use after stack scope in stream strerr code (#20040) --- main/streams/streams.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/streams/streams.c b/main/streams/streams.c index 11eed3e80c61e..372ed6635c3f0 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -154,6 +154,7 @@ static void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const { char *tmp; char *msg; + char errstr[256]; int free_msg = 0; if (EG(exception)) { @@ -203,7 +204,6 @@ static void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const free_msg = 1; } else { if (wrapper == &php_plain_files_wrapper) { - char errstr[256]; msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); } else { msg = "operation failed"; From eab2c2007bbb7f73e82eab6db3af40990723893a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:22:38 +0200 Subject: [PATCH 61/83] Fix GH-20011: Array of SoapVar of unknown type causes crash We "guess" the type in this case, consistent with what a SoapVar would do outside of an array. Closes GH-20030. --- NEWS | 2 ++ ext/soap/php_encoding.c | 5 +++++ ext/soap/tests/bugs/gh20011.phpt | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 ext/soap/tests/bugs/gh20011.phpt diff --git a/NEWS b/NEWS index 0e2726e14b5d9..2789d02634c91 100644 --- a/NEWS +++ b/NEWS @@ -45,6 +45,8 @@ PHP NEWS - Soap: . Fixed bug GH-19784 (SoapServer memory leak). (nielsdos) + . Fixed bug GH-20011 (Array of SoapVar of unknown type causes crash). + (nielsdos) - Standard: . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 0b4b553d87540..2e03eac0f1b29 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -3584,6 +3584,11 @@ static encodePtr get_array_type(xmlNodePtr node, zval *array, smart_str *type) soap_error0(E_ERROR, "Encoding: SoapVar has no 'enc_type' property"); } cur_type = Z_LVAL_P(ztype); + if (cur_type == UNKNOWN_TYPE) { + /* Mimic guess_xml_convert() where we use the type of the data. + * UNDEFs are handled transparently as it will error out upon encoding the data. */ + cur_type = Z_TYPE_P(Z_VAR_ENC_VALUE_P(tmp)); + } zval *zstype = Z_VAR_ENC_STYPE_P(tmp); if (Z_TYPE_P(zstype) == IS_STRING) { diff --git a/ext/soap/tests/bugs/gh20011.phpt b/ext/soap/tests/bugs/gh20011.phpt new file mode 100644 index 0000000000000..cf40d813e6178 --- /dev/null +++ b/ext/soap/tests/bugs/gh20011.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-20011 (Array of SoapVar of unknown type causes crash) +--EXTENSIONS-- +soap +--FILE-- + 'test://', 'uri' => 'http://soapinterop.org/']); +$client->echoStringArray($array); +?> +--EXPECT-- + +test string From 831597748ba812d0c0d2bc7909be7ee0804b3ce5 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 30 Sep 2025 16:35:52 +0200 Subject: [PATCH 62/83] Automatically skip tty tests if not on tty See GH-19975 Closes GH-20013 --- run-tests.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/run-tests.php b/run-tests.php index d51ab99a205a3..8fb638ed3e805 100755 --- a/run-tests.php +++ b/run-tests.php @@ -651,6 +651,12 @@ function main(): void } } + if (!defined('STDIN') || !stream_isatty(STDIN) + || !defined('STDOUT') || !stream_isatty(STDOUT) + || !defined('STDERR') || !stream_isatty(STDERR)) { + $environment['SKIP_IO_CAPTURE_TESTS'] = '1'; + } + if ($selected_tests && count($test_files) === 0) { echo "No tests found.\n"; return; From 0e1addf8b51f6d429b00cb096d9d26e76a9c932b Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 3 Oct 2025 23:11:47 +0200 Subject: [PATCH 63/83] Skip mysqli_fetch_all_data_types_variation.phpt on PPC runner --- .../tests/fetch/mysqli_fetch_all_data_types_variation.phpt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt b/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt index 594980ec0f829..81c15d53b84cb 100644 --- a/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt +++ b/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt @@ -4,6 +4,9 @@ mysqli_fetch_all() data types variation mysqli --SKIPIF-- From db65d22ce2129f409694e674384181b24e643d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Sat, 4 Oct 2025 20:00:50 +0200 Subject: [PATCH 64/83] CI: Do not save ccache for PRs for LINUX_X32 see #14168 which added this for all other builds see 69dbfadd1eebc2e99f349b6fd617cda1d5cad448 which added the LINUX_X32 build --- .github/workflows/push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 21903f63df5fe..d97736cc46a60 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -140,6 +140,7 @@ jobs: with: key: "${{github.job}}-${{hashFiles('main/php_version.h')}}" append-timestamp: false + save: ${{ github.event_name != 'pull_request' }} - name: ./configure uses: ./.github/actions/configure-x32 with: From 4fed57e7464cb4bcbf7ee2acf515156b3ab53d8f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 4 Oct 2025 14:59:27 +0200 Subject: [PATCH 65/83] Fix GH-20043: array_unique assertion failure with RC1 array causing an exception on sort The reason this happens is because the array_unique operation happens in-place because the input array is RC1. At one point during comparison an exception is thrown which will capture the arguments in the backtrace, which will increment the refcount of the RC1 array to 2. Then a modification happens after the throw on the RC2 array causing the assertion failure. We shouldn't try continue work after an exception happened during the sort. Closes GH-20059. --- NEWS | 2 ++ ext/standard/array.c | 7 +++++++ ext/standard/tests/array/gh20043.phpt | 12 ++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 ext/standard/tests/array/gh20043.phpt diff --git a/NEWS b/NEWS index 2789d02634c91..c25c1922f8b05 100644 --- a/NEWS +++ b/NEWS @@ -54,6 +54,8 @@ PHP NEWS . Fixed bug GH-19701 (Serialize/deserialize loses some data). (nielsdos) . Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()). (alexandre-daubois) + . Fixed bug GH-20043 (array_unique assertion failure with RC1 array + causing an exception on sort). (nielsdos) - Streams: . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). diff --git a/ext/standard/array.c b/ext/standard/array.c index f0a81b5ba8953..b7b5f82d61c53 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -4900,6 +4900,11 @@ PHP_FUNCTION(array_unique) ZVAL_UNDEF(&arTmp[i].b.val); zend_sort((void *) arTmp, i, sizeof(struct bucketindex), (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap); + + if (UNEXPECTED(EG(exception))) { + goto out; + } + /* go through the sorted array and delete duplicates from the copy */ lastkept = arTmp; for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) { @@ -4919,6 +4924,8 @@ PHP_FUNCTION(array_unique) } } } + +out: pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT); if (in_place) { diff --git a/ext/standard/tests/array/gh20043.phpt b/ext/standard/tests/array/gh20043.phpt new file mode 100644 index 0000000000000..d5c7e06417f18 --- /dev/null +++ b/ext/standard/tests/array/gh20043.phpt @@ -0,0 +1,12 @@ +--TEST-- +GH-20043 (array_unique assertion failure with RC1 array causing an exception on sort) +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Object of class stdClass could not be converted to string From 66c833444cf9a52a2e95656d1116efdb8495d507 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 4 Oct 2025 12:20:49 +0200 Subject: [PATCH 66/83] phar: Fix memory leaks when creating temp file fails when applying zip signature Also fixes up the error propagation at the call site which jumped to the wrong place in the error handling code. Closes GH-20057. --- NEWS | 2 ++ ext/phar/zip.c | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index c25c1922f8b05..82b1cd77d7c32 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,8 @@ PHP NEWS - Phar: . Fix memory leak and invalid continuation after tar header writing fails. (nielsdos) + . Fix memory leaks when creating temp file fails when applying zip signature. + (nielsdos) - SimpleXML: . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 87681c69959a7..dff170dc29b48 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -1192,7 +1192,9 @@ static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pas entry.fp_type = PHAR_MOD; entry.is_modified = 1; if (entry.fp == NULL) { + efree(signature); spprintf(pass->error, 0, "phar error: unable to create temporary file for signature"); + php_stream_close(newfile); return FAILURE; } @@ -1456,11 +1458,12 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int phar_metadata_tracker_try_ensure_has_serialized_data(&phar->metadata_tracker, phar->is_persistent); if (temperr) { +temperror: if (error) { spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr); } efree(temperr); -temperror: +notemperror: php_stream_close(pass.centralfp); nocentralerror: php_stream_close(pass.filefp); @@ -1488,7 +1491,7 @@ int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int if (error) { spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname); } - goto temperror; + goto notemperror; } } From d34278a0cd2622e8a207b2328d540de4ed9ac1d4 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc <365207+arnaud-lb@users.noreply.github.com> Date: Mon, 6 Oct 2025 10:12:21 +0200 Subject: [PATCH 67/83] Add Alpine push job (#20038) --- .github/workflows/push.yml | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index e191fc1ee8b2e..265f78abe0763 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -40,6 +40,52 @@ env: CC: ccache gcc CXX: ccache g++ jobs: + ALPINE: + if: github.repository == 'php/php-src' || github.event_name == 'pull_request' + name: ALPINE_X64_ASAN_UBSAN_DEBUG_ZTS + runs-on: ubuntu-22.04 + container: + image: 'alpine:3.20.1' + steps: + - name: git checkout + uses: actions/checkout@v5 + - name: apk + uses: ./.github/actions/apk + - name: LLVM 17 (ASAN-only) + # libclang_rt.asan-x86_64.a is provided by compiler-rt, and only for clang17: + # https://pkgs.alpinelinux.org/contents?file=libclang_rt.asan-x86_64.a&path=&name=&branch=v3.20 + run: | + apk add clang17 compiler-rt + - name: System info + run: | + echo "::group::Show host CPU info" + lscpu + echo "::endgroup::" + echo "::group::Show installed package versions" + apk list + echo "::endgroup::" + - name: ./configure + uses: ./.github/actions/configure-alpine + with: + configurationParameters: >- + CFLAGS="-fsanitize=undefined,address -fno-sanitize=function -DZEND_TRACK_ARENA_ALLOC" + LDFLAGS="-fsanitize=undefined,address -fno-sanitize=function" + CC=clang-17 + CXX=clang++-17 + --enable-debug + --enable-zts + skipSlow: true # FIXME: This should likely include slow extensions + - name: make + run: make -j$(/usr/bin/nproc) >/dev/null + - name: make install + uses: ./.github/actions/install-alpine + - name: Test Tracing JIT + uses: ./.github/actions/test-alpine + with: + jitType: tracing + runTestsParameters: >- + --asan -x + -d opcache.enable_cli=1 LINUX_X64: if: github.repository == 'php/php-src' || github.event_name == 'pull_request' services: From 7e7d6d6380e992d6acd61e8e18cb96ee2d98941c Mon Sep 17 00:00:00 2001 From: Leo Gaskin Date: Sat, 6 Sep 2025 14:06:23 +0200 Subject: [PATCH 68/83] Treat accept failing with SOCK_EAGAIN as success in CLI web server This ensures that no useless "Failed to poll event" error messages are logged during normal server operation, as the SOCK_EAGAIN error simply indicates another worker is already serving the request. Closes GH-19964 --- NEWS | 4 ++++ sapi/cli/php_cli_server.c | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 82b1cd77d7c32..2edaf09ce70ea 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,10 @@ PHP NEWS configured). (nielsdos) . Fixed bug GH-20002 (Broken build on *BSD with MSAN). (outtersg) +- CLI: + . Fix useless "Failed to poll event" error logs due to EAGAIN in CLI server + with PHP_CLI_SERVER_WORKERS. (leotaku) + - Curl: . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead of the curl_copy_handle() function to clone a CurlHandle. (timwolla) diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 9eaeb4779f428..8d67fb2864c6d 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -2721,14 +2721,16 @@ static zend_result php_cli_server_do_event_for_each_fd_callback(void *_params, p struct sockaddr *sa = pemalloc(server->socklen, 1); client_sock = accept(server->server_sock, sa, &socklen); if (!ZEND_VALID_SOCKET(client_sock)) { - int err = php_socket_errno(); - if (err != SOCK_EAGAIN && php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { + pefree(sa, 1); + if (php_socket_errno() == SOCK_EAGAIN) { + return SUCCESS; + } + if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { char *errstr = php_socket_strerror(php_socket_errno(), NULL, 0); php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "Failed to accept a client (reason: %s)", errstr); efree(errstr); } - pefree(sa, 1); return FAILURE; } if (SUCCESS != php_set_sock_blocking(client_sock, 0)) { From 64c1d43b68dd812ce4d3dec0a9b469fd4c060f30 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:51:23 +0200 Subject: [PATCH 69/83] Fix GH-19926: reset internal pointer earlier while splicing array while COW violation flag is still set (#19929) --- NEWS | 2 ++ ext/standard/array.c | 8 ++++++-- ext/standard/tests/array/gh19926.phpt | 20 +++++++++++++++++++ ext/standard/tests/array/gh19926_pointer.phpt | 19 ++++++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/array/gh19926.phpt create mode 100644 ext/standard/tests/array/gh19926_pointer.phpt diff --git a/NEWS b/NEWS index 2edaf09ce70ea..c2db743c84e01 100644 --- a/NEWS +++ b/NEWS @@ -62,6 +62,8 @@ PHP NEWS (alexandre-daubois) . Fixed bug GH-20043 (array_unique assertion failure with RC1 array causing an exception on sort). (nielsdos) + . Fixed bug GH-19926 (reset internal pointer earlier while splicing array + while COW violation flag is still set). (alexandre-daubois) - Streams: . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). diff --git a/ext/standard/array.c b/ext/standard/array.c index b7b5f82d61c53..4896ac44a727d 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3376,6 +3376,12 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H HT_SET_ITERATORS_COUNT(in_hash, 0); in_hash->pDestructor = NULL; + /* Set internal pointer to 0 directly instead of calling zend_hash_internal_pointer_reset(). + * This avoids the COW violation assertion and delays advancing to the first valid position + * until after we've switched to the new array structure (out_hash). The iterator will be + * advanced when actually accessed, at which point it will find valid indexes in the new array. */ + in_hash->nInternalPointer = 0; + if (UNEXPECTED(GC_DELREF(in_hash) == 0)) { /* Array was completely deallocated during the operation */ zend_array_destroy(in_hash); @@ -3394,8 +3400,6 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H in_hash->nNextFreeElement = out_hash.nNextFreeElement; in_hash->arData = out_hash.arData; in_hash->pDestructor = out_hash.pDestructor; - - zend_hash_internal_pointer_reset(in_hash); } /* }}} */ diff --git a/ext/standard/tests/array/gh19926.phpt b/ext/standard/tests/array/gh19926.phpt new file mode 100644 index 0000000000000..b714db9eb2b32 --- /dev/null +++ b/ext/standard/tests/array/gh19926.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-19926 (Assertion failure zend_hash_internal_pointer_reset_ex) +--FILE-- + +--EXPECT-- +Exception caught, no assertion failure diff --git a/ext/standard/tests/array/gh19926_pointer.phpt b/ext/standard/tests/array/gh19926_pointer.phpt new file mode 100644 index 0000000000000..c134f3b594b3d --- /dev/null +++ b/ext/standard/tests/array/gh19926_pointer.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-19926 (internal pointer behavior after array_splice) +--FILE-- + +--EXPECT-- +Before array_splice: int(3) +After array_splice: int(999) From b7aeb0a69f94aeaa983c6dbba2c01b584fdfeabe Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 6 Sep 2025 14:20:24 +0200 Subject: [PATCH 70/83] Fix GH-19570: unable to fseek in /dev/zero and /dev/null On Linux, these two character devices are exceptions in that they can be seeked. Check their major/minor device number. Co-authored-by: divinity76 --- NEWS | 2 ++ ext/standard/tests/streams/gh19570.phpt | 22 ++++++++++++++++++++++ main/streams/plain_wrapper.c | 25 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 ext/standard/tests/streams/gh19570.phpt diff --git a/NEWS b/NEWS index c2db743c84e01..7a42307caec46 100644 --- a/NEWS +++ b/NEWS @@ -64,6 +64,8 @@ PHP NEWS causing an exception on sort). (nielsdos) . Fixed bug GH-19926 (reset internal pointer earlier while splicing array while COW violation flag is still set). (alexandre-daubois) + . Fixed bug GH-19570 (unable to fseek in /dev/zero and /dev/null). + (nielsdos, divinity76) - Streams: . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). diff --git a/ext/standard/tests/streams/gh19570.phpt b/ext/standard/tests/streams/gh19570.phpt new file mode 100644 index 0000000000000..e3d486ea83cf9 --- /dev/null +++ b/ext/standard/tests/streams/gh19570.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-19570 (unable to fseek in /dev/zero and /dev/null) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(0) +int(0) +int(0) diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 9ac049f746847..09528e926f72c 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -43,6 +43,10 @@ # include #endif +#ifdef __linux__ +# include +#endif + #define php_stream_fopen_from_fd_int(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC) #define php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_REL_CC) #define php_stream_fopen_from_file_int(file, mode) _php_stream_fopen_from_file_int((file), (mode) STREAMS_CC) @@ -255,7 +259,28 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC) static void detect_is_seekable(php_stdio_stream_data *self) { #if defined(S_ISFIFO) && defined(S_ISCHR) if (self->fd >= 0 && do_fstat(self, 0) == 0) { +#ifdef __linux__ + if (S_ISCHR(self->sb.st_mode)) { + /* Some character devices are exceptions, check their major/minor ID + * https://www.kernel.org/doc/Documentation/admin-guide/devices.txt */ + if (major(self->sb.st_rdev) == 1) { + unsigned m = minor(self->sb.st_rdev); + self->is_seekable = + m == 1 || /* /dev/mem */ + m == 2 || /* /dev/kmem */ + m == 3 || /* /dev/null */ + m == 4 || /* /dev/port (seekable, offset = I/O port) */ + m == 5 || /* /dev/zero */ + m == 7; /* /dev/full */ + } else { + self->is_seekable = false; + } + } else { + self->is_seekable = !S_ISFIFO(self->sb.st_mode); + } +#else self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode)); +#endif self->is_pipe = S_ISFIFO(self->sb.st_mode); } #elif defined(PHP_WIN32) From 9d9b73a3f0d2fbfb60eee8e943e4135a73012f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 6 Oct 2025 20:17:53 +0200 Subject: [PATCH 71/83] .gdbinit: Fix printing of variable names in `print_cvs` (#20077) Previously only the first character of each variable was printed. --- .gdbinit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gdbinit b/.gdbinit index c4705b2f59a95..4477828265e0e 100644 --- a/.gdbinit +++ b/.gdbinit @@ -42,7 +42,7 @@ define print_cvs printf "Compiled variables count: %d\n\n", $cv_count while $cv_idx < $cv_count - printf "[%d] '%s'\n", $cv_idx, $cv[$cv_idx].val + printf "[%d] '$%s'\n", $cv_idx, $cv[$cv_idx].val@$cv[$cv_idx].len set $zvalue = ((zval *) $cv_ex_ptr) + $callFrameSize + $cv_idx printzv $zvalue set $cv_idx = $cv_idx + 1 From 08924cd8c4bf877fe1433561c43fcacf21acc0e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 6 Oct 2025 20:44:25 +0200 Subject: [PATCH 72/83] Regenerate main/debug_gdb_scripts.c Following 9d9b73a3f0d2fbfb60eee8e943e4135a73012f1a / GH-20077. --- main/debug_gdb_scripts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/debug_gdb_scripts.c b/main/debug_gdb_scripts.c index 384a1c8b46ee8..032dd09491bac 100644 --- a/main/debug_gdb_scripts.c +++ b/main/debug_gdb_scripts.c @@ -55,7 +55,7 @@ asm( ".ascii \"\\n\"\n" ".ascii \" printf \\\"Compiled variables count: %d\\\\\\\\n\\\\\\\\n\\\", $cv_count\\n\"\n" ".ascii \" while $cv_idx < $cv_count\\n\"\n" - ".ascii \" printf \\\"[%d] \\\\'%s\\\\'\\\\\\\\n\\\", $cv_idx, $cv[$cv_idx].val\\n\"\n" + ".ascii \" printf \\\"[%d] \\\\'$%s\\\\'\\\\\\\\n\\\", $cv_idx, $cv[$cv_idx].val@$cv[$cv_idx].len\\n\"\n" ".ascii \" set $zvalue = ((zval *) $cv_ex_ptr) + $callFrameSize + $cv_idx\\n\"\n" ".ascii \" printzv $zvalue\\n\"\n" ".ascii \" set $cv_idx = $cv_idx + 1\\n\"\n" From 359ad80c4a9f469956e303138c26ac46bb54a25f Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 26 Sep 2025 10:24:45 +0200 Subject: [PATCH 73/83] Fix race condition in zend_runtime_jit(), zend_jit_hot_func() zend_runtime_jit() prevents concurrent compilation with zend_shared_alloc_lock(), but this doesn't prevent blocked threads from trying to compile the function again after they acquire the lock. In the case of GH-19889, one of the function entries is compiled with zend_jit_handler(), which fails when the op handler has already been replaced by a JIT'ed handler. Fix by marking compiled functions with a new flag ZEND_FUNC_JITED, and skipping compilation of marked functions. The same fix is applied to zend_jit_hot_func(). Fixes GH-19889 Closes GH-19971 --- NEWS | 2 ++ Zend/Optimizer/zend_func_info.h | 2 +- ext/opcache/jit/zend_jit.c | 12 ++++++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 1d8270a3b8751..9eadaaf7f3d9c 100644 --- a/NEWS +++ b/NEWS @@ -44,6 +44,8 @@ PHP NEWS . Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex). (Arnaud) . Fixed bug GH-19831 (function JIT may not deref property value). (Arnaud) + . Fixed bug GH-19889 (race condition in zend_runtime_jit(), + zend_jit_hot_func()). (Arnaud) - Phar: . Fix memory leak and invalid continuation after tar header writing fails. diff --git a/Zend/Optimizer/zend_func_info.h b/Zend/Optimizer/zend_func_info.h index b53683bdf5e70..db00d843ee10e 100644 --- a/Zend/Optimizer/zend_func_info.h +++ b/Zend/Optimizer/zend_func_info.h @@ -39,7 +39,7 @@ #define ZEND_FUNC_JIT_ON_PROF_REQUEST (1<<14) /* used by JIT */ #define ZEND_FUNC_JIT_ON_HOT_COUNTERS (1<<15) /* used by JIT */ #define ZEND_FUNC_JIT_ON_HOT_TRACE (1<<16) /* used by JIT */ - +#define ZEND_FUNC_JITED (1<<17) /* used by JIT */ typedef struct _zend_func_info zend_func_info; typedef struct _zend_call_info zend_call_info; diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index b0423dc06bd1e..19e5520b1569e 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2945,8 +2945,9 @@ static int ZEND_FASTCALL zend_runtime_jit(void) bool do_bailout = 0; zend_shared_alloc_lock(); + jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array); - if (ZEND_FUNC_INFO(op_array)) { + if (jit_extension && !(jit_extension->func_info.flags & ZEND_FUNC_JITED)) { SHM_UNPROTECT(); zend_jit_unprotect(); @@ -2958,11 +2959,12 @@ static int ZEND_FASTCALL zend_runtime_jit(void) opline++; } } - jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array); - opline->handler = jit_extension->orig_handler; + ((zend_op*)opline)->handler = jit_extension->orig_handler; /* perform real JIT for this function */ zend_real_jit_func(op_array, NULL, NULL, ZEND_JIT_ON_FIRST_EXEC); + + jit_extension->func_info.flags |= ZEND_FUNC_JITED; } zend_catch { do_bailout = true; } zend_end_try(); @@ -3024,7 +3026,7 @@ void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend zend_shared_alloc_lock(); jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array); - if (jit_extension) { + if (jit_extension && !(jit_extension->func_info.flags & ZEND_FUNC_JITED)) { SHM_UNPROTECT(); zend_jit_unprotect(); @@ -3039,6 +3041,8 @@ void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend /* perform real JIT for this function */ zend_real_jit_func(op_array, NULL, opline, ZEND_JIT_ON_HOT_COUNTERS); + + jit_extension->func_info.flags |= ZEND_FUNC_JITED; } zend_catch { do_bailout = 1; } zend_end_try(); From 66708de8417d743aba4f542ee6476b646cdb918b Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 2 Oct 2025 15:53:53 +0200 Subject: [PATCH 74/83] Upgrade Alpine in nightly job Closes GH-20044 --- .github/workflows/nightly.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a8c8c80d7d500..e0322e3b1be2e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -93,9 +93,9 @@ jobs: ALPINE: if: inputs.run_alpine name: ALPINE_X64_ASAN_UBSAN_DEBUG_ZTS - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 container: - image: 'alpine:3.20.1' + image: 'alpine:3.22' steps: - name: git checkout uses: actions/checkout@v5 @@ -103,11 +103,6 @@ jobs: ref: ${{ inputs.branch }} - name: apk uses: ./.github/actions/apk - - name: LLVM 17 (ASAN-only) - # libclang_rt.asan-x86_64.a is provided by compiler-rt, and only for clang17: - # https://pkgs.alpinelinux.org/contents?file=libclang_rt.asan-x86_64.a&path=&name=&branch=v3.20 - run: | - apk add clang17 compiler-rt - name: System info run: | echo "::group::Show host CPU info" @@ -122,8 +117,8 @@ jobs: configurationParameters: >- CFLAGS="-fsanitize=undefined,address -fno-sanitize=function -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address -fno-sanitize=function" - CC=clang-17 - CXX=clang++-17 + CC=clang-20 + CXX=clang++-20 --enable-debug --enable-zts skipSlow: true # FIXME: This should likely include slow extensions From 93104e5c5980842f5cf9e9e7e267125ddc584f89 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 7 Oct 2025 11:05:11 +0200 Subject: [PATCH 75/83] Upgrade Alpine in push job Closes GH-20090 --- .github/workflows/push.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 265f78abe0763..ac1ac8be7b06d 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -43,19 +43,14 @@ jobs: ALPINE: if: github.repository == 'php/php-src' || github.event_name == 'pull_request' name: ALPINE_X64_ASAN_UBSAN_DEBUG_ZTS - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 container: - image: 'alpine:3.20.1' + image: 'alpine:3.22' steps: - name: git checkout uses: actions/checkout@v5 - name: apk uses: ./.github/actions/apk - - name: LLVM 17 (ASAN-only) - # libclang_rt.asan-x86_64.a is provided by compiler-rt, and only for clang17: - # https://pkgs.alpinelinux.org/contents?file=libclang_rt.asan-x86_64.a&path=&name=&branch=v3.20 - run: | - apk add clang17 compiler-rt - name: System info run: | echo "::group::Show host CPU info" @@ -70,8 +65,8 @@ jobs: configurationParameters: >- CFLAGS="-fsanitize=undefined,address -fno-sanitize=function -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address -fno-sanitize=function" - CC=clang-17 - CXX=clang++-17 + CC=clang-20 + CXX=clang++-20 --enable-debug --enable-zts skipSlow: true # FIXME: This should likely include slow extensions From 1c8363d2bfe214c142b3e17bf7303b6c338ac41a Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:15:01 +0200 Subject: [PATCH 76/83] Fix curl build failure on macOS+curl 8.16 --- ext/curl/interface.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 807b27cb78c90..4df24cff1b5bc 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1453,11 +1453,11 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string) /* }}} */ /* {{{ curl_progress */ -static size_t curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) +static int curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { php_curl *ch = (php_curl *)clientp; php_curl_callback *t = ch->handlers.progress; - size_t rval = 0; + int rval = 0; #if PHP_CURL_DEBUG fprintf(stderr, "curl_progress() called\n"); @@ -1818,8 +1818,8 @@ static void _php_curl_set_default_options(php_curl *ch) { char *cainfo; - curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); + curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L); curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write); curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); @@ -1828,10 +1828,10 @@ static void _php_curl_set_default_options(php_curl *ch) curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_header); curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); #ifndef ZTS - curl_easy_setopt(ch->cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 1); + curl_easy_setopt(ch->cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 1L); #endif - curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120); - curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20); /* prevent infinite redirects */ + curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120L); + curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20L); /* prevent infinite redirects */ cainfo = INI_STR("openssl.cafile"); if (!(cainfo && cainfo[0] != '\0')) { @@ -2807,7 +2807,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i /* no need to build the mime structure for empty hashtables; also works around https://github.com/curl/curl/issues/6455 */ curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, ""); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0L); } else { return build_mime_structure_from_hash(ch, zvalue); } @@ -2871,7 +2871,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i case CURLOPT_POSTREDIR: lval = zval_get_long(zvalue); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, lval & CURL_REDIR_POST_ALL); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, (long) (lval & CURL_REDIR_POST_ALL)); break; /* the following options deal with files, therefore the open_basedir check @@ -2906,11 +2906,11 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i if (zend_is_true(zvalue)) { curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug); curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *)ch); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1L); } else { curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, NULL); curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, NULL); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L); } break; From da75e41a6f7e1d64904e45612eaafe4a636fe502 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 22 Sep 2025 10:55:59 +0200 Subject: [PATCH 77/83] Fix curl 8.16.0 compilation with zts --- ext/curl/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 4df24cff1b5bc..f8f0d1cec3585 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1842,7 +1842,7 @@ static void _php_curl_set_default_options(php_curl *ch) } #ifdef ZTS - curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1L); #endif } /* }}} */ From b810a235870f7fbb377fa50470970eaf16d796ce Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 23 Sep 2025 14:14:09 +0200 Subject: [PATCH 78/83] Fix more curl 8.16 issues The CURLOPT_FOLLOWLOCATION seems like a gcc bug, where the integer extension of bool to long is lost, but I was unable to reproduce on godbolt.org. --- ext/curl/interface.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index f8f0d1cec3585..c56a1b01f2fa1 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2289,7 +2289,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i lval = zval_get_long(zvalue); if (lval == 1) { php_error_docref(NULL, E_NOTICE, "CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead"); - error = curl_easy_setopt(ch->cp, option, 2); + error = curl_easy_setopt(ch->cp, option, 2L); break; } ZEND_FALLTHROUGH; @@ -2789,7 +2789,7 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i case CURLOPT_FOLLOWLOCATION: lval = zend_is_true(zvalue); - error = curl_easy_setopt(ch->cp, option, lval); + error = curl_easy_setopt(ch->cp, option, (long) lval); break; case CURLOPT_HEADERFUNCTION: From 36859ad97753f6d2e550d57823111d91f0ad0ce9 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 7 Oct 2025 12:33:26 +0200 Subject: [PATCH 79/83] Fix curl_setopt_ssl test for curl 8.16 --- ext/curl/tests/curl_setopt_ssl.phpt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ext/curl/tests/curl_setopt_ssl.phpt b/ext/curl/tests/curl_setopt_ssl.phpt index 11d8fff702a88..ff08528321a0f 100644 --- a/ext/curl/tests/curl_setopt_ssl.phpt +++ b/ext/curl/tests/curl_setopt_ssl.phpt @@ -18,9 +18,13 @@ if ($curl_version['version_number'] < 0x074700) { --FILE-- Date: Tue, 7 Oct 2025 14:25:08 +0200 Subject: [PATCH 80/83] Update NEWS with info about curl 8.16 compat fixes --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index a05b577d101b7..998b5d97d6351 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.1.34 +- Curl: + . Fix curl build and test failures with version 8.16. + (nielsdos, ilutov, Jakub Zelenka) + - Opcache: . Reset global pointers to prevent use-after-free in zend_jit_status(). (Florian Engelhardt) From 78a24ffc032804755e31bb308c0e754cbc049051 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 7 Oct 2025 14:39:53 +0200 Subject: [PATCH 81/83] Update NEWS entry for curl 8.16 update --- NEWS | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index cd793b71c9cba..884ccd0855d71 100644 --- a/NEWS +++ b/NEWS @@ -23,7 +23,8 @@ PHP NEWS - Curl: . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead of the curl_copy_handle() function to clone a CurlHandle. (timwolla) - . Fix curl build failure on macOS+curl 8.16. (nielsdos) + . Fix curl build and test failures with version 8.16. + (nielsdos, ilutov, Jakub Zelenka) - Date: . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. @@ -208,10 +209,6 @@ PHP NEWS . Fixed bug GH-19397 (mb_list_encodings() can cause crashes on shutdown). (nielsdos) -- Curl: - . Fix curl build and test failures with version 8.16. - (nielsdos, ilutov, Jakub Zelenka) - - Opcache: . Reset global pointers to prevent use-after-free in zend_jit_status(). (Florian Engelhardt) From 91eb2a558db5d70a49ea62cee761eda8d663125e Mon Sep 17 00:00:00 2001 From: Eric Mann Date: Tue, 7 Oct 2025 06:58:36 -0700 Subject: [PATCH 82/83] PHP-8.3 is now for PHP 8.3.28-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 884ccd0855d71..026aac13e69ff 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.27 +?? ??? ????, PHP 8.3.28 + + +23 Oct 2025, PHP 8.3.27 - Core: . Fixed bug GH-19765 (object_properties_load() bypasses readonly property diff --git a/Zend/zend.h b/Zend/zend.h index f0925298413ff..3eade95aa6ac7 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.27-dev" +#define ZEND_VERSION "4.3.28-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 1e39e16644b1f..2ceb4d8df3389 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.3.27-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.28-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index bf1b95c631936..00dbab93a7470 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 3 -#define PHP_RELEASE_VERSION 27 +#define PHP_RELEASE_VERSION 28 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.27-dev" -#define PHP_VERSION_ID 80327 +#define PHP_VERSION "8.3.28-dev" +#define PHP_VERSION_ID 80328 From a11f7398d8dc518e9c607333569082ed093e676b Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 7 Oct 2025 23:27:38 +0900 Subject: [PATCH 83/83] Update versions for PHP 8.4.14RC1 --- NEWS | 2 +- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index b5349e815d9ad..dcea861d94142 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,6 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.4.14 +09 Oct 2025, PHP 8.4.14 - Core: . Fixed bug GH-19765 (object_properties_load() bypasses readonly property diff --git a/Zend/zend.h b/Zend/zend.h index 1c6d37b7f5044..f747eb1b8d9a0 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.14-dev" +#define ZEND_VERSION "4.4.14RC1" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 30bbdf22ff3c6..5d78afefd4610 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.4.14-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.14RC1],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 5c0e7705b9e10..1154ef4f1c516 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -3,6 +3,6 @@ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 4 #define PHP_RELEASE_VERSION 14 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.14-dev" +#define PHP_EXTRA_VERSION "RC1" +#define PHP_VERSION "8.4.14RC1" #define PHP_VERSION_ID 80414