From 49c943de914294d90ecce0d5956f4cf4ff6461ba Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Fri, 7 May 2021 17:52:44 +0200 Subject: [PATCH 1/2] Fix #80863: ZipArchive::extractTo() ignores references We need to cater to references, when traversing the files to extract. While we're at it, we add an empty default clause, which would have caught this issue. We also move the `zval_file` declaration into a narrower scope, which makes it easier to reason about the value modification. --- ext/zip/php_zip.c | 7 +++++- ext/zip/tests/bug80863.phpt | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 ext/zip/tests/bug80863.phpt diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 21182068d1d7f..40c14561f7e54 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -2616,7 +2616,6 @@ static ZIPARCHIVE_METHOD(extractTo) zval *self = ZEND_THIS; zval *zval_files = NULL; - zval *zval_file = NULL; php_stream_statbuf ssb; char *pathto; size_t pathto_len; @@ -2653,7 +2652,9 @@ static ZIPARCHIVE_METHOD(extractTo) RETURN_FALSE; } for (i = 0; i < nelems; i++) { + zval *zval_file; if ((zval_file = zend_hash_index_find(Z_ARRVAL_P(zval_files), i)) != NULL) { +try_again: switch (Z_TYPE_P(zval_file)) { case IS_LONG: break; @@ -2662,6 +2663,10 @@ static ZIPARCHIVE_METHOD(extractTo) RETURN_FALSE; } break; + case IS_REFERENCE: + zval_file = Z_REFVAL_P(zval_file); + goto try_again; + EMPTY_SWITCH_DEFAULT_CASE(); } } } diff --git a/ext/zip/tests/bug80863.phpt b/ext/zip/tests/bug80863.phpt new file mode 100644 index 0000000000000..9b9d3b2ff39b5 --- /dev/null +++ b/ext/zip/tests/bug80863.phpt @@ -0,0 +1,43 @@ +--TEST-- +Bug #80863 (ZipArchive::extractTo() ignores references) +--SKIPIF-- + +--FILE-- +open($archive, ZipArchive::CREATE | ZipArchive::OVERWRITE); +$zip->addFromString("file1.txt", "contents"); +$zip->addFromString("file2.txt", "contents"); +$zip->close(); + +$target = __DIR__ . "/bug80683"; +mkdir($target); + +$files = [ + "file1.txt", + "file2.txt", +]; +// turn into references +foreach ($files as &$file); + +$zip = new ZipArchive(); +$zip->open($archive); +$zip->extractTo($target, $files); +var_dump(is_file("$target/file1.txt")); +var_dump(is_file("$target/file2.txt")); +?> +--EXPECT-- +bool(true) +bool(true) +--CLEAN-- + From 7049839d57799f9284ba053887f7ae059368b023 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Fri, 7 May 2021 18:18:06 +0200 Subject: [PATCH 2/2] Address review comments We can simplify by using `zend_hash_index_find_deref()`. We must not have an empty default clause, since the array is allowed to contain arbitrary values. --- ext/zip/php_zip.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 40c14561f7e54..2a2577de225b4 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -2653,8 +2653,7 @@ static ZIPARCHIVE_METHOD(extractTo) } for (i = 0; i < nelems; i++) { zval *zval_file; - if ((zval_file = zend_hash_index_find(Z_ARRVAL_P(zval_files), i)) != NULL) { -try_again: + if ((zval_file = zend_hash_index_find_deref(Z_ARRVAL_P(zval_files), i)) != NULL) { switch (Z_TYPE_P(zval_file)) { case IS_LONG: break; @@ -2663,10 +2662,6 @@ static ZIPARCHIVE_METHOD(extractTo) RETURN_FALSE; } break; - case IS_REFERENCE: - zval_file = Z_REFVAL_P(zval_file); - goto try_again; - EMPTY_SWITCH_DEFAULT_CASE(); } } }