Skip to content

Fix GH-11078: PHP Fatal error triggers pointer being freed was not allocated and malloc: double free for ptr errors #14524

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,38 @@ static ZEND_FUNCTION(zend_test_set_fmode)
}
#endif

static ZEND_FUNCTION(zend_test_cast_fread)
{
zval *stream_zv;
php_stream *stream;
FILE *fp;

ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_RESOURCE(stream_zv);
ZEND_PARSE_PARAMETERS_END();

php_stream_from_zval(stream, stream_zv);

if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS) == FAILURE) {
return;
}

size_t size = 10240; /* Must be large enough to trigger the issue */
char *buf = malloc(size);
bool bail = false;
zend_try {
(void) !fread(buf, 1, size, fp);
} zend_catch {
bail = true;
} zend_end_try();

free(buf);

if (bail) {
zend_bailout();
}
}

static zend_object *zend_test_class_new(zend_class_entry *class_type)
{
zend_object *obj = zend_objects_new(class_type);
Expand Down
3 changes: 3 additions & 0 deletions ext/zend_test/test.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ function zend_test_is_pcre_bundled(): bool {}
#if defined(PHP_WIN32)
function zend_test_set_fmode(bool $binary): void {}
#endif

/** @param resource $stream */
function zend_test_cast_fread($stream): void {}
}

namespace ZendTestNS {
Expand Down
8 changes: 7 additions & 1 deletion ext/zend_test/test_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions ext/zend_test/tests/gh11078.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
GH-11078 (PHP Fatal error triggers pointer being freed was not allocated and malloc: double free for ptr errors)
--EXTENSIONS--
zend_test
--SKIPIF--
<?php
if (getenv('USE_ZEND_ALLOC') === '0') die('skip Zend MM disabled');
if (PHP_OS_FAMILY === 'Windows') die('skip Windows does not support generic stream casting');
?>
--FILE--
<?php

const MEM = 32 * 1024 * 1024;
ini_set('memory_limit', MEM);

class CrashingFifo {
public $context;

function stream_open($path, $mode, $options, &$opened_path): bool {
return true;
}

function stream_read(int $count): false|string|null {
return str_repeat('x', MEM);
}
}

stream_register_wrapper('fifo', CrashingFifo::class);
$readStream = fopen('fifo://1', 'r');
zend_test_cast_fread($readStream);

?>
--EXPECTF--
Fatal error: Allowed memory size of %d bytes exhausted %s
9 changes: 8 additions & 1 deletion main/streams/cast.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,14 @@ typedef struct {

FILE *fopencookie(void *cookie, const char *mode, COOKIE_IO_FUNCTIONS_T *funcs)
{
return funopen(cookie, funcs->reader, funcs->writer, funcs->seeker, funcs->closer);
FILE *file = funopen(cookie, funcs->reader, funcs->writer, funcs->seeker, funcs->closer);
if (file) {
/* Buffering of FILE handles is stateful.
* A bailout during these can corrupt the state of the FILE handle
* and cause memory corruption errors. See GH-11078. */
setvbuf(file, NULL, _IONBF, 0);
}
return file;
}
# define HAVE_FOPENCOOKIE 1
# define PHP_EMULATE_FOPENCOOKIE 1
Expand Down
Loading