diff --git a/NEWS b/NEWS index cb16871317d..1da82f4baa4 100644 --- a/NEWS +++ b/NEWS @@ -60,6 +60,10 @@ PHP NEWS . Fixed bug GH-14290 (Member access within null pointer in extension spl). (nielsdos) +- Streams: + . Fixed bug GH-11078 (PHP Fatal error triggers pointer being freed was not + allocated and malloc: double free for ptr errors). (nielsdos) + 06 Jun 2024, PHP 8.3.8 - CGI: diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 7fc815c9d60..e4a8870f77e 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -750,6 +750,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); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index ffed975ecf5..abe51785612 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -250,6 +250,9 @@ function zend_test_override_libxml_global_state(): void {} #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 { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 2125f2852b8..66dbaccc681 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 4d9128205072443957f6229e09153a04c59cce74 */ + * Stub hash: 98329c979738792b2fc672d5b5c5a4b5dc0271f4 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -150,6 +150,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_set_fmode, 0, 1, IS_VO ZEND_END_ARG_INFO() #endif +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_cast_fread, 0, 1, IS_VOID, 0) + ZEND_ARG_INFO(0, stream) +ZEND_END_ARG_INFO() + #define arginfo_ZendTestNS2_namespaced_func arginfo_zend_test_is_pcre_bundled #define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_void_return @@ -275,6 +279,7 @@ static ZEND_FUNCTION(zend_test_is_pcre_bundled); #if defined(PHP_WIN32) static ZEND_FUNCTION(zend_test_set_fmode); #endif +static ZEND_FUNCTION(zend_test_cast_fread); static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); @@ -351,6 +356,7 @@ static const zend_function_entry ext_functions[] = { #if defined(PHP_WIN32) ZEND_FE(zend_test_set_fmode, arginfo_zend_test_set_fmode) #endif + ZEND_FE(zend_test_cast_fread, arginfo_zend_test_cast_fread) ZEND_NS_FALIAS("ZendTestNS2", namespaced_func, ZendTestNS2_namespaced_func, arginfo_ZendTestNS2_namespaced_func) ZEND_NS_DEP_FALIAS("ZendTestNS2", namespaced_deprecated_func, ZendTestNS2_namespaced_deprecated_func, arginfo_ZendTestNS2_namespaced_deprecated_func) ZEND_NS_FALIAS("ZendTestNS2", namespaced_aliased_func, zend_test_void_return, arginfo_ZendTestNS2_namespaced_aliased_func) diff --git a/ext/zend_test/tests/gh11078.phpt b/ext/zend_test/tests/gh11078.phpt new file mode 100644 index 00000000000..5279d703b27 --- /dev/null +++ b/ext/zend_test/tests/gh11078.phpt @@ -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-- + +--FILE-- + +--EXPECTF-- +Fatal error: Allowed memory size of %d bytes exhausted %s diff --git a/main/streams/cast.c b/main/streams/cast.c index 8d9f4a9d2d5..c4ab3d367c4 100644 --- a/main/streams/cast.c +++ b/main/streams/cast.c @@ -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