Skip to content

Commit e735d2b

Browse files
committed
Fix GH-17808: PharFileInfo refcount bug
PharFileInfo just takes a pointer from the manifest without refcounting anything. If the entry is then removed from the manifest while the PharFileInfo object still exists, we get a UAF. We fix this by using the fp_refcount field. This is technically a behaviour change as the unlinking is now blocked, and potentially file modifications can be blocked as well. The alternative would be to have a field that indicates whether deletion is blocked, but similar corruption bugs may occur as well with file overwrites, so we increment fp_refcount instead. This also fixes an issue where a destructor called multiple times resulted in a UAF as well, by moving the NULL'ing of the entry field out of the if. Closes GH-17811.
1 parent 0f63bee commit e735d2b

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ PHP NEWS
3737
JIT crash). (nielsdos)
3838
. Fixed bug GH-17577 (JIT packed type guard crash). (nielsdos, Dmitry)
3939

40+
- Phar:
41+
. Fixed bug GH-17808: PharFileInfo refcount bug. (nielsdos)
42+
4043
- PHPDBG:
4144
. Partially fixed bug GH-17387 (Trivial crash in phpdbg lexer). (nielsdos)
4245
. Fix memory leak in phpdbg calling registered function. (nielsdos)

ext/phar/phar_object.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -4483,6 +4483,9 @@ PHP_METHOD(PharFileInfo, __construct)
44834483
efree(entry);
44844484

44854485
entry_obj->entry = entry_info;
4486+
if (!entry_info->is_persistent && !entry_info->is_temp_dir) {
4487+
++entry_info->fp_refcount;
4488+
}
44864489

44874490
ZVAL_STRINGL(&arg1, fname, fname_len);
44884491

@@ -4512,15 +4515,23 @@ PHP_METHOD(PharFileInfo, __destruct)
45124515
RETURN_THROWS();
45134516
}
45144517

4515-
if (entry_obj->entry && entry_obj->entry->is_temp_dir) {
4518+
if (!entry_obj->entry) {
4519+
return;
4520+
}
4521+
4522+
if (entry_obj->entry->is_temp_dir) {
45164523
if (entry_obj->entry->filename) {
45174524
efree(entry_obj->entry->filename);
45184525
entry_obj->entry->filename = NULL;
45194526
}
45204527

45214528
efree(entry_obj->entry);
4522-
entry_obj->entry = NULL;
4529+
} else if (!entry_obj->entry->is_persistent) {
4530+
--entry_obj->entry->fp_refcount;
4531+
/* It is necessarily still in the manifest, which will ultimately free this. */
45234532
}
4533+
4534+
entry_obj->entry = NULL;
45244535
}
45254536
/* }}} */
45264537

ext/phar/tests/gh17808.phpt

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
GH-17808 (PharFileInfo refcount bug)
3+
--EXTENSIONS--
4+
phar
5+
--FILE--
6+
<?php
7+
$fname = __DIR__.'/tar/files/Structures_Graph-1.0.3.tgz';
8+
$tar = new PharData($fname);
9+
foreach (new RecursiveIteratorIterator($tar) as $file) {
10+
}
11+
var_dump("$file");
12+
var_dump(strlen($file->getContent()));
13+
unlink("$file");
14+
var_dump($file->getATime());
15+
?>
16+
--EXPECTF--
17+
string(%d) "phar://%spackage.xml"
18+
int(6747)
19+
20+
Warning: unlink(): phar error: "package.xml" in phar %s, has open file pointers, cannot unlink in %s on line %d
21+
int(33188)

0 commit comments

Comments
 (0)