From 4f3c28aaac7ab4e5e66e7ef9018600d648ef29d2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Mon, 22 Dec 2025 02:51:31 -0800 Subject: [PATCH 1/5] phar: Simplify phar_open_archive_fp() (#20753) By returning the stream directly, we avoid calling some helpers functions and it becomes more clear on what stream the code actually acts upon. --- ext/phar/phar.c | 11 ++++++----- ext/phar/phar_internal.h | 2 +- ext/phar/stream.c | 4 ++-- ext/phar/util.c | 30 +++++++++++++++--------------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/ext/phar/phar.c b/ext/phar/phar.c index a4464444a02b5..b57f5596b37e3 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -2310,15 +2310,16 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_postprocess_file(phar_entry_data *idata, /* verify local file header */ phar_zip_file_header local; phar_zip_data_desc desc; + php_stream *stream = phar_open_archive_fp(idata->phar); - if (SUCCESS != phar_open_archive_fp(idata->phar)) { + if (!stream) { spprintf(error, 0, "phar error: unable to open zip-based phar archive \"%s\" to verify local file header for file \"%s\"", idata->phar->fname, ZSTR_VAL(entry->filename)); return FAILURE; } - php_stream_seek(phar_get_entrypfp(idata->internal_file), entry->header_offset, SEEK_SET); + php_stream_seek(stream, entry->header_offset, SEEK_SET); - if (sizeof(local) != php_stream_read(phar_get_entrypfp(idata->internal_file), (char *) &local, sizeof(local))) { + if (sizeof(local) != php_stream_read(stream, (char *) &local, sizeof(local))) { spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local file header for file \"%s\")", idata->phar->fname, ZSTR_VAL(entry->filename)); return FAILURE; @@ -2326,12 +2327,12 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_postprocess_file(phar_entry_data *idata, /* check for data descriptor */ if (((PHAR_ZIP_16(local.flags)) & 0x8) == 0x8) { - php_stream_seek(phar_get_entrypfp(idata->internal_file), + php_stream_seek(stream, entry->header_offset + sizeof(local) + PHAR_ZIP_16(local.filename_len) + PHAR_ZIP_16(local.extra_len) + entry->compressed_filesize, SEEK_SET); - if (sizeof(desc) != php_stream_read(phar_get_entrypfp(idata->internal_file), + if (sizeof(desc) != php_stream_read(stream, (char *) &desc, sizeof(desc))) { spprintf(error, 0, "phar error: internal corruption of zip-based phar \"%s\" (cannot read local data descriptor for file \"%s\")", idata->phar->fname, ZSTR_VAL(entry->filename)); diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index abdbb9a9816f5..5ed213fb03e16 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -440,7 +440,7 @@ php_stream *phar_get_efp(phar_entry_info *entry, bool follow_links); ZEND_ATTRIBUTE_NONNULL zend_result phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **error); ZEND_ATTRIBUTE_NONNULL zend_result phar_open_entry_fp(phar_entry_info *entry, char **error, bool follow_links); phar_entry_info *phar_get_link_source(phar_entry_info *entry); -zend_result phar_open_archive_fp(phar_archive_data *phar); +php_stream *phar_open_archive_fp(phar_archive_data *phar); zend_result phar_copy_on_write(phar_archive_data **pphar); /* tar functions in tar.c */ diff --git a/ext/phar/stream.c b/ext/phar/stream.c index 08da1847cd9ce..4bd1e85666b85 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -254,13 +254,13 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha } else { php_stream *stream = phar_get_pharfp(phar); if (stream == NULL) { - if (UNEXPECTED(FAILURE == phar_open_archive_fp(phar))) { + stream = phar_open_archive_fp(phar); + if (UNEXPECTED(!stream)) { php_stream_wrapper_log_error(wrapper, options, "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; } - stream = phar_get_pharfp(phar); } phar_entry_info *entry; diff --git a/ext/phar/util.c b/ext/phar/util.c index 5e5495d8d96af..fdadc4d9b6bf7 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -107,11 +107,12 @@ php_stream *phar_get_efp(phar_entry_info *entry, bool follow_links) /* {{{ */ } if (phar_get_fp_type(entry) == PHAR_FP) { - if (!phar_get_entrypfp(entry)) { + php_stream *stream = phar_get_entrypfp(entry); + if (!stream) { /* re-open just in time for cases where our refcount reached 0 on the phar archive */ - phar_open_archive_fp(entry->phar); + stream = phar_open_archive_fp(entry->phar); } - return phar_get_entrypfp(entry); + return stream; } else if (phar_get_fp_type(entry) == PHAR_UFP) { return phar_get_entrypufp(entry); } else if (entry->fp_type == PHAR_MOD) { @@ -708,24 +709,23 @@ static inline void phar_set_pharfp(phar_archive_data *phar, php_stream *fp) PHAR_G(cached_fp)[phar->phar_pos].fp = fp; } -/* initialize a phar_archive_data's read-only fp for existing phar data */ -zend_result phar_open_archive_fp(phar_archive_data *phar) /* {{{ */ +/* Initialize a phar_archive_data's read-only fp for existing phar data. + * The stream is owned by the `phar` object and must not be closed manually. */ +php_stream *phar_open_archive_fp(phar_archive_data *phar) /* {{{ */ { - if (phar_get_pharfp(phar)) { - return SUCCESS; + php_stream *stream = phar_get_pharfp(phar); + if (stream) { + return stream; } if (php_check_open_basedir(phar->fname)) { - return FAILURE; + return NULL; } - phar_set_pharfp(phar, php_stream_open_wrapper(phar->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK|0, NULL)); - - if (!phar_get_pharfp(phar)) { - return FAILURE; - } + stream = php_stream_open_wrapper(phar->fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, NULL); + phar_set_pharfp(phar, stream); - return SUCCESS; + return stream; } /* }}} */ @@ -829,7 +829,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_open_entry_fp(phar_entry_info *entry, ch } if (!phar_get_pharfp(phar)) { - if (FAILURE == phar_open_archive_fp(phar)) { + if (!phar_open_archive_fp(phar)) { spprintf(error, 4096, "phar error: Cannot open phar archive \"%s\" for reading", phar->fname); return FAILURE; } From 9a7c09c9639afd333d9c01894972988fb9d58d70 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:37:02 +0100 Subject: [PATCH 2/5] intl: Fix leak in umsg_format_helper() Closes GH-20756. --- NEWS | 3 +++ ext/intl/msgformat/msgformat_helpers.cpp | 1 + ext/intl/tests/msgfmt_format_error4.phpt | 9 +++++++++ 3 files changed, 13 insertions(+) diff --git a/NEWS b/NEWS index ecdd051c5d6c2..b52e9b1a1366d 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,9 @@ PHP NEWS - GD: . Fixed bug GH-20622 (imagestring/imagestringup overflow). (David Carlier) +- Intl: + . Fix leak in umsg_format_helper(). (ndossche) + - LDAP: . Fix memory leak in ldap_set_options(). (ndossche) diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp index fbd85b857f3bc..58e2a96bf23b9 100644 --- a/ext/intl/msgformat/msgformat_helpers.cpp +++ b/ext/intl/msgformat/msgformat_helpers.cpp @@ -457,6 +457,7 @@ U_CFUNC void umsg_format_helper(MessageFormatter_object *mfo, char *message; spprintf(&message, 0, "Invalid UTF-8 data in string argument: " "'%s'", ZSTR_VAL(str)); + zend_tmp_string_release(tmp_str); intl_errors_set(&err, err.code, message, 1); efree(message); delete text; diff --git a/ext/intl/tests/msgfmt_format_error4.phpt b/ext/intl/tests/msgfmt_format_error4.phpt index 08b52d8ba421f..ee84388f4a7db 100644 --- a/ext/intl/tests/msgfmt_format_error4.phpt +++ b/ext/intl/tests/msgfmt_format_error4.phpt @@ -14,9 +14,18 @@ $mf = new MessageFormatter('en_US', $fmt); var_dump($mf->format(array("foo" => 7, "\x80" => "bar"))); var_dump($mf->format(array("foo" => "\x80"))); + +var_dump($mf->format(array("foo" => new class { + function __toString(): string { + return str_repeat("\x80", random_int(1, 1)); + } +}))); --EXPECTF-- Warning: MessageFormatter::format(): Invalid UTF-8 data in argument key: '€' in %s on line %d bool(false) Warning: MessageFormatter::format(): Invalid UTF-8 data in string argument: '€' in %s on line %d bool(false) + +Warning: MessageFormatter::format(): Invalid UTF-8 data in string argument: '€' in %s on line %d +bool(false) From e90b48c8e5dd9f007aeb25f28d266cf588e975fa Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+ndossche@users.noreply.github.com> Date: Sun, 21 Dec 2025 15:46:47 +0100 Subject: [PATCH 3/5] Fix bug #74154: Phar extractTo creates empty files The current code causes the phar entry to remain in the fname cache. This would be fine for uncompressed phars, but is a problem for compressed phars when they try to reopen the file pointer. The reopen code will try to use the compressed file pointer as if it were an uncompressed file pointer. In that case, for the given test, the file offsets are out of bounds for the compressed file pointer because they are the uncompressed offsets. This results in empty files. In other cases, it's possible to read compressed parts of the file that don't belong to that particular file. To solve this, we simply remove the phar entry from the fname cache if the file pointer was closed but the phar is compressed. This will make sure that reopening the phar will not go through the cache and instead opens up a fresh file pointer with the right decompression settings. Closes GH-20754. --- NEWS | 1 + ext/phar/phar.c | 20 ++++++++++------ ext/phar/tests/bug74154.phpt | 40 ++++++++++++++++++++++++++++++++ ext/phar/tests/tar/bug70417.phpt | 5 ++-- 4 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 ext/phar/tests/bug74154.phpt diff --git a/NEWS b/NEWS index 23ff95cee78b3..223373fdc7442 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,7 @@ PHP NEWS (ndossche) . Fix SplFileInfo::openFile() in write mode. (ndossche) . Fix build on legacy OpenSSL 1.1.0 systems. (Giovanni Giacobbi) + . Fixed bug #74154 (Phar extractTo creates empty files). (ndossche) - SPL: . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). diff --git a/ext/phar/phar.c b/ext/phar/phar.c index fb80075a140dc..7e74de782ccbf 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -260,20 +260,26 @@ bool phar_archive_delref(phar_archive_data *phar) /* {{{ */ PHAR_G(last_phar) = NULL; PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL; + /* This is a new phar that has perhaps had an alias/metadata set, but has never been flushed. */ + bool remove_fname_cache = !zend_hash_num_elements(&phar->manifest); + if (phar->fp && (!(phar->flags & PHAR_FILE_COMPRESSION_MASK) || !phar->alias)) { /* close open file handle - allows removal or rename of the file on windows, which has greedy locking - only close if the archive was not already compressed. If it - was compressed, then the fp does not refer to the original file. - We're also closing compressed files to save resources, - but only if the archive isn't aliased. */ + only close if the archive was not already compressed. + We're also closing compressed files to save resources, but only if the archive isn't aliased. + If it was compressed, then the fp does not refer to the original compressed file: + it refers to the **uncompressed** filtered file stream. + Therefore, upon closing a compressed file we need to invalidate the phar archive such + that the code that reopens the phar will not try to use the **compressed** file as if it was uncompressed. + That would result in treating compressed file data as if it were compressed and using uncompressed file offsets + on the compressed file. */ php_stream_close(phar->fp); phar->fp = NULL; + remove_fname_cache |= phar->flags & PHAR_FILE_COMPRESSION_MASK; } - if (!zend_hash_num_elements(&phar->manifest)) { - /* this is a new phar that has perhaps had an alias/metadata set, but has never - been flushed */ + if (remove_fname_cache) { if (zend_hash_str_del(&(PHAR_G(phar_fname_map)), phar->fname, phar->fname_len) != SUCCESS) { phar_destroy_phar_data(phar); } diff --git a/ext/phar/tests/bug74154.phpt b/ext/phar/tests/bug74154.phpt new file mode 100644 index 0000000000000..4aecc76e6dc18 --- /dev/null +++ b/ext/phar/tests/bug74154.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #74154 (Phar extractTo creates empty files) +--EXTENSIONS-- +phar +--FILE-- +buildFromDirectory($dir); + +$compPhar = $phar->compress(Phar::GZ); +unset($phar); //make sure that test.tar is closed +unlink(__DIR__.'/bug74154.tar'); +unset($compPhar); //make sure that test.tar.gz is closed +$extractingPhar = new PharData(__DIR__.'/bug74154.tar.gz'); +$extractingPhar->extractTo($dir.'_out'); + +var_dump(file_get_contents($dir.'_out/1.txt')); +var_dump(file_get_contents($dir.'_out/2.txt')); + +?> +--CLEAN-- + +--EXPECT-- +string(64) "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" +string(64) "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" diff --git a/ext/phar/tests/tar/bug70417.phpt b/ext/phar/tests/tar/bug70417.phpt index 504d7e1e387b6..7bb6bbafbcfa4 100644 --- a/ext/phar/tests/tar/bug70417.phpt +++ b/ext/phar/tests/tar/bug70417.phpt @@ -10,10 +10,11 @@ $filename = __DIR__ . '/bug70417.tar'; $resBefore = count(get_resources()); $arch = new PharData($filename); $arch->addFromString('foo', 'bar'); +$arch->addFromString('foo2', 'baz'); $arch->compress(Phar::GZ); unset($arch); $resAfter = count(get_resources()); -var_dump($resBefore === $resAfter); +var_dump($resAfter - $resBefore); ?> --CLEAN-- --EXPECT-- -bool(true) +int(0) From d3ef80649e66066fc675d707d0783b167aee19e0 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sun, 21 Dec 2025 13:35:54 +0100 Subject: [PATCH 4/5] Fix zend_vm_gen.php when executed with PHP 8.5 PHP 8.5 defines constant ZEND_VM_KIND since GH-19574, but this name is also used by zend_vm_gen.php. This causes zend_vm_gen.php to generate invalid code when executed with PHP 8.5 in an older branch. Here I rename the constant in zend_vm_gen.php. --- Zend/zend_vm_gen.php | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index 64105f5659f17..362d8554d54c3 100755 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -841,7 +841,7 @@ function($matches) use ($spec, $prefix, $op1, $op2, $extra_spec, $name) { $handler = $matches[1]; $opcode = $opcodes[$opnames[$handler]]; $inline = - ZEND_VM_KIND == ZEND_VM_KIND_HYBRID && + ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID && isset($opcode["use"]) && is_hot_handler($opcode["hot"], $op1, $op2, $extra_spec) && is_hot_handler($opcodes[$opnames[$name]]["hot"], $op1, $op2, $extra_spec) ? @@ -1077,7 +1077,7 @@ function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno, } return; case ZEND_VM_KIND_CALL: - if ($opcode["hot"] && ZEND_VM_KIND == ZEND_VM_KIND_HYBRID && is_hot_handler($opcode["hot"], $op1, $op2, $extra_spec)) { + if ($opcode["hot"] && ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID && is_hot_handler($opcode["hot"], $op1, $op2, $extra_spec)) { if (isset($opcode["use"])) { out($f,"static zend_always_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL {$spec_name}_INLINE_HANDLER(ZEND_OPCODE_HANDLER_ARGS)\n"); $additional_func = true; @@ -2326,7 +2326,7 @@ function gen_vm_opcodes_header( $str .= "#define ZEND_VM_KIND_SWITCH\t" . ZEND_VM_KIND_SWITCH . "\n"; $str .= "#define ZEND_VM_KIND_GOTO\t" . ZEND_VM_KIND_GOTO . "\n"; $str .= "#define ZEND_VM_KIND_HYBRID\t" . ZEND_VM_KIND_HYBRID . "\n"; - if ($GLOBALS["vm_kind_name"][ZEND_VM_KIND] === "ZEND_VM_KIND_HYBRID") { + if ($GLOBALS["vm_kind_name"][ZEND_VM_GEN_KIND] === "ZEND_VM_KIND_HYBRID") { $str .= "/* HYBRID requires support for computed GOTO and global register variables*/\n"; $str .= "#if (defined(__GNUC__) && defined(HAVE_GCC_GLOBAL_REGS))\n"; $str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_HYBRID\n"; @@ -2334,7 +2334,7 @@ function gen_vm_opcodes_header( $str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_CALL\n"; $str .= "#endif\n"; } else { - $str .= "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_KIND] . "\n"; + $str .= "#define ZEND_VM_KIND\t\t" . $GLOBALS["vm_kind_name"][ZEND_VM_GEN_KIND] . "\n"; } $str .= "\n"; $str .= "#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) && !defined(__SANITIZE_ADDRESS__)\n"; @@ -2536,9 +2536,9 @@ function gen_vm($def, $skel) { } // Store parameters - if ((ZEND_VM_KIND == ZEND_VM_KIND_GOTO - || ZEND_VM_KIND == ZEND_VM_KIND_SWITCH - || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID && $hot)) + if ((ZEND_VM_GEN_KIND == ZEND_VM_KIND_GOTO + || ZEND_VM_GEN_KIND == ZEND_VM_KIND_SWITCH + || (ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID && $hot)) && $param) { foreach (explode(",", $param ) as $p) { $p = trim($p); @@ -2600,7 +2600,7 @@ function gen_vm($def, $skel) { die("ERROR ($def:$lineno): Opcode with name '$op' is not defined.\n"); } $opcodes[$opnames[$dsc['op']]]['alias'] = $op; - if (!ZEND_VM_SPEC && ZEND_VM_KIND == ZEND_VM_KIND_SWITCH) { + if (!ZEND_VM_SPEC && ZEND_VM_GEN_KIND == ZEND_VM_KIND_SWITCH) { $code = $opnames[$op]; $opcodes[$code]['use'] = 1; } @@ -2713,7 +2713,7 @@ function gen_vm($def, $skel) { out($f, "255\n};\n\n"); // Generate specialized executor - gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_KIND, "execute", "zend_vm_init"); + gen_executor($f, $skl, ZEND_VM_SPEC, ZEND_VM_GEN_KIND, "execute", "zend_vm_init"); out($f, "\n"); // Generate zend_vm_get_opcode_handler() function @@ -2807,7 +2807,7 @@ function gen_vm($def, $skel) { out($f, "}\n"); out($f, "#endif\n\n"); - if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID) { // Generate zend_vm_get_opcode_handler_func() function out($f, "#if ZEND_VM_KIND == ZEND_VM_KIND_HYBRID\n"); out($f,"static const void *zend_vm_get_opcode_handler_func(uint8_t opcode, const zend_op* op)\n"); @@ -2918,10 +2918,10 @@ function gen_vm($def, $skel) { out($f, "}\n\n"); // Generate zend_vm_call_opcode_handler() function - if (ZEND_VM_KIND == ZEND_VM_KIND_CALL || ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_GEN_KIND == ZEND_VM_KIND_CALL || ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID) { out($f, "ZEND_API int ZEND_FASTCALL zend_vm_call_opcode_handler(zend_execute_data* ex)\n"); out($f, "{\n"); - if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID) { out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n"); out($f, "\topcode_handler_t handler;\n"); out($f,"#endif\n"); @@ -2939,7 +2939,7 @@ function gen_vm($def, $skel) { out($f, "\n"); out($f, "\tLOAD_OPLINE();\n"); out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)\n"); - if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID) { out($f,"#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)\n"); out($f, "\thandler = (opcode_handler_t)zend_vm_get_opcode_handler_func(zend_user_opcodes[opline->opcode], opline);\n"); out($f, "\thandler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); @@ -2947,7 +2947,7 @@ function gen_vm($def, $skel) { out($f,"#else\n"); } out($f, "\t((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);\n"); - if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) { + if (ZEND_VM_GEN_KIND == ZEND_VM_KIND_HYBRID) { out($f, "\tif (EXPECTED(opline)) {\n"); out($f,"#endif\n"); } else { @@ -3009,16 +3009,16 @@ function usage() { $kind = substr($argv[$i], strlen("--with-vm-kind=")); switch ($kind) { case "CALL": - define("ZEND_VM_KIND", ZEND_VM_KIND_CALL); + define("ZEND_VM_GEN_KIND", ZEND_VM_KIND_CALL); break; case "SWITCH": - define("ZEND_VM_KIND", ZEND_VM_KIND_SWITCH); + define("ZEND_VM_GEN_KIND", ZEND_VM_KIND_SWITCH); break; case "GOTO": - define("ZEND_VM_KIND", ZEND_VM_KIND_GOTO); + define("ZEND_VM_GEN_KIND", ZEND_VM_KIND_GOTO); break; case "HYBRID": - define("ZEND_VM_KIND", ZEND_VM_KIND_HYBRID); + define("ZEND_VM_GEN_KIND", ZEND_VM_KIND_HYBRID); break; default: echo("ERROR: Invalid vm kind '$kind'\n"); @@ -3042,9 +3042,9 @@ function usage() { } // Using defaults -if (!defined("ZEND_VM_KIND")) { +if (!defined("ZEND_VM_GEN_KIND")) { // Using CALL threading by default - define("ZEND_VM_KIND", ZEND_VM_KIND_HYBRID); + define("ZEND_VM_GEN_KIND", ZEND_VM_KIND_HYBRID); } if (!defined("ZEND_VM_SPEC")) { // Using specialized executor by default From e63dae2941959c832b5488fc66b1b7375d25efcb Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 20 Dec 2025 22:15:12 +0000 Subject: [PATCH 5/5] ext/posix: (Further) fix groups array creation on macos. With macos Tahoe and clang "17.0.0" (Xcode) the ext/posix/tests/posix_getgrgid_macosx.phpt test crashes as follow: ext/posix/posix.c:681:19: runtime error: load of misaligned address 0x60800000e972 for type 'char **', which requires 8 byte alignment 0x60800000e972: note: pointer points here 70 00 2a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 seems memcpy had been translated to a load instruction ? anyhow, we force to copy a "proper" char * source. close GH-20744 --- NEWS | 4 ++++ ext/posix/posix.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index b52e9b1a1366d..44da45b565b04 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,10 @@ PHP NEWS . Fix SplFileInfo::openFile() in write mode. (ndossche) . Fix build on legacy OpenSSL 1.1.0 systems. (Giovanni Giacobbi) +- POSIX: + . Fixed crash on posix groups to php array creation on macos. + (David Carlier) + - SPL: . Fixed bug GH-20678 (resource created by GlobIterator crashes with fclose()). (David Carlier) diff --git a/ext/posix/posix.c b/ext/posix/posix.c index 2c87fbd28d981..85ea03d8cc0cb 100644 --- a/ext/posix/posix.c +++ b/ext/posix/posix.c @@ -678,7 +678,8 @@ int php_posix_group_to_array(struct group *g, zval *array_group) /* {{{ */ for (count = 0;; count++) { /* gr_mem entries may be misaligned on macos. */ char *gr_mem; - memcpy(&gr_mem, &g->gr_mem[count], sizeof(char *)); + char *entry = (char *)g->gr_mem + (count * sizeof (char *)); + memcpy(&gr_mem, entry, sizeof(char *)); if (!gr_mem) { break; }