Merge branch 'PHP-7.4' into PHP-8.0

* PHP-7.4:
  Fix #77565: Incorrect locator detection in ZIP-based phars
This commit is contained in:
Christoph M. Becker 2021-01-05 23:43:20 +01:00
commit 66a74f2f4a
6 changed files with 80 additions and 44 deletions

2
NEWS
View File

@ -39,6 +39,8 @@ PHP NEWS
- Phar:
. Fixed bug #76929 (zip-based phar does not respect phar.require_hash).
(david at bamsoftware, cmb)
. Fixed bug #77565 (Incorrect locator detection in ZIP-based phars). (cmb)
07 Jan 2021, PHP 8.0.1

View File

@ -12,7 +12,7 @@ $r = new Phar($fname, 0);
}
?>
--EXPECTF--
UnexpectedValueException: phar error: corrupted central directory entry, no magic signature in zip-based phar "%sbug69441.phar" in %sbug69441.php:%d
UnexpectedValueException: phar error: end of central directory not found in zip-based phar "%sbug69441.phar" in %sbug69441.php:%d
Stack trace:
#0 %s%ebug69441.php(%d): Phar->__construct('%s', 0)
#1 {main}

View File

@ -0,0 +1,13 @@
--TEST--
Bug #77565 (Incorrect locator detection in ZIP-based phars)
--SKIPIF--
<?php
if (!extension_loaded('phar')) die('skip phar extension not available');
?>
--FILE--
<?php
$phar = new PharData(__DIR__ . '/bug77565.zip');
var_dump($phar['1.zip']->getFilename());
?>
--EXPECT--
string(5) "1.zip"

BIN
ext/phar/tests/bug77565.zip Normal file

Binary file not shown.

View File

@ -11,4 +11,4 @@ try {
}
?>
--EXPECTF--
phar error: corrupt zip archive, zip file comment truncated in zip-based phar "%sfilecomment.zip"
phar error: end of central directory not found in zip-based phar "%sfilecomment.zip"

View File

@ -161,6 +161,29 @@ static void phar_zip_u2d_time(time_t time, char *dtime, char *ddate) /* {{{ */
}
/* }}} */
static char *phar_find_eocd(const char *s, size_t n)
{
const char *end = s + n + sizeof("PK\5\6") - 1 - sizeof(phar_zip_dir_end);
/* search backwards for end of central directory signatures */
do {
uint16_t comment_len;
const char *eocd_start = zend_memnrstr(s, "PK\5\6", sizeof("PK\5\6") - 1, end);
if (eocd_start == NULL) {
return NULL;
}
ZEND_ASSERT(eocd_start + sizeof(phar_zip_dir_end) <= s + n);
comment_len = PHAR_GET_16(((phar_zip_dir_end *) eocd_start)->comment_len);
if (eocd_start + sizeof(phar_zip_dir_end) + comment_len == s + n) {
/* we can't be sure, but this looks like the proper EOCD signature */
return (char *) eocd_start;
}
end = eocd_start;
} while (end > s);
return NULL;
}
/**
* Does not check for a previously opened phar in the cache.
*
@ -205,50 +228,48 @@ int phar_parse_zipfile(php_stream *fp, char *fname, size_t fname_len, char *alia
return FAILURE;
}
while ((p=(char *) memchr(p + 1, 'P', (size_t) (size - (p + 1 - buf)))) != NULL) {
if ((p - buf) + sizeof(locator) <= (size_t)size && !memcmp(p + 1, "K\5\6", 3)) {
memcpy((void *)&locator, (void *) p, sizeof(locator));
if (PHAR_GET_16(locator.centraldisk) != 0 || PHAR_GET_16(locator.disknumber) != 0) {
/* split archives not handled */
php_stream_close(fp);
if (error) {
spprintf(error, 4096, "phar error: split archives spanning multiple zips cannot be processed in zip-based phar \"%s\"", fname);
}
return FAILURE;
if ((p = phar_find_eocd(buf, size)) != NULL) {
memcpy((void *)&locator, (void *) p, sizeof(locator));
if (PHAR_GET_16(locator.centraldisk) != 0 || PHAR_GET_16(locator.disknumber) != 0) {
/* split archives not handled */
php_stream_close(fp);
if (error) {
spprintf(error, 4096, "phar error: split archives spanning multiple zips cannot be processed in zip-based phar \"%s\"", fname);
}
if (PHAR_GET_16(locator.counthere) != PHAR_GET_16(locator.count)) {
if (error) {
spprintf(error, 4096, "phar error: corrupt zip archive, conflicting file count in end of central directory record in zip-based phar \"%s\"", fname);
}
php_stream_close(fp);
return FAILURE;
}
mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
mydata->is_persistent = PHAR_G(persist);
/* read in archive comment, if any */
if (PHAR_GET_16(locator.comment_len)) {
metadata = p + sizeof(locator);
if (PHAR_GET_16(locator.comment_len) != size - (metadata - buf)) {
if (error) {
spprintf(error, 4096, "phar error: corrupt zip archive, zip file comment truncated in zip-based phar \"%s\"", fname);
}
php_stream_close(fp);
pefree(mydata, mydata->is_persistent);
return FAILURE;
}
phar_parse_metadata_lazy(metadata, &mydata->metadata_tracker, PHAR_GET_16(locator.comment_len), mydata->is_persistent);
} else {
ZVAL_UNDEF(&mydata->metadata_tracker.val);
}
goto foundit;
return FAILURE;
}
if (PHAR_GET_16(locator.counthere) != PHAR_GET_16(locator.count)) {
if (error) {
spprintf(error, 4096, "phar error: corrupt zip archive, conflicting file count in end of central directory record in zip-based phar \"%s\"", fname);
}
php_stream_close(fp);
return FAILURE;
}
mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
mydata->is_persistent = PHAR_G(persist);
/* read in archive comment, if any */
if (PHAR_GET_16(locator.comment_len)) {
metadata = p + sizeof(locator);
if (PHAR_GET_16(locator.comment_len) != size - (metadata - buf)) {
if (error) {
spprintf(error, 4096, "phar error: corrupt zip archive, zip file comment truncated in zip-based phar \"%s\"", fname);
}
php_stream_close(fp);
pefree(mydata, mydata->is_persistent);
return FAILURE;
}
phar_parse_metadata_lazy(metadata, &mydata->metadata_tracker, PHAR_GET_16(locator.comment_len), mydata->is_persistent);
} else {
ZVAL_UNDEF(&mydata->metadata_tracker.val);
}
goto foundit;
}
php_stream_close(fp);