diff --git a/NEWS b/NEWS index c7645ca27b8a..858ca396189a 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,8 @@ PHP NEWS invalid variable names). (timwolla) . Fixed bug GH-22291 (AST pretty printing does not correctly handle braces in string interpolation). (timwolla) + . Fixed bug GH-22373 (AST pretty-printing drops meaningful parentheses + surrounding property access). (timwolla) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) diff --git a/UPGRADING b/UPGRADING index c77bfbfa4e02..9f7917301b59 100644 --- a/UPGRADING +++ b/UPGRADING @@ -481,6 +481,9 @@ PHP 8.6 UPGRADE NOTES . Improved performance of indentation generation in json_encode() when using PHP_JSON_PRETTY_PRINT. +- Phar: + . Reduced temporary allocations when iterating Phar directories. + - Standard: . Improved performance of array_fill_keys(). . Improved performance of array_map() with multiple arrays passed. diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index f495c4c8e3bb..d3ce419c737e 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1578,9 +1578,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio static ZEND_COLD void zend_ast_export_str(smart_str *str, const zend_string *s) { - size_t i; - - for (i = 0; i < ZSTR_LEN(s); i++) { + for (size_t i = 0; i < ZSTR_LEN(s); i++) { unsigned char c = ZSTR_VAL(s)[i]; if (c == '\'' || c == '\\') { smart_str_appendc(str, '\\'); @@ -1593,9 +1591,7 @@ static ZEND_COLD void zend_ast_export_str(smart_str *str, const zend_string *s) static ZEND_COLD void zend_ast_export_qstr(smart_str *str, char quote, const zend_string *s) { - size_t i; - - for (i = 0; i < ZSTR_LEN(s); i++) { + for (size_t i = 0; i < ZSTR_LEN(s); i++) { unsigned char c = ZSTR_VAL(s)[i]; if (c < ' ') { switch (c) { @@ -1636,12 +1632,11 @@ static ZEND_COLD void zend_ast_export_qstr(smart_str *str, char quote, const zen } } -static ZEND_COLD void zend_ast_export_quoted_str(smart_str *str, zend_string *s) +static ZEND_COLD void zend_ast_export_quoted_str(smart_str *str, const zend_string *s) { - size_t i; - - for (i = 0; i < ZSTR_LEN(s); i++) { - if ((unsigned char) ZSTR_VAL(s)[i] < ' ') { + for (size_t i = 0; i < ZSTR_LEN(s); i++) { + unsigned char c = ZSTR_VAL(s)[i]; + if (c < ' ') { smart_str_appendc(str, '"'); zend_ast_export_qstr(str, '"', s); smart_str_appendc(str, '"'); @@ -1940,11 +1935,10 @@ static ZEND_COLD void zend_ast_export_zval(smart_str *str, const zval *zv, int p } if (key) { zend_ast_export_quoted_str(str, key); - smart_str_appends(str, " => "); } else { smart_str_append_long(str, idx); - smart_str_appends(str, " => "); } + smart_str_appends(str, " => "); zend_ast_export_zval(str, val, 0, indent); } ZEND_HASH_FOREACH_END(); smart_str_appendc(str, ']'); @@ -2535,12 +2529,18 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio break; case ZEND_AST_CALL: { zend_ast *left = ast->child[0]; - if (left->kind == ZEND_AST_ARROW_FUNC || left->kind == ZEND_AST_CLOSURE) { - smart_str_appendc(str, '('); - zend_ast_export_ns_name(str, left, 0, indent); - smart_str_appendc(str, ')'); - } else { - zend_ast_export_ns_name(str, left, 0, indent); + switch (left->kind) { + /* ZEND_AST_ZVAL is a regular function call. */ + case ZEND_AST_ZVAL: + /* ZEND_AST_VAR ($foo()) is unambiguous without parens. */ + case ZEND_AST_VAR: + zend_ast_export_ns_name(str, left, 0, indent); + break; + default: + smart_str_appendc(str, '('); + zend_ast_export_ex(str, left, 0, indent); + smart_str_appendc(str, ')'); + break; } smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); diff --git a/build/gen_stub.php b/build/gen_stub.php index 34bbd34fd37f..e60e61885206 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2344,9 +2344,28 @@ public function getCExpr(): ?string // $this->expr has all its PHP constants replaced by C constants $prettyPrinter = new Standard; $expr = $prettyPrinter->prettyPrintExpr($this->expr); - // PHP single-quote to C double-quote string if ($this->type->isString()) { - $expr = preg_replace("/(^'|'$)/", '"', $expr); + // The string in $expr has had the octal, hex, and unicode + // backslash sequences already applied for double-quoted strings, + // but not the other sequences. + // + // PHP has single quote strings, C doesn't (they're one char). + // Single-quoted strings need handling to replace their escapes + // with the double-quoted equivalent; namely single quote escapes. + // + // Double-quoted strings have similar escape sequences as C does, + // so we can pass them through directly. However, C does *not* + // support the \$ escape sequence (in ""), so strip that. Variable + // interpolation shouldn't be possible in a stub, so we don't need + // to worry about mangling such a case. + if (preg_match("/(^'|'$)/", $expr)) { + $expr = substr($expr, 1, -1); // strip quotes, readd later + $expr = str_replace("\\'", "'", $expr); + $expr = addcslashes($expr, "\\\""); + $expr = "\"$expr\""; + } else { + $expr = str_replace('\$', "$", $expr); + } } return $expr[0] == '"' ? $expr : preg_replace('(\bnull\b)', 'NULL', str_replace('\\', '', $expr)); } diff --git a/ext/com_dotnet/com_com.c b/ext/com_dotnet/com_com.c index d9938a13c950..a0c8a955e0c5 100644 --- a/ext/com_dotnet/com_com.c +++ b/ext/com_dotnet/com_com.c @@ -322,7 +322,7 @@ PHP_FUNCTION(com_get_active_object) IDispatch_Release(obj); } if (unk) { - IUnknown_Release(obj); + IUnknown_Release(unk); } efree(module); } diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index 900f26804b74..2ea93f979683 100644 --- a/ext/ftp/ftp.c +++ b/ext/ftp/ftp.c @@ -1273,7 +1273,7 @@ static bool ftp_readline(ftpbuf_t *ftp) } data = eol; - if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) { + if (size < 2 || (rcvd = my_recv(ftp, ftp->fd, data, size - 1)) < 1) { *data = 0; return false; } diff --git a/ext/ftp/tests/bug80901.phpt b/ext/ftp/tests/bug80901.phpt index e2a58fa0668a..a1c0e479c6ae 100644 --- a/ext/ftp/tests/bug80901.phpt +++ b/ext/ftp/tests/bug80901.phpt @@ -16,4 +16,4 @@ ftp_systype($ftp); --EXPECTF-- bool(true) -Warning: ftp_systype(): **************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** in %s on line %d +Warning: ftp_systype(): *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** in %s on line %d diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 19f83e3140be..70a379dbe9be 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -533,7 +533,7 @@ PHP_FUNCTION(imageloadfont) */ font = (gdFontPtr) emalloc(sizeof(gdFont)); b = 0; - while (b < hdr_size && (n = php_stream_read(stream, (char*)&font[b], hdr_size - b)) > 0) { + while (b < hdr_size && (n = php_stream_read(stream, (char *) font + b, hdr_size - b)) > 0) { b += n; } diff --git a/ext/gd/tests/imageloadfont_short_read.phpt b/ext/gd/tests/imageloadfont_short_read.phpt new file mode 100644 index 000000000000..5a7f6a14c9bb --- /dev/null +++ b/ext/gd/tests/imageloadfont_short_read.phpt @@ -0,0 +1,65 @@ +--TEST-- +imageloadfont(): header read must stay in bounds on short reads +--EXTENSIONS-- +gd +--FILE-- +data = pack('V4', 1, 32, 1, 1) . "\x00"; + return true; + } + + public function stream_read($count): string + { + return $this->pos < strlen($this->data) ? $this->data[$this->pos++] : ''; + } + + public function stream_eof(): bool + { + return $this->pos >= strlen($this->data); + } + + public function stream_stat() + { + return []; + } + + public function stream_tell(): int + { + return $this->pos; + } + + public function stream_seek($offset, $whence): bool + { + if ($whence === SEEK_CUR) { + $this->pos += $offset; + } elseif ($whence === SEEK_END) { + $this->pos = strlen($this->data) + $offset; + } else { + $this->pos = $offset; + } + return true; + } + + public function stream_set_option($option, $arg1, $arg2): bool + { + return false; + } +} + +stream_wrapper_register('drip', drip::class); +var_dump(imageloadfont('drip://font') instanceof GdFont); +?> +--EXPECT-- +bool(true) diff --git a/ext/intl/collator/collator_create.cpp b/ext/intl/collator/collator_create.cpp index 7aa715f8e091..d2f16b67a26f 100644 --- a/ext/intl/collator/collator_create.cpp +++ b/ext/intl/collator/collator_create.cpp @@ -44,6 +44,10 @@ static int collator_ctor(INTERNAL_FUNCTION_PARAMETERS) INTL_CHECK_LOCALE_LEN_OR_FAILURE(locale_len); COLLATOR_METHOD_FETCH_OBJECT; + if (co->ucoll) { + zend_throw_error(NULL, "Collator object is already constructed"); + return FAILURE; + } if(locale_len == 0) { locale = (char *)intl_locale_get_default(); diff --git a/ext/intl/listformatter/listformatter_class.cpp b/ext/intl/listformatter/listformatter_class.cpp index 950eb81b7e15..221dadfcd3b5 100644 --- a/ext/intl/listformatter/listformatter_class.cpp +++ b/ext/intl/listformatter/listformatter_class.cpp @@ -74,6 +74,11 @@ PHP_METHOD(IntlListFormatter, __construct) Z_PARAM_LONG(width) ZEND_PARSE_PARAMETERS_END(); + if (LISTFORMATTER_OBJECT(obj)) { + zend_throw_error(NULL, "IntlListFormatter object is already constructed"); + RETURN_THROWS(); + } + if(locale_len == 0) { locale = (char *)intl_locale_get_default(); } diff --git a/ext/intl/spoofchecker/spoofchecker_create.cpp b/ext/intl/spoofchecker/spoofchecker_create.cpp index f4b51492cac6..df032f91035e 100644 --- a/ext/intl/spoofchecker/spoofchecker_create.cpp +++ b/ext/intl/spoofchecker/spoofchecker_create.cpp @@ -38,6 +38,10 @@ U_CFUNC PHP_METHOD(Spoofchecker, __construct) ZEND_PARSE_PARAMETERS_NONE(); SPOOFCHECKER_METHOD_FETCH_OBJECT_NO_CHECK; + if (co->uspoof) { + zend_throw_error(NULL, "Spoofchecker object is already constructed"); + RETURN_THROWS(); + } co->uspoof = uspoof_open(SPOOFCHECKER_ERROR_CODE_P(co)); if (U_FAILURE(INTL_DATA_ERROR_CODE(co))) { diff --git a/ext/intl/tests/collator_double_ctor.phpt b/ext/intl/tests/collator_double_ctor.phpt new file mode 100644 index 000000000000..93b72f7392b3 --- /dev/null +++ b/ext/intl/tests/collator_double_ctor.phpt @@ -0,0 +1,16 @@ +--TEST-- +Collator double construction should not be allowed +--EXTENSIONS-- +intl +--FILE-- +__construct('en_US'); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Collator object is already constructed diff --git a/ext/intl/tests/listformatter/listformatter_double_ctor.phpt b/ext/intl/tests/listformatter/listformatter_double_ctor.phpt new file mode 100644 index 000000000000..f8b0ca1e1633 --- /dev/null +++ b/ext/intl/tests/listformatter/listformatter_double_ctor.phpt @@ -0,0 +1,16 @@ +--TEST-- +IntlListFormatter double construction should not be allowed +--EXTENSIONS-- +intl +--FILE-- +__construct('en_US'); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +IntlListFormatter object is already constructed diff --git a/ext/intl/tests/spoofchecker_double_ctor.phpt b/ext/intl/tests/spoofchecker_double_ctor.phpt new file mode 100644 index 000000000000..01dae5ab4bc5 --- /dev/null +++ b/ext/intl/tests/spoofchecker_double_ctor.phpt @@ -0,0 +1,18 @@ +--TEST-- +Spoofchecker double construction should not be allowed +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +__construct(); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Spoofchecker object is already constructed diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 3cfb7a3fa01a..8f5e22640ccc 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -714,6 +714,9 @@ void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS) if (Z_TYPE_P(pg_rows) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pg_rows), tmp) { if (!_pdo_pgsql_send_copy_data(H, tmp)) { + if (EG(exception)) { + RETURN_THROWS(); + } pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); PDO_HANDLE_DBH_ERR(); RETURN_FALSE; @@ -736,6 +739,9 @@ void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS) tmp = iter->funcs->get_current_data(iter); if (!_pdo_pgsql_send_copy_data(H, tmp)) { zend_iterator_dtor(iter); + if (EG(exception)) { + RETURN_THROWS(); + } pdo_pgsql_error(dbh, PGRES_FATAL_ERROR, NULL); PDO_HANDLE_DBH_ERR(); RETURN_FALSE; diff --git a/ext/pdo_pgsql/tests/copy_from_array_non_stringable.phpt b/ext/pdo_pgsql/tests/copy_from_array_non_stringable.phpt new file mode 100644 index 000000000000..65edb30d6671 --- /dev/null +++ b/ext/pdo_pgsql/tests/copy_from_array_non_stringable.phpt @@ -0,0 +1,49 @@ +--TEST-- +PDO PgSQL pgsqlCopyFromArray()/copyFromArray() with a non-stringable row throws without polluting errorInfo +--EXTENSIONS-- +pdo_pgsql +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + +$db->exec('CREATE TABLE test_copy_non_stringable (v text)'); + +try { + $db->pgsqlCopyFromArray('test_copy_non_stringable', [new stdClass()]); +} catch (\Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +var_dump($db->errorInfo()[0]); + +$pgsql = PDOTest::test_factory(__DIR__ . '/common.phpt', Pdo\Pgsql::class, true); +$pgsql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); + +try { + $pgsql->copyFromArray('test_copy_non_stringable', [new stdClass()]); +} catch (\Error $e) { + echo $e->getMessage(), PHP_EOL; +} + +var_dump($pgsql->errorInfo()[0]); +?> +--CLEAN-- +query('DROP TABLE IF EXISTS test_copy_non_stringable CASCADE'); +?> +--EXPECTF-- +Deprecated: Method PDO::pgsqlCopyFromArray() is deprecated since 8.5, use Pdo\Pgsql::copyFromArray() instead in %s on line %d +Object of class stdClass could not be converted to string +string(5) "00000" +Object of class stdClass could not be converted to string +string(5) "00000" diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index b1c8c4747cc6..1e41b2d0801d 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -149,12 +149,12 @@ static int phar_compare_dir_name(Bucket *f, Bucket *s) /* {{{ */ static php_stream *phar_make_dirstream(const char *dir, size_t dirlen, const HashTable *manifest) /* {{{ */ { HashTable *data; - char *entry; + const char *entry; ALLOC_HASHTABLE(data); zend_hash_init(data, 64, NULL, NULL, 0); - if ((*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) || (dirlen >= sizeof(".phar")-1 && !memcmp(dir, ".phar", sizeof(".phar")-1))) { + if ((*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) || phar_path_is_magic_phar_ex(dir, dirlen)) { /* make empty root directory for empty phar */ /* make empty directory for .phar magic directory */ return php_stream_alloc(&phar_dir_ops, data, NULL, "r"); @@ -171,7 +171,7 @@ static php_stream *phar_make_dirstream(const char *dir, size_t dirlen, const Has if (*dir == '/') { /* root directory */ - if (zend_string_starts_with_literal(str_key, ".phar")) { + if (phar_is_magic_phar(str_key)) { /* do not add any magic entries to this directory */ continue; } @@ -181,9 +181,7 @@ static php_stream *phar_make_dirstream(const char *dir, size_t dirlen, const Has /* the entry has a path separator and is a subdirectory */ keylen = has_slash - ZSTR_VAL(str_key); } - entry = safe_emalloc(keylen, 1, 1); - memcpy(entry, ZSTR_VAL(str_key), keylen); - entry[keylen] = '\0'; + entry = ZSTR_VAL(str_key); } else { if (0 != memcmp(ZSTR_VAL(str_key), dir, dirlen)) { /* entry in directory not found */ @@ -201,16 +199,12 @@ static php_stream *phar_make_dirstream(const char *dir, size_t dirlen, const Has if (has_slash) { /* is subdirectory */ save -= dirlen + 1; - entry = safe_emalloc(has_slash - save + dirlen, 1, 1); - memcpy(entry, save + dirlen + 1, has_slash - save - dirlen - 1); keylen = has_slash - save - dirlen - 1; - entry[keylen] = '\0'; + entry = save + dirlen + 1; } else { /* is file */ save -= dirlen + 1; - entry = safe_emalloc(keylen - dirlen, 1, 1); - memcpy(entry, save + dirlen + 1, keylen - dirlen - 1); - entry[keylen - dirlen - 1] = '\0'; + entry = save + dirlen + 1; keylen = keylen - dirlen - 1; } } @@ -227,8 +221,6 @@ static php_stream *phar_make_dirstream(const char *dir, size_t dirlen, const Has ZVAL_NULL(&dummy); zend_hash_str_update(data, entry, keylen, &dummy); } - - efree(entry); } ZEND_HASH_FOREACH_END(); if (FAILURE != zend_hash_has_more_elements(data)) { diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index d75498f9c95a..f175fb7b9d60 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -376,6 +376,31 @@ static inline bool phar_validate_alias(const char *alias, size_t alias_len) /* { } /* }}} */ +static inline bool phar_path_is_magic_phar_ex(const char *path, size_t path_len) /* {{{ */ +{ + if (path_len > 0 && path[0] == '/') { + path++; + path_len--; + } + + if (path_len < sizeof(".phar") - 1 || memcmp(path, ".phar", sizeof(".phar") - 1) != 0) { + return false; + } + + if (path_len == sizeof(".phar") - 1) { + return true; + } + + return path[sizeof(".phar") - 1] == '/' || path[sizeof(".phar") - 1] == '\\'; +} +/* }}} */ + +static inline bool phar_is_magic_phar(const zend_string *path) /* {{{ */ +{ + return phar_path_is_magic_phar_ex(ZSTR_VAL(path), ZSTR_LEN(path)); +} +/* }}} */ + static inline void phar_set_inode(phar_entry_info *entry) /* {{{ */ { if (entry->phar->fname) { diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 46db925cfd50..e2ba71c501e7 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -1612,7 +1612,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ return ZEND_HASH_APPLY_STOP; } after_open_fp: - if (str_key_len >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) { + if (phar_path_is_magic_phar_ex(str_key, str_key_len)) { /* silently skip any files that would be added to the magic .phar directory */ if (save) { efree(save); @@ -3401,14 +3401,14 @@ PHP_METHOD(Phar, copy) RETURN_THROWS(); } - if (zend_string_starts_with_literal(old_file, ".phar")) { + if (phar_is_magic_phar(old_file)) { /* can't copy a meta file */ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "file \"%s\" cannot be copied to file \"%s\", cannot copy Phar meta-file in %s", ZSTR_VAL(old_file), ZSTR_VAL(new_file), ZSTR_VAL(phar_obj->archive->fname)); RETURN_THROWS(); } - if (zend_string_starts_with_literal(new_file, ".phar")) { + if (phar_is_magic_phar(new_file)) { /* can't copy a meta file */ zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "file \"%s\" cannot be copied to file \"%s\", cannot copy to Phar meta-file in %s", ZSTR_VAL(old_file), ZSTR_VAL(new_file), ZSTR_VAL(phar_obj->archive->fname)); @@ -3495,7 +3495,7 @@ PHP_METHOD(Phar, offsetExists) } /* none of these are real files, so they don't exist */ - RETURN_BOOL(!zend_string_starts_with_literal(file_name, ".phar")); + RETURN_BOOL(!phar_is_magic_phar(file_name)); } else { /* If the info class is not based on PharFileInfo, directories are not directly instantiable */ if (UNEXPECTED(!instanceof_function(phar_obj->spl.info_class, phar_ce_entry))) { @@ -3538,7 +3538,7 @@ PHP_METHOD(Phar, offsetGet) RETURN_THROWS(); } - if (zend_string_starts_with_literal(file_name, ".phar")) { + if (phar_is_magic_phar(file_name)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot directly get any files or directories in magic \".phar\" directory"); RETURN_THROWS(); } @@ -3568,16 +3568,9 @@ static void phar_add_file(phar_archive_data **pphar, zend_string *file_name, con ALLOCA_FLAG(filename_use_heap) #endif - if ( - zend_string_starts_with_literal(file_name, ".phar") - || zend_string_starts_with_literal(file_name, "/.phar") - ) { - size_t prefix_len = (ZSTR_VAL(file_name)[0] == '/') + sizeof(".phar")-1; - char next_char = ZSTR_VAL(file_name)[prefix_len]; - if (next_char == '/' || next_char == '\\' || next_char == '\0') { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create any files in magic \".phar\" directory"); - return; - } + if (phar_is_magic_phar(file_name)) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create any files in magic \".phar\" directory"); + return; } /* TODO How to handle Windows path normalisation with zend_string ? */ @@ -3722,7 +3715,7 @@ PHP_METHOD(Phar, offsetSet) RETURN_THROWS(); } - if (zend_string_starts_with_literal(file_name, ".phar")) { + if (phar_is_magic_phar(file_name)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot set any files or directories in magic \".phar\" directory"); RETURN_THROWS(); } @@ -3789,16 +3782,9 @@ PHP_METHOD(Phar, addEmptyDir) PHAR_ARCHIVE_OBJECT(); - if ( - zend_string_starts_with_literal(dir_name, ".phar") - || zend_string_starts_with_literal(dir_name, "/.phar") - ) { - size_t prefix_len = (ZSTR_VAL(dir_name)[0] == '/') + sizeof(".phar") - 1; - char next_char = ZSTR_VAL(dir_name)[prefix_len]; - if (next_char == '/' || next_char == '\\' || next_char == '\0') { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory"); - RETURN_THROWS(); - } + if (phar_is_magic_phar(dir_name)) { + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory"); + RETURN_THROWS(); } phar_mkdir(&phar_obj->archive, dir_name); @@ -4099,7 +4085,7 @@ ZEND_ATTRIBUTE_NONNULL static zend_result phar_extract_file(bool overwrite, phar return SUCCESS; } - if (zend_string_starts_with_literal(entry->filename, ".phar")) { + if (phar_is_magic_phar(entry->filename)) { return SUCCESS; } /* strip .. from path and restrict it to be under dest directory */ diff --git a/ext/phar/tests/phar_magic_dir_prefix.phpt b/ext/phar/tests/phar_magic_dir_prefix.phpt new file mode 100644 index 000000000000..e1f1c517632e --- /dev/null +++ b/ext/phar/tests/phar_magic_dir_prefix.phpt @@ -0,0 +1,80 @@ +--TEST-- +Phar: .phar-prefixed non-magic directories are accessible +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--FILE-- +addFromString('.pharx/from-string.txt', 'from-string'); +$phar->addFromString('/.phary/leading.txt', 'leading'); +$phar->copy('.pharx/array.txt', '.pharx/copy.txt'); + +var_dump(isset($phar['.pharx/array.txt'])); +echo $phar['.pharx/array.txt']->getContent(), "\n"; +echo file_get_contents($pname . '/.pharx/from-string.txt'), "\n"; +echo file_get_contents($pname . '/.phary/leading.txt'), "\n"; +echo file_get_contents($pname . '/.pharx/copy.txt'), "\n"; + +$root = []; +$dh = opendir($pname . '/'); +while (false !== ($entry = readdir($dh))) { + $root[] = $entry; +} +closedir($dh); +sort($root); +var_dump($root); + +$subdir = []; +$dh = opendir($pname . '/.pharx'); +while (false !== ($entry = readdir($dh))) { + $subdir[] = $entry; +} +closedir($dh); +sort($subdir); +var_dump($subdir); + +try { + $phar->addFromString('.phar/still-magic.txt', 'no'); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + $phar->addEmptyDir('/.phar'); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +?> +--CLEAN-- + +--EXPECT-- +bool(true) +array +from-string +leading +array +array(2) { + [0]=> + string(6) ".pharx" + [1]=> + string(6) ".phary" +} +array(3) { + [0]=> + string(9) "array.txt" + [1]=> + string(8) "copy.txt" + [2]=> + string(15) "from-string.txt" +} +Cannot create any files in magic ".phar" directory +Cannot create a directory in magic ".phar" directory diff --git a/ext/phar/util.c b/ext/phar/util.c index 9906728a00f7..2962f778b274 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -207,7 +207,7 @@ zend_result phar_mount_entry(phar_archive_data *phar, const char *filename, size return FAILURE; } - if (path_len >= sizeof(".phar")-1 && !memcmp(path, ".phar", sizeof(".phar")-1)) { + if (phar_path_is_magic_phar_ex(path, path_len)) { /* no creating magic phar files by mounting them */ return FAILURE; } @@ -1181,7 +1181,7 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, si *error = NULL; } - if (security && path_len >= sizeof(".phar")-1 && !memcmp(path, ".phar", sizeof(".phar")-1)) { + if (security && phar_path_is_magic_phar_ex(path, path_len)) { if (error) { spprintf(error, 4096, "phar error: cannot directly access magic \".phar\" directory or files within it"); } diff --git a/ext/session/mod_user.c b/ext/session/mod_user.c index c87e9654c4ea..71b8abdea8b0 100644 --- a/ext/session/mod_user.c +++ b/ext/session/mod_user.c @@ -27,16 +27,16 @@ static void ps_call_handler(zval *func, int argc, zval *argv, zval *retval) PS(in_save_handler) = false; ZVAL_UNDEF(retval); php_error_docref(NULL, E_WARNING, "Cannot call session save handler in a recursive manner"); - return; - } - PS(in_save_handler) = true; - if (call_user_function(NULL, NULL, func, retval, argc, argv) == FAILURE) { - zval_ptr_dtor(retval); - ZVAL_UNDEF(retval); - } else if (Z_ISUNDEF_P(retval)) { - ZVAL_NULL(retval); + } else { + PS(in_save_handler) = true; + if (call_user_function(NULL, NULL, func, retval, argc, argv) == FAILURE) { + zval_ptr_dtor(retval); + ZVAL_UNDEF(retval); + } else if (Z_ISUNDEF_P(retval)) { + ZVAL_NULL(retval); + } + PS(in_save_handler) = false; } - PS(in_save_handler) = false; for (i = 0; i < argc; i++) { zval_ptr_dtor(&argv[i]); } diff --git a/ext/session/tests/user_session_module/recursive_handler_argv_leak.phpt b/ext/session/tests/user_session_module/recursive_handler_argv_leak.phpt new file mode 100644 index 000000000000..2b954494e87c --- /dev/null +++ b/ext/session/tests/user_session_module/recursive_handler_argv_leak.phpt @@ -0,0 +1,31 @@ +--TEST-- +ps_call_handler() releases argv when a recursive save-handler call is rejected +--INI-- +session.save_path= +session.name=PHPSESSID +--EXTENSIONS-- +session +--FILE-- +tripped) { + $this->tripped = true; + session_destroy(); + } + return true; + } +} + +session_set_save_handler(new H, true); +session_start(); +$_SESSION['x'] = 1; +session_write_close(); +echo "done\n"; +?> +--EXPECTF-- +Warning: session_destroy(): Cannot call session save handler in a recursive manner in %s on line %d + +Warning: session_destroy(): Session object destruction failed in %s on line %d +done diff --git a/ext/sockets/tests/gh21161.phpt b/ext/sockets/tests/gh21161.phpt index 8a3958a583d9..f8dbe909c8d6 100644 --- a/ext/sockets/tests/gh21161.phpt +++ b/ext/sockets/tests/gh21161.phpt @@ -7,6 +7,7 @@ sockets if (substr(PHP_OS, 0, 3) == 'WIN') { die('skip.. Not valid for Windows'); } +require 'ipv6_skipif.inc'; ?> --FILE-- +--FILE-- +getMessage(), "\n"; +} + +try { + sodium_crypto_pwhash_str("password", SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, 1); +} catch (SodiumException $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECTF-- +sodium_crypto_pwhash(): Argument #5 ($memlimit) must be greater than or equal to %d +sodium_crypto_pwhash_str(): Argument #3 ($memlimit) must be greater than or equal to %d diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 39d27bc40105..1976192e7b06 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -1153,18 +1153,12 @@ PHP_METHOD(ArrayObject, count) RETURN_LONG(spl_array_object_count_elements_helper(intern)); } /* }}} */ -enum spl_array_object_sort_methods { - SPL_NAT_SORT, - SPL_CALLBACK_SORT, - SPL_OPTIONAL_FLAG_SORT -}; - -static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, const char *fname, size_t fname_len, enum spl_array_object_sort_methods use_arg) /* {{{ */ +static void spl_array_method(zval *return_value, spl_array_object *intern, const char *fname, size_t fname_len, const zval *extra_arg) /* {{{ */ { - spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS); HashTable **ht_ptr = spl_array_get_hash_table_ptr(intern); HashTable *aht = *ht_ptr; - zval params[2], *arg = NULL; + zval params[2]; + uint32_t param_num = 1; zend_function *fn = zend_hash_str_find_ptr(EG(function_table), fname, fname_len); if (UNEXPECTED(fn == NULL)) { @@ -1176,68 +1170,85 @@ static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, const char *fname, si ZVAL_ARR(Z_REFVAL(params[0]), aht); GC_ADDREF(aht); - if (use_arg == SPL_NAT_SORT) { - if (zend_parse_parameters_none() == FAILURE) { - goto exit; - } - - intern->nApplyCount++; - zend_call_known_function(fn, NULL, NULL, return_value, 1, params, NULL); - intern->nApplyCount--; - } else if (use_arg == SPL_OPTIONAL_FLAG_SORT) { - zend_long sort_flags = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &sort_flags) == FAILURE) { - goto exit; - } - ZVAL_LONG(¶ms[1], sort_flags); - intern->nApplyCount++; - zend_call_known_function(fn, NULL, NULL, return_value, 2, params, NULL); - intern->nApplyCount--; - } else { - ZEND_ASSERT(use_arg == SPL_CALLBACK_SORT); - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) { - goto exit; - } - ZVAL_COPY_VALUE(¶ms[1], arg); - intern->nApplyCount++; - zend_call_known_function(fn, NULL, NULL, return_value, 2, params, NULL); - intern->nApplyCount--; + if (extra_arg) { + param_num = 2; + ZVAL_COPY_VALUE(¶ms[1], extra_arg); } + intern->nApplyCount++; + zend_call_known_function(fn, NULL, NULL, return_value, param_num, params, NULL); + intern->nApplyCount--; -exit: - { - zval *ht_zv = Z_REFVAL(params[0]); - zend_array_release(*ht_ptr); - SEPARATE_ARRAY(ht_zv); - *ht_ptr = Z_ARRVAL_P(ht_zv); - ZVAL_NULL(ht_zv); - zval_ptr_dtor(¶ms[0]); - } + zval *ht_zv = Z_REFVAL(params[0]); + zend_array_release(*ht_ptr); + SEPARATE_ARRAY(ht_zv); + *ht_ptr = Z_ARRVAL_P(ht_zv); + ZVAL_NULL(ht_zv); + zval_ptr_dtor(¶ms[0]); } /* }}} */ -#define SPL_ARRAY_METHOD(cname, fname, use_arg) \ -PHP_METHOD(cname, fname) \ -{ \ - spl_array_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, #fname, sizeof(#fname)-1, use_arg); \ -} - /* Sort the entries by values. */ -SPL_ARRAY_METHOD(ArrayObject, asort, SPL_OPTIONAL_FLAG_SORT) +PHP_METHOD(ArrayObject, asort) +{ + zend_long sort_flags = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &sort_flags) == FAILURE) { + RETURN_THROWS(); + } + zval sort_flag_param; + ZVAL_LONG(&sort_flag_param, sort_flags); + + spl_array_method(return_value, Z_SPLARRAY_P(ZEND_THIS), ZEND_STRL("asort"), &sort_flag_param); +} /* Sort the entries by key. */ -SPL_ARRAY_METHOD(ArrayObject, ksort, SPL_OPTIONAL_FLAG_SORT) +PHP_METHOD(ArrayObject, ksort) +{ + zend_long sort_flags = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &sort_flags) == FAILURE) { + RETURN_THROWS(); + } + zval sort_flag_param; + ZVAL_LONG(&sort_flag_param, sort_flags); + + spl_array_method(return_value, Z_SPLARRAY_P(ZEND_THIS), ZEND_STRL("ksort"), &sort_flag_param); +} /* Sort the entries by values user defined function. */ -SPL_ARRAY_METHOD(ArrayObject, uasort, SPL_CALLBACK_SORT) +PHP_METHOD(ArrayObject, uasort) +{ + zval *callback = NULL; + /* TODO: Should check variable is callable */ + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callback) == FAILURE) { + RETURN_THROWS(); + } + spl_array_method(return_value, Z_SPLARRAY_P(ZEND_THIS), ZEND_STRL("uasort"), callback); +} /* Sort the entries by key using user defined function. */ -SPL_ARRAY_METHOD(ArrayObject, uksort, SPL_CALLBACK_SORT) +PHP_METHOD(ArrayObject, uksort) +{ + zval *callback = NULL; + /* TODO: Should check variable is callable */ + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callback) == FAILURE) { + RETURN_THROWS(); + } + spl_array_method(return_value, Z_SPLARRAY_P(ZEND_THIS), ZEND_STRL("uksort"), callback); +} /* Sort the entries by values using "natural order" algorithm. */ -SPL_ARRAY_METHOD(ArrayObject, natsort, SPL_NAT_SORT) +PHP_METHOD(ArrayObject, natsort) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + spl_array_method(return_value, Z_SPLARRAY_P(ZEND_THIS), ZEND_STRL("natsort"), NULL); +} -/* {{{ Sort the entries by key using case-insensitive "natural order" algorithm. */ -SPL_ARRAY_METHOD(ArrayObject, natcasesort, SPL_NAT_SORT) +/* Sort the entries by key using case-insensitive "natural order" algorithm. */ +PHP_METHOD(ArrayObject, natcasesort) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + spl_array_method(return_value, Z_SPLARRAY_P(ZEND_THIS), ZEND_STRL("natcasesort"), NULL); +} /* {{{ Serialize the object */ PHP_METHOD(ArrayObject, serialize) diff --git a/ext/spl/tests/ArrayObject/arrayObject_uasort_error1.phpt b/ext/spl/tests/ArrayObject/arrayObject_uasort_error1.phpt index d4c853245127..32defd35e6fc 100644 --- a/ext/spl/tests/ArrayObject/arrayObject_uasort_error1.phpt +++ b/ext/spl/tests/ArrayObject/arrayObject_uasort_error1.phpt @@ -1,26 +1,16 @@ --TEST-- -Test ArrayObject::uasort() function : wrong arg count +ArrayObject::uasort() function: non callable error --FILE-- uasort(); -} catch (ArgumentCountError $e) { - echo $e->getMessage() . "\n"; + $ao->uasort('not_a_valid_function'); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; } -try { - $ao->uasort(1,2); -} catch (ArgumentCountError $e) { - echo $e->getMessage() . "\n"; -} ?> --EXPECT-- -ArrayObject::uasort() expects exactly 1 argument, 0 given -ArrayObject::uasort() expects exactly 1 argument, 2 given +TypeError: uasort(): Argument #2 ($callback) must be a valid callback, function "not_a_valid_function" not found or invalid function name diff --git a/ext/spl/tests/ArrayObject/arrayObject_uksort_error1.phpt b/ext/spl/tests/ArrayObject/arrayObject_uksort_error1.phpt index 71164383e41b..11b40aae8c8f 100644 --- a/ext/spl/tests/ArrayObject/arrayObject_uksort_error1.phpt +++ b/ext/spl/tests/ArrayObject/arrayObject_uksort_error1.phpt @@ -1,26 +1,16 @@ --TEST-- -Test ArrayObject::uksort() function : wrong arg count +ArrayObject::uksort() function: non callable error --FILE-- uksort(); -} catch (ArgumentCountError $e) { - echo $e->getMessage() . "\n"; + $ao->uksort('not_a_valid_function'); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; } -try { - $ao->uksort(1,2); -} catch (ArgumentCountError $e) { - echo $e->getMessage() . "\n"; -} ?> --EXPECT-- -ArrayObject::uksort() expects exactly 1 argument, 0 given -ArrayObject::uksort() expects exactly 1 argument, 2 given +TypeError: uksort(): Argument #2 ($callback) must be a valid callback, function "not_a_valid_function" not found or invalid function name diff --git a/ext/standard/filters.c b/ext/standard/filters.c index d26429704e26..c0741b46074e 100644 --- a/ext/standard/filters.c +++ b/ext/standard/filters.c @@ -1575,7 +1575,7 @@ static php_stream_filter_status_t strfilter_convert_filter( php_stream_bucket_delref(bucket); } - if (flags != PSFS_FLAG_NORMAL) { + if (flags & PSFS_FLAG_FLUSH_CLOSE) { if (strfilter_convert_append_bucket(inst, stream, thisfilter, buckets_out, NULL, 0, &consumed, php_stream_is_persistent(stream)) != SUCCESS) { diff --git a/ext/standard/math.c b/ext/standard/math.c index 1898d210ce65..6550e965d177 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -22,6 +22,7 @@ #include "zend_exceptions.h" #include "zend_multiply.h" #include "zend_portability.h" +#include "zend_strtod.h" #include #include @@ -1001,7 +1002,7 @@ PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base) if (Z_TYPE_P(arg) == IS_DOUBLE) { double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */ char *ptr, *end; - char buf[(sizeof(double) << 3) + 1]; + char buf[ZEND_DOUBLE_MAX_LENGTH]; /* Don't try to convert +/- infinity */ if (fvalue == ZEND_INFINITY || fvalue == -ZEND_INFINITY) { diff --git a/ext/standard/tests/assert/gh22373.phpt b/ext/standard/tests/assert/gh22373.phpt new file mode 100644 index 000000000000..8c26f77f490b --- /dev/null +++ b/ext/standard/tests/assert/gh22373.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-22373: AST pretty-printing drops meaningful parentheses surrounding property access +--FILE-- +f)('abc') !== 'cba'); + } catch (Error $e) { + echo $e->getMessage(), PHP_EOL; + } + try { + assert(($this?->f)('abc') !== 'cba'); + } catch (Error $e) { + echo $e->getMessage(), PHP_EOL; + } + try { + assert((self::$sf)('abc') !== 'cba'); + } catch (Error $e) { + echo $e->getMessage(), PHP_EOL; + } + } +} + +new Foo(); + +?> +--EXPECT-- +assert(($this->f)('abc') !== 'cba') +assert(($this?->f)('abc') !== 'cba') +assert((self::$sf)('abc') !== 'cba') diff --git a/ext/standard/tests/filters/gh22360.phpt b/ext/standard/tests/filters/gh22360.phpt new file mode 100644 index 000000000000..b23483b22b92 --- /dev/null +++ b/ext/standard/tests/filters/gh22360.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-22360 (convert.base64-encode emits padding on incremental flush) +--FILE-- + +--CLEAN-- + +--EXPECT-- +string(4) "YWJj" +YWJj diff --git a/ext/standard/tests/math/gh22395.phpt b/ext/standard/tests/math/gh22395.phpt new file mode 100644 index 000000000000..73c2c66da199 --- /dev/null +++ b/ext/standard/tests/math/gh22395.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-22395 (base_convert outputs at most 64 characters) +--FILE-- + +--EXPECT-- +int(78) +string(13) "4b61b5e0639ff" diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 489d7d0a260b..a4562368735e 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -57,6 +57,12 @@ class _ZendTestClass implements _ZendTestInterface { public static $_StaticProp; public static int $staticIntProp = 123; + /* If there's a problem with escapes in quotes in generated headers, + * the generated header won't compile. (tests/gh22169.phpt) */ + public static string $doubleQuoteEscaped = "BEGIN \n\r\t\v\e\f\\\$\"\101\x41\u{41} END"; + public static string $singleQuoteEscaped = 'BEGIN \n\r\t\v\e\f\\\$\"\101\x41\u{41} END'; + public static string $escapeInterpolated = "begin \$ \\$ end"; + public int $intProp = 123; public ?stdClass $classProp = null; public stdClass|Iterator|null $classUnionProp = null; diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 94f75cdb3601..bd6548e7bffa 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 test.stub.php instead. - * Stub hash: 4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9 + * Stub hash: 8ca2fc33013d5a1c325bf5f0090cc6416a242297 * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, IS_NEVER, 0) @@ -795,6 +795,27 @@ static zend_class_entry *register_class__ZendTestClass(zend_class_entry *class_e zend_declare_typed_property(class_entry, property_staticIntProp_name, &property_staticIntProp_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release_ex(property_staticIntProp_name, true); + zval property_doubleQuoteEscaped_default_value; + zend_string *property_doubleQuoteEscaped_default_value_str = zend_string_init("BEGIN \n\r\t\v\x1b\f\\$\"AAA END", strlen("BEGIN \n\r\t\v\x1b\f\\$\"AAA END"), 1); + ZVAL_STR(&property_doubleQuoteEscaped_default_value, property_doubleQuoteEscaped_default_value_str); + zend_string *property_doubleQuoteEscaped_name = zend_string_init("doubleQuoteEscaped", sizeof("doubleQuoteEscaped") - 1, true); + zend_declare_typed_property(class_entry, property_doubleQuoteEscaped_name, &property_doubleQuoteEscaped_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release_ex(property_doubleQuoteEscaped_name, true); + + zval property_singleQuoteEscaped_default_value; + zend_string *property_singleQuoteEscaped_default_value_str = zend_string_init("BEGIN \\n\\r\\t\\v\\e\\f\\\\\\\\$\\\"\\101\\x41\\u{41} END", strlen("BEGIN \\n\\r\\t\\v\\e\\f\\\\\\\\$\\\"\\101\\x41\\u{41} END"), 1); + ZVAL_STR(&property_singleQuoteEscaped_default_value, property_singleQuoteEscaped_default_value_str); + zend_string *property_singleQuoteEscaped_name = zend_string_init("singleQuoteEscaped", sizeof("singleQuoteEscaped") - 1, true); + zend_declare_typed_property(class_entry, property_singleQuoteEscaped_name, &property_singleQuoteEscaped_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release_ex(property_singleQuoteEscaped_name, true); + + zval property_escapeInterpolated_default_value; + zend_string *property_escapeInterpolated_default_value_str = zend_string_init("begin $ \\$ end", strlen("begin $ \\$ end"), 1); + ZVAL_STR(&property_escapeInterpolated_default_value, property_escapeInterpolated_default_value_str); + zend_string *property_escapeInterpolated_name = zend_string_init("escapeInterpolated", sizeof("escapeInterpolated") - 1, true); + zend_declare_typed_property(class_entry, property_escapeInterpolated_name, &property_escapeInterpolated_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release_ex(property_escapeInterpolated_name, true); + zval property_intProp_default_value; ZVAL_LONG(&property_intProp_default_value, 123); zend_string *property_intProp_name = zend_string_init("intProp", sizeof("intProp") - 1, true); diff --git a/ext/zend_test/test_decl.h b/ext/zend_test/test_decl.h index 4a6babbe12b9..2561000f4b60 100644 --- a/ext/zend_test/test_decl.h +++ b/ext/zend_test/test_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9 */ + * Stub hash: 8ca2fc33013d5a1c325bf5f0090cc6416a242297 */ -#ifndef ZEND_TEST_DECL_4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9_H -#define ZEND_TEST_DECL_4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9_H +#ifndef ZEND_TEST_DECL_8ca2fc33013d5a1c325bf5f0090cc6416a242297_H +#define ZEND_TEST_DECL_8ca2fc33013d5a1c325bf5f0090cc6416a242297_H typedef enum zend_enum_ZendTestUnitEnum { ZEND_ENUM_ZendTestUnitEnum_Foo = 1, @@ -27,4 +27,4 @@ typedef enum zend_enum_ZendTestEnumWithInterface { ZEND_ENUM_ZendTestEnumWithInterface_Bar = 2, } zend_enum_ZendTestEnumWithInterface; -#endif /* ZEND_TEST_DECL_4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9_H */ +#endif /* ZEND_TEST_DECL_8ca2fc33013d5a1c325bf5f0090cc6416a242297_H */ diff --git a/ext/zend_test/test_legacy_arginfo.h b/ext/zend_test/test_legacy_arginfo.h index a4c1ae3f2c96..a254a637e07d 100644 --- a/ext/zend_test/test_legacy_arginfo.h +++ b/ext/zend_test/test_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9 + * Stub hash: 8ca2fc33013d5a1c325bf5f0090cc6416a242297 * Has decl header: yes */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, 0) @@ -650,6 +650,27 @@ static zend_class_entry *register_class__ZendTestClass(zend_class_entry *class_e zend_declare_property_ex(class_entry, property_staticIntProp_name, &property_staticIntProp_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL); zend_string_release_ex(property_staticIntProp_name, true); + zval property_doubleQuoteEscaped_default_value; + zend_string *property_doubleQuoteEscaped_default_value_str = zend_string_init("BEGIN \n\r\t\v\x1b\f\\$\"AAA END", strlen("BEGIN \n\r\t\v\x1b\f\\$\"AAA END"), 1); + ZVAL_STR(&property_doubleQuoteEscaped_default_value, property_doubleQuoteEscaped_default_value_str); + zend_string *property_doubleQuoteEscaped_name = zend_string_init("doubleQuoteEscaped", sizeof("doubleQuoteEscaped") - 1, true); + zend_declare_property_ex(class_entry, property_doubleQuoteEscaped_name, &property_doubleQuoteEscaped_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL); + zend_string_release_ex(property_doubleQuoteEscaped_name, true); + + zval property_singleQuoteEscaped_default_value; + zend_string *property_singleQuoteEscaped_default_value_str = zend_string_init("BEGIN \\n\\r\\t\\v\\e\\f\\\\\\\\$\\\"\\101\\x41\\u{41} END", strlen("BEGIN \\n\\r\\t\\v\\e\\f\\\\\\\\$\\\"\\101\\x41\\u{41} END"), 1); + ZVAL_STR(&property_singleQuoteEscaped_default_value, property_singleQuoteEscaped_default_value_str); + zend_string *property_singleQuoteEscaped_name = zend_string_init("singleQuoteEscaped", sizeof("singleQuoteEscaped") - 1, true); + zend_declare_property_ex(class_entry, property_singleQuoteEscaped_name, &property_singleQuoteEscaped_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL); + zend_string_release_ex(property_singleQuoteEscaped_name, true); + + zval property_escapeInterpolated_default_value; + zend_string *property_escapeInterpolated_default_value_str = zend_string_init("begin $ \\$ end", strlen("begin $ \\$ end"), 1); + ZVAL_STR(&property_escapeInterpolated_default_value, property_escapeInterpolated_default_value_str); + zend_string *property_escapeInterpolated_name = zend_string_init("escapeInterpolated", sizeof("escapeInterpolated") - 1, true); + zend_declare_property_ex(class_entry, property_escapeInterpolated_name, &property_escapeInterpolated_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL); + zend_string_release_ex(property_escapeInterpolated_name, true); + zval property_intProp_default_value; ZVAL_LONG(&property_intProp_default_value, 123); zend_string *property_intProp_name = zend_string_init("intProp", sizeof("intProp") - 1, true); diff --git a/ext/zend_test/tests/gh22169.phpt b/ext/zend_test/tests/gh22169.phpt new file mode 100644 index 000000000000..5ed2ab22fa21 --- /dev/null +++ b/ext/zend_test/tests/gh22169.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-22169: Ensure escaped strings in stubs are valid +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECT-- +string(44) "424547494e200a0d090b1b0c5c242241414120454e44" +string(43) "BEGIN \n\r\t\v\e\f\\\\$\"\101\x41\u{41} END" +string(14) "begin $ \$ end" +