diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 0d1f9bddce4e1..b707d425f72f8 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -2461,7 +2461,7 @@ static int phar_flush_clean_deleted_apply(zval *zv) /* {{{ */ { phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv); - if (entry->fp_refcount <= 0 && entry->is_deleted) { + if (entry->is_deleted && phar_entry_can_remove(entry)) { return ZEND_HASH_APPLY_REMOVE; } else { return ZEND_HASH_APPLY_KEEP; diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index e707fb4ca4669..7b2b25573d4ad 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -400,6 +400,11 @@ static inline void phar_set_inode(phar_entry_info *entry) /* {{{ */ } /* }}} */ +static inline bool phar_entry_can_remove(phar_entry_info *entry) +{ + return entry->fp_refcount == 0 && entry->fileinfo_lock_count == 0; +} + void phar_request_initialize(void); void phar_object_init(void); diff --git a/ext/phar/tar.c b/ext/phar/tar.c index c69c16ccd67ae..743349d397a39 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -730,7 +730,7 @@ static int phar_tar_writeheaders_int(phar_entry_info *entry, void *argument) /* } if (entry->is_deleted) { - if (entry->fp_refcount <= 0 && entry->fileinfo_lock_count == 0) { + if (phar_entry_can_remove(entry)) { return ZEND_HASH_APPLY_REMOVE; } else { /* we can't delete this in-memory until it is closed */ diff --git a/ext/phar/tests/gh21333.phpt b/ext/phar/tests/gh21333.phpt new file mode 100644 index 0000000000000..334986619c328 --- /dev/null +++ b/ext/phar/tests/gh21333.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-21333 (UAF when unlinking entries during iteration of a compressed phar) +--CREDITS-- +YuanchengJiang +--EXTENSIONS-- +phar +zlib +--INI-- +phar.readonly=0 +--FILE-- +addFromString("file", "initial_content"); +$phar->addEmptyDir("dir"); + +$phar2 = $phar->compress(Phar::GZ); + +$tmp_src = __DIR__ . "/gh21333.tmp"; +file_put_contents($tmp_src, str_repeat("A", 100)); + +foreach ($phar2 as $item) { + @copy($tmp_src, $item); + @unlink($item); +} + +$garbage = get_defined_vars(); + +echo "Done\n"; +?> +--CLEAN-- + +--EXPECT-- +Done diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 9439fe8b94d8a..838475841e125 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -865,7 +865,7 @@ static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ } if (entry->is_deleted) { - if (entry->fp_refcount <= 0 && entry->fileinfo_lock_count == 0) { + if (phar_entry_can_remove(entry)) { return ZEND_HASH_APPLY_REMOVE; } else { /* we can't delete this in-memory until it is closed */