Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ext/intl/msgformat/msgformat_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,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);
efree(message);
delete text;
Expand Down
13 changes: 13 additions & 0 deletions ext/intl/tests/msgfmt_format_error4.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,22 @@ try {
var_dump($e::class === 'IntlException');
var_dump("MessageFormatter::format(): Invalid UTF-8 data in string argument: '\x80'" === $e->getMessage());
}

try {
var_dump($mf->format(array("foo" => new class {
function __toString(): string {
return str_repeat("\x80", random_int(1, 1));
}
})));
} catch (Throwable $e) {
var_dump($e::class === 'IntlException');
var_dump("MessageFormatter::format(): Invalid UTF-8 data in string argument: '\x80'" === $e->getMessage());
}
?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
31 changes: 19 additions & 12 deletions ext/phar/phar.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -2310,28 +2316,29 @@ 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;
}

/* 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));
Expand Down
2 changes: 1 addition & 1 deletion ext/phar/phar_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
4 changes: 2 additions & 2 deletions ext/phar/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
40 changes: 40 additions & 0 deletions ext/phar/tests/bug74154.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
Bug #74154 (Phar extractTo creates empty files)
--EXTENSIONS--
phar
--FILE--
<?php

$dir = __DIR__.'/bug74154';
mkdir($dir);
file_put_contents("$dir/1.txt", str_repeat('h', 64));
file_put_contents("$dir/2.txt", str_repeat('i', 64));
$phar = new PharData(__DIR__.'/bug74154.tar');
$phar->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--
<?php
$dir = __DIR__.'/bug74154';
@unlink("$dir/1.txt");
@unlink("$dir/2.txt");
@rmdir($dir);
@unlink($dir.'_out/1.txt');
@unlink($dir.'_out/2.txt');
@rmdir($dir.'_out');
@unlink(__DIR__.'/bug74154.tar');
@unlink(__DIR__.'/bug74154.tar.gz');
?>
--EXPECT--
string(64) "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
string(64) "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"
5 changes: 3 additions & 2 deletions ext/phar/tests/tar/bug70417.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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--
<?php
Expand All @@ -22,4 +23,4 @@ $filename = __DIR__ . '/bug70417.tar';
@unlink("$filename.gz");
?>
--EXPECT--
bool(true)
int(0)
30 changes: 15 additions & 15 deletions ext/phar/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
/* }}} */

Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 2 additions & 1 deletion ext/posix/posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,8 @@ static void 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;
}
Expand Down