From 18674e39ad12918ebbcd835df4e9f4c92eb7dc82 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 25 Nov 2024 21:24:38 +0100 Subject: [PATCH 1/9] Fix is_zend_ptr() huge block comparison We should compare the block memory, not the block metadata (See zend_mm_add_huge_block). This caused random test failure for ext/ffi/tests/gh14626.phpt when the malloc() performed by the FFI code lies close to the block metadata, and the size of the block is large enough. This was reported by https://github.com/php/php-src/issues/16902#issuecomment-2498310452 Closes GH-16938. --- NEWS | 1 + Zend/zend_alloc.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index d7c37b39d3a1c..cfddddad95cbc 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ PHP NEWS (nielsdos) . Fixed bug GH-16630 (UAF in lexer with encoding translation and heredocs). (nielsdos) + . Fix is_zend_ptr() huge block comparison. (nielsdos) - FPM: . Fixed GH-16432 (PHP-FPM 8.2 SIGSEGV in fpm_get_status). (Jakub Zelenka) diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index e86f2961cfac9..b4db2f0b03cb7 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2457,8 +2457,8 @@ ZEND_API bool is_zend_ptr(const void *ptr) zend_mm_huge_list *block = AG(mm_heap)->huge_list; while (block) { - if (ptr >= (void*)block - && ptr < (void*)((char*)block + block->size)) { + if (ptr >= block->ptr + && ptr < (void*)((char*)block->ptr + block->size)) { return 1; } block = block->next; From de30ba50426f1828a3385e350bf808faf50bcf51 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 25 Nov 2024 22:02:49 +0100 Subject: [PATCH 2/9] Fix GH-16879: JIT dead code skipping does not update call_level We intend to execute `MATCH_ERROR` in the VM and return to trace a hot function in BB1. We generate a tail handler and skip all remaining oplines of BB0. That means the `INIT_FCALL` in BB0 is missed and `call_level` is not increased to 1. This leads to the assertion failure. This patch fixes the issue by updating the `call_level` for the skipped oplines. Closes GH-16939. --- NEWS | 2 ++ ext/opcache/jit/zend_jit.c | 29 ++++++++++++++++++++++++++++- ext/opcache/tests/jit/gh16879.phpt | 22 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 ext/opcache/tests/jit/gh16879.phpt diff --git a/NEWS b/NEWS index 348e58cc10fe1..8ff774ee5a4ef 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,8 @@ PHP NEWS . Fixed bug GH-16851 (JIT_G(enabled) not set correctly on other threads). (dktapps) . Fixed bug GH-16902 (Set of opcache tests fail zts+aarch64). (nielsdos) + . Fixed bug GH-16879 (JIT dead code skipping does not update call_level). + (nielsdos) - Windows: . Fixed bug GH-16849 (Error dialog causes process to hang). (cmb) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 0c6ab6c5cbc8c..cc22e7375a8c1 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2576,7 +2576,34 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } /* THROW and EXIT may be used in the middle of BB */ /* don't generate code for the rest of BB */ - i = end; + + /* Skip current opline for call_level computation + * Don't include last opline because end of loop already checks call level of last opline */ + i++; + for (; i < end; i++) { + opline = op_array->opcodes + i; + switch (opline->opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_NEW: + call_level++; + break; + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_CALLABLE_CONVERT: + call_level--; + break; + } + } + opline = op_array->opcodes + i; break; /* stackless execution */ case ZEND_INCLUDE_OR_EVAL: diff --git a/ext/opcache/tests/jit/gh16879.phpt b/ext/opcache/tests/jit/gh16879.phpt new file mode 100644 index 0000000000000..7a17fd34135b2 --- /dev/null +++ b/ext/opcache/tests/jit/gh16879.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-16879 (JIT dead code skipping does not update call_level) +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=32M +opcache.jit=1235 +opcache.jit_hot_func=1 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught UnhandledMatchError: Unhandled match case 0 in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d From d31de85f5f70541a4051668b7720c44047a6573a Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 26 Nov 2024 21:44:15 +0300 Subject: [PATCH 3/9] Avoid possible spill conflict (one of the problem that caused GH-16821) (#16947) --- ext/opcache/jit/zend_jit_ir.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 0331d0e7b6e0d..6c55f404f9b6c 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -1306,6 +1306,13 @@ static bool zend_jit_spilling_may_cause_conflict(zend_jit_ctx *jit, int var, ir_ && (jit->ssa->cfg.blocks[jit->ssa->vars[jit->ssa->ops[jit->ssa->vars[var].definition].op1_use].definition_phi->block].flags & ZEND_BB_LOOP_HEADER)) { /* Avoid moving spill store out of loop */ return 1; + } else if (jit->ssa->vars[var].definition >= 0 + && jit->ssa->ops[jit->ssa->vars[var].definition].op1_def == var + && jit->ssa->ops[jit->ssa->vars[var].definition].op1_use >= 0 + && jit->ssa->ops[jit->ssa->vars[var].definition].op2_use >= 0 + && jit->ra[jit->ssa->ops[jit->ssa->vars[var].definition].op2_use].ref == val) { + /* Avoid spill conflict between of ASSIGN.op1_def and ASSIGN.op1_use */ + return 1; } return 0; } From a80f0b515a22bde29b6fffbf26ae74025822bbdf Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 10 Nov 2024 14:04:50 +0100 Subject: [PATCH 4/9] Fix various memory leaks in curl mime handling Closes GH-16745. --- NEWS | 3 +++ ext/curl/interface.c | 39 ++++++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/NEWS b/NEWS index cfddddad95cbc..c09900ebd6b8d 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,9 @@ PHP NEWS (nielsdos) . Fix is_zend_ptr() huge block comparison. (nielsdos) +- Curl: + . Fix various memory leaks in curl mime handling. (nielsdos) + - FPM: . Fixed GH-16432 (PHP-FPM 8.2 SIGSEGV in fpm_get_status). (Jakub Zelenka) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 4884ddc8228a1..6798a384c7785 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1381,7 +1381,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo postval = Z_STR_P(prop); if (php_check_open_basedir(ZSTR_VAL(postval))) { - return FAILURE; + goto out_string; } prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv); @@ -1407,15 +1407,18 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo seekfunc = NULL; } + part = curl_mime_addpart(mime); + if (part == NULL) { + if (stream) { + php_stream_close(stream); + } + goto out_string; + } + cb_arg = emalloc(sizeof *cb_arg); cb_arg->filename = zend_string_copy(postval); cb_arg->stream = stream; - part = curl_mime_addpart(mime); - if (part == NULL) { - zend_string_release_ex(string_key, 0); - return FAILURE; - } if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK || (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK || (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK @@ -1449,8 +1452,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "postname", sizeof("postname")-1, 0, &rv); if (EG(exception)) { - zend_string_release_ex(string_key, 0); - return FAILURE; + goto out_string; } ZVAL_DEREF(prop); ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); @@ -1459,8 +1461,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv); if (EG(exception)) { - zend_string_release_ex(string_key, 0); - return FAILURE; + goto out_string; } ZVAL_DEREF(prop); ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); @@ -1469,8 +1470,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "data", sizeof("data")-1, 0, &rv); if (EG(exception)) { - zend_string_release_ex(string_key, 0); - return FAILURE; + goto out_string; } ZVAL_DEREF(prop); ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); @@ -1483,8 +1483,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo part = curl_mime_addpart(mime); if (part == NULL) { - zend_string_release_ex(string_key, 0); - return FAILURE; + goto out_string; } if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK || (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK @@ -1540,7 +1539,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo SAVE_CURL_ERROR(ch, error); if (error != CURLE_OK) { - return FAILURE; + goto out_mime; } if ((*ch->clone) == 1) { @@ -1556,6 +1555,16 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo SAVE_CURL_ERROR(ch, error); return error == CURLE_OK ? SUCCESS : FAILURE; + +out_string: + zend_string_release_ex(string_key, false); +out_mime: +#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ + curl_mime_free(mime); +#else + curl_formfree(first); +#endif + return FAILURE; } /* }}} */ From ed556939dfc61dc5f20346905155f36b14a2e3a1 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:22:12 +0100 Subject: [PATCH 5/9] Extract call_level conditions out to separate functions (#16949) These are repeated a couple of times, so centralise it in 2 functions to reduce repetition and make updating this less error-prone. --- ext/opcache/jit/zend_jit.c | 77 +++++++++++++++++--------------- ext/opcache/jit/zend_jit_trace.c | 51 +++++---------------- 2 files changed, 51 insertions(+), 77 deletions(-) diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index cc22e7375a8c1..558faae22d0a0 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -727,6 +727,38 @@ ZEND_EXT_API void zend_jit_status(zval *ret) add_assoc_zval(ret, "jit", &stats); } +static bool zend_jit_inc_call_level(uint8_t opcode) +{ + switch (opcode) { + case ZEND_INIT_FCALL: + case ZEND_INIT_FCALL_BY_NAME: + case ZEND_INIT_NS_FCALL_BY_NAME: + case ZEND_INIT_METHOD_CALL: + case ZEND_INIT_DYNAMIC_CALL: + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: + case ZEND_INIT_USER_CALL: + case ZEND_NEW: + return true; + default: + return false; + } +} + +static bool zend_jit_dec_call_level(uint8_t opcode) +{ + switch (opcode) { + case ZEND_DO_FCALL: + case ZEND_DO_ICALL: + case ZEND_DO_UCALL: + case ZEND_DO_FCALL_BY_NAME: + case ZEND_CALLABLE_CONVERT: + return true; + default: + return false; + } +} + static zend_string *zend_jit_func_name(const zend_op_array *op_array) { smart_str buf = {0}; @@ -1463,17 +1495,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op for (i = ssa->cfg.blocks[b].start; i <= end; i++) { zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[i] : NULL; opline = op_array->opcodes + i; - switch (opline->opcode) { - case ZEND_INIT_FCALL: - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_DYNAMIC_CALL: - case ZEND_INIT_STATIC_METHOD_CALL: - case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: - case ZEND_INIT_USER_CALL: - case ZEND_NEW: - call_level++; + if (zend_jit_inc_call_level(opline->opcode)) { + call_level++; } if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) { @@ -2582,25 +2605,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op i++; for (; i < end; i++) { opline = op_array->opcodes + i; - switch (opline->opcode) { - case ZEND_INIT_FCALL: - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_DYNAMIC_CALL: - case ZEND_INIT_STATIC_METHOD_CALL: - case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: - case ZEND_INIT_USER_CALL: - case ZEND_NEW: - call_level++; - break; - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - case ZEND_CALLABLE_CONVERT: - call_level--; - break; + if (zend_jit_inc_call_level(opline->opcode)) { + call_level++; + } else if (zend_jit_dec_call_level(opline->opcode)) { + call_level--; } } opline = op_array->opcodes + i; @@ -2715,13 +2723,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op } } done: - switch (opline->opcode) { - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - case ZEND_CALLABLE_CONVERT: - call_level--; + if (zend_jit_dec_call_level(opline->opcode)) { + call_level--; } } zend_jit_bb_end(&ctx, b); diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index a8ea08cd1733c..c7c470330f060 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -1164,28 +1164,13 @@ static const zend_op *zend_jit_trace_find_init_fcall_op(zend_jit_trace_rec *p, c if (opline) { while (opline > op_array->opcodes) { opline--; - switch (opline->opcode) { - case ZEND_INIT_FCALL: - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_DYNAMIC_CALL: - case ZEND_INIT_STATIC_METHOD_CALL: - case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: - case ZEND_INIT_USER_CALL: - case ZEND_NEW: - if (call_level == 0) { - return opline; - } - call_level--; - break; - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - case ZEND_CALLABLE_CONVERT: - call_level++; - break; + if (zend_jit_inc_call_level(opline->opcode)) { + if (call_level == 0) { + return opline; + } + call_level--; + } else if (zend_jit_dec_call_level(opline->opcode)) { + call_level++; } } } @@ -4394,17 +4379,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par frame_flags = 0; - switch (opline->opcode) { - case ZEND_INIT_FCALL: - case ZEND_INIT_FCALL_BY_NAME: - case ZEND_INIT_NS_FCALL_BY_NAME: - case ZEND_INIT_METHOD_CALL: - case ZEND_INIT_DYNAMIC_CALL: - case ZEND_INIT_STATIC_METHOD_CALL: - case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: - case ZEND_INIT_USER_CALL: - case ZEND_NEW: - frame->call_level++; + if (zend_jit_inc_call_level(opline->opcode)) { + frame->call_level++; } if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) { @@ -6426,13 +6402,8 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par done: polymorphic_side_trace = 0; - switch (opline->opcode) { - case ZEND_DO_FCALL: - case ZEND_DO_ICALL: - case ZEND_DO_UCALL: - case ZEND_DO_FCALL_BY_NAME: - case ZEND_CALLABLE_CONVERT: - frame->call_level--; + if (zend_jit_dec_call_level(opline->opcode)) { + frame->call_level--; } if (ra) { From ae84b81bfaa40dfd241e25d5dc8fae6cf9585c68 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 15 Jul 2024 07:16:25 -0400 Subject: [PATCH 6/9] Backport GH-14962 to stable versions Alpine CI regularly fails because of the sorting order of these tests. See https://github.com/php/php-src/pull/14962#issuecomment-2498799881 Closes GH-16950. --- ext/dba/tests/dba_flatfile.phpt | 4 ++-- ext/dba/tests/dba_gdbm.phpt | 12 ++++++------ ext/dba/tests/dba_inifile.phpt | 8 ++++---- ext/dba/tests/dba_ndbm.phpt | 12 ++++++------ ext/dba/tests/dba_qdbm.phpt | 8 ++++---- ext/dba/tests/dba_tcadb.phpt | 4 ++-- ext/dba/tests/setup/setup_dba_tests.inc | 19 +++++++++++++++++-- ext/pgsql/tests/80_bug14383.phpt | 4 ++-- 8 files changed, 43 insertions(+), 28 deletions(-) diff --git a/ext/dba/tests/dba_flatfile.phpt b/ext/dba/tests/dba_flatfile.phpt index 9d989e9069b8d..1061e0a00e704 100644 --- a/ext/dba/tests/dba_flatfile.phpt +++ b/ext/dba/tests/dba_flatfile.phpt @@ -29,12 +29,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 key4: Another Content String key5: The last content string name9: Content String 9 -[key10]name10: Content String 10 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/dba_gdbm.phpt b/ext/dba/tests/dba_gdbm.phpt index 480e6063b5212..7e3d43a4f1709 100644 --- a/ext/dba/tests/dba_gdbm.phpt +++ b/ext/dba/tests/dba_gdbm.phpt @@ -35,12 +35,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) -key4: Another Content String +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 +key4: Another Content String key5: The last content string -[key10]name10: Content String 10 name9: Content String 9 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y @@ -81,12 +81,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) -key4: Another Content String +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 +key4: Another Content String key5: The last content string -[key10]name10: Content String 10 name9: Content String 9 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/dba_inifile.phpt b/ext/dba/tests/dba_inifile.phpt index 06be22c085ff6..c2d638a747581 100644 --- a/ext/dba/tests/dba_inifile.phpt +++ b/ext/dba/tests/dba_inifile.phpt @@ -30,14 +30,14 @@ bool(true) bool(true) Try to remove key 1 again bool(false) -key2: Content String 2 -key4: Another Content String -key5: The last content string -name9: Content String 9 [key10]: [key10]name10: Content String 10 [key30]: [key30]name30: Content String 30 +key2: Content String 2 +key4: Another Content String +key5: The last content string +name9: Content String 9 Total keys: 8 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/dba_ndbm.phpt b/ext/dba/tests/dba_ndbm.phpt index dcf368ff1a36f..730932966cfef 100644 --- a/ext/dba/tests/dba_ndbm.phpt +++ b/ext/dba/tests/dba_ndbm.phpt @@ -36,12 +36,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) -key4: Another Content String +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 +key4: Another Content String key5: The last content string -[key10]name10: Content String 10 name9: Content String 9 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y @@ -82,12 +82,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) -key4: Another Content String +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 +key4: Another Content String key5: The last content string -[key10]name10: Content String 10 name9: Content String 9 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/dba_qdbm.phpt b/ext/dba/tests/dba_qdbm.phpt index e4321e7dc3750..fad229c368ee4 100644 --- a/ext/dba/tests/dba_qdbm.phpt +++ b/ext/dba/tests/dba_qdbm.phpt @@ -35,12 +35,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 key4: Another Content String key5: The last content string name9: Content String 9 -[key10]name10: Content String 10 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y @@ -81,12 +81,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 key4: Another Content String key5: The last content string name9: Content String 9 -[key10]name10: Content String 10 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/dba_tcadb.phpt b/ext/dba/tests/dba_tcadb.phpt index 6459c5b372ea7..24900073c9cc8 100644 --- a/ext/dba/tests/dba_tcadb.phpt +++ b/ext/dba/tests/dba_tcadb.phpt @@ -30,12 +30,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 key4: Another Content String key5: The last content string name9: Content String 9 -[key10]name10: Content String 10 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/setup/setup_dba_tests.inc b/ext/dba/tests/setup/setup_dba_tests.inc index 3e79ed3d54ce1..2ffac29e69782 100644 --- a/ext/dba/tests/setup/setup_dba_tests.inc +++ b/ext/dba/tests/setup/setup_dba_tests.inc @@ -102,14 +102,29 @@ function run_standard_tests_ex(string $handler, string $name, LockFlag $lock, bo echo 'Try to remove key 1 again', \PHP_EOL; var_dump(dba_delete("key1", $db_writer)); - // Fetch data + // Fetch and sort data. We sort to guarantee that the output is + // consistent across invocations and architectures. When iterating + // with firstkey() and nextkey(), several engines (GDBM, LMDB, + // QDBM) make no promise about the iteration order. Others (TCADB, + // DBM) explicitly state that the order is arbitrary. With GDBM at + // least, the order appears platform-dependent -- we have a report + // in Github issue 14786. GDBM's own test suite sorts this output, + // suggesting that sorting is a reasonable workaround for the issue. + $output = []; + $key = dba_firstkey($db_writer); $total_keys = 0; while ($key) { - echo $key, ': ', dba_fetch($key, $db_writer), \PHP_EOL; + $output[] = $key . ': ' . dba_fetch($key, $db_writer) . \PHP_EOL; $key = dba_nextkey($db_writer); $total_keys++; } + + sort($output, SORT_STRING); + foreach ($output as $line) { + echo $line; + } + echo 'Total keys: ', $total_keys, \PHP_EOL; for ($i = 1; $i < 6; $i++) { echo "Key $i exists? ", dba_exists("key$i", $db_writer) ? 'Y' : 'N', \PHP_EOL; diff --git a/ext/pgsql/tests/80_bug14383.phpt b/ext/pgsql/tests/80_bug14383.phpt index f17af830d7041..c14b2f414d9bd 100644 --- a/ext/pgsql/tests/80_bug14383.phpt +++ b/ext/pgsql/tests/80_bug14383.phpt @@ -39,12 +39,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 key4: Another Content String key5: The last content string name9: Content String 9 -[key10]name10: Content String 10 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y From 97b03186c4e6964ad8683dc8b225e4dcc4de3199 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:19:03 +0100 Subject: [PATCH 7/9] Fix GH-15208: Segfault with breakpoint map and phpdbg_clear() It crashes because it's gonna try accessing the breakpoint which was cleared by user code in `phpdbg_clear();`. Not all breakpoint data was properly cleaned. Closes GH-16953. --- NEWS | 4 ++++ sapi/phpdbg/phpdbg.c | 1 + sapi/phpdbg/tests/gh15208.phpt | 15 +++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 sapi/phpdbg/tests/gh15208.phpt diff --git a/NEWS b/NEWS index c09900ebd6b8d..d4dea493bca0a 100644 --- a/NEWS +++ b/NEWS @@ -47,6 +47,10 @@ PHP NEWS . Fixed bug GH-16695 (phar:// tar parser and zero-length file header blocks). (nielsdos, Hans Krentel) +- PHPDBG: + . Fixed bug GH-15208 (Segfault with breakpoint map and phpdbg_clear()). + (nielsdos) + - SimpleXML: . Fixed bug GH-16808 (Segmentation fault in RecursiveIteratorIterator ->current() with a xml element input). (nielsdos) diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index d9cc8f5e891e0..4e685d0894a8b 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -369,6 +369,7 @@ PHP_FUNCTION(phpdbg_clear) zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); } /* }}} */ diff --git a/sapi/phpdbg/tests/gh15208.phpt b/sapi/phpdbg/tests/gh15208.phpt new file mode 100644 index 0000000000000..4fa63a61c5262 --- /dev/null +++ b/sapi/phpdbg/tests/gh15208.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-15208 (Segfault with breakpoint map and phpdbg_clear()) +--PHPDBG-- +r +q +--FILE-- + +--EXPECTF-- +[Successful compilation of %s] +prompt> [Breakpoint #0 added at foo::bar] +[Script ended normally] +prompt> From b89d7ff92ac7ce4fdfc7656d5312d59b48dedcc3 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 27 Nov 2024 00:43:45 +0300 Subject: [PATCH 8/9] Fix GH-16821: runtime error: member access within misaligned address when running phpseclib tests (#16951) --- ext/opcache/jit/zend_jit_ir.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 6c55f404f9b6c..e4b68d23520c8 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -17381,8 +17381,15 @@ static void jit_frameless_icall2(zend_jit_ctx *jit, const zend_op *opline, uint3 jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, NULL); /* Set OP1 to UNDEF in case FREE_OP2() throws. */ - if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) != 0 && (opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0) { + if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) != 0 + && (opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0 + && (op2_info & MAY_BE_RC1) + && (op2_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) { jit_set_Z_TYPE_INFO(jit, op1_addr, IS_UNDEF); + if (JIT_G(current_frame)) { + SET_STACK_TYPE(JIT_G(current_frame)->stack, + EX_VAR_TO_NUM(opline->op1.var), IS_UNKNOWN, 1); + } } jit_FREE_OP(jit, opline->op2_type, opline->op2, op2_info, NULL); zend_jit_check_exception(jit); @@ -17455,18 +17462,34 @@ static void jit_frameless_icall3(zend_jit_ctx *jit, const zend_op *opline, uint3 jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, NULL); /* Set OP1 to UNDEF in case FREE_OP2() throws. */ + bool op1_undef = false; if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) - && ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) - || (op_data_type & (IS_VAR|IS_TMP_VAR)))) { + && (((opline->op2_type & (IS_VAR|IS_TMP_VAR)) + && (op2_info & MAY_BE_RC1) + && (op2_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) + || ((op_data_type & (IS_VAR|IS_TMP_VAR)) + && (op1_data_info & MAY_BE_RC1) + && (op1_data_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))))) { + op1_undef = true; jit_set_Z_TYPE_INFO(jit, op1_addr, IS_UNDEF); + if (JIT_G(current_frame)) { + SET_STACK_TYPE(JIT_G(current_frame)->stack, + EX_VAR_TO_NUM(opline->op1.var), IS_UNKNOWN, 1); + } } jit_FREE_OP(jit, opline->op2_type, opline->op2, op2_info, NULL); - /* If OP1 is a TMP|VAR, we don't need to set OP2 to UNDEF on free because + /* If OP1 is set to UNDEF, we don't need to set OP2 to UNDEF on free because * zend_fetch_debug_backtrace aborts when it encounters the first UNDEF TMP|VAR. */ - if (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) + if (!op1_undef && (opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0 - && (op_data_type & (IS_VAR|IS_TMP_VAR)) != 0) { + && (op_data_type & (IS_VAR|IS_TMP_VAR)) != 0 + && (op1_data_info & MAY_BE_RC1) + && (op1_data_info & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY))) { jit_set_Z_TYPE_INFO(jit, op2_addr, IS_UNDEF); + if (JIT_G(current_frame)) { + SET_STACK_TYPE(JIT_G(current_frame)->stack, + EX_VAR_TO_NUM(opline->op2.var), IS_UNKNOWN, 1); + } } jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, op1_data_info, NULL); zend_jit_check_exception(jit); From 56fb910d9c11aeb613caa1830947c31d529daa1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 26 Nov 2024 22:44:25 +0100 Subject: [PATCH 9/9] Fix the 1st parameter type casing of pg_set_chunked_rows_size() --- ext/pgsql/pgsql.stub.php | 2 +- ext/pgsql/pgsql_arginfo.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/pgsql/pgsql.stub.php b/ext/pgsql/pgsql.stub.php index f507de2e062a2..ec7e2614ce0a3 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -968,7 +968,7 @@ function pg_put_copy_end(PgSql\Connection $connection, ?string $error = null): i function pg_socket_poll($socket, int $read, int $write, int $timeout = -1): int {} #ifdef HAVE_PG_SET_CHUNKED_ROWS_SIZE - function pg_set_chunked_rows_size(Pgsql\Connection $connection, int $size): bool {} + function pg_set_chunked_rows_size(PgSql\Connection $connection, int $size): bool {} #endif } diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index 182dea8d221a8..ab38b6a7a8bfa 100644 --- a/ext/pgsql/pgsql_arginfo.h +++ b/ext/pgsql/pgsql_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0b89a48c27c6682542312391f10a3ab8fb719ef8 */ + * Stub hash: 14b0bdd019480b850940b2c2b012b5f6d51746b8 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_pg_connect, 0, 1, PgSql\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, connection_string, IS_STRING, 0) @@ -490,7 +490,7 @@ ZEND_END_ARG_INFO() #if defined(HAVE_PG_SET_CHUNKED_ROWS_SIZE) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_set_chunked_rows_size, 0, 2, _IS_BOOL, 0) - ZEND_ARG_OBJ_INFO(0, connection, Pgsql\\Connection, 0) + ZEND_ARG_OBJ_INFO(0, connection, PgSql\\Connection, 0) ZEND_ARG_TYPE_INFO(0, size, IS_LONG, 0) ZEND_END_ARG_INFO() #endif