Skip to content

Commit 631bf8a

Browse files
authored
Fix GH-22200: stream api memory leaks. (#22204)
Report the stream errors before popping the operation off the stack, so a reentrant error handler that runs another stream operation no longer reuses the in-flight operation pool slot and orphans its error entry. While at it, free the docref in php_stream_error_entry_free(), matching the legacy list destructor.
1 parent 21c2318 commit 631bf8a

2 files changed

Lines changed: 26 additions & 3 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
GH-22200 (Memory leaks in the stream errors code on reentrant error handler)
3+
--FILE--
4+
<?php
5+
6+
// When the warning emitted for the failing outer stream operation is reported,
7+
// php_stream_error_operation_end() has already popped the operation off the
8+
// stack. The user error handler then runs another stream operation, which
9+
// reuses the same operation pool slot and orphans the in-flight error entry,
10+
// leaking it.
11+
set_error_handler(function ($errno, $errstr) {
12+
@file_get_contents(__DIR__ . '/gh22200-does-not-exist-inner');
13+
return true;
14+
});
15+
16+
file_get_contents(__DIR__ . '/gh22200-does-not-exist-outer');
17+
18+
echo "done\n";
19+
20+
?>
21+
--EXPECT--
22+
done

main/streams/stream_errors.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ static void php_stream_error_entry_free(php_stream_error_entry *entry)
215215
zend_string_release(entry->message);
216216
efree(entry->wrapper_name);
217217
efree(entry->param);
218+
efree(entry->docref);
218219
efree(entry);
219220
entry = next;
220221
}
@@ -449,9 +450,6 @@ PHPAPI void php_stream_error_operation_end(php_stream_context *context)
449450
return;
450451
}
451452

452-
state->operation_depth--;
453-
state->current_operation = php_stream_get_parent_operation();
454-
455453
if (op->error_count > 0) {
456454
if (context == NULL) {
457455
context = FG(default_context);
@@ -527,6 +525,9 @@ PHPAPI void php_stream_error_operation_end(php_stream_context *context)
527525
}
528526
}
529527

528+
state->operation_depth--;
529+
state->current_operation = php_stream_get_parent_operation();
530+
530531
op->first_error = NULL;
531532
op->last_error = NULL;
532533
op->error_count = 0;

0 commit comments

Comments
 (0)