From c9923a63334e6537765924622ebc8b350da90760 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 31 Aug 2025 09:29:46 +0200 Subject: [PATCH 1/2] Remove include "sanity check" to get better error Instead of rejecting directories / non-regular files early with a generic error, we should just accept them and error later when a read is attempted. This is more general and will generate a better error message on Linux. On Windows, the error remains the same as before. --- Zend/tests/require_directory.phpt | 21 ++++++++++++++++++ Zend/tests/require_directory_windows.phpt | 21 ++++++++++++++++++ ext/standard/tests/file/bug35740.phpt | 6 +++++- main/streams/plain_wrapper.c | 26 +---------------------- 4 files changed, 48 insertions(+), 26 deletions(-) create mode 100644 Zend/tests/require_directory.phpt create mode 100644 Zend/tests/require_directory_windows.phpt diff --git a/Zend/tests/require_directory.phpt b/Zend/tests/require_directory.phpt new file mode 100644 index 0000000000000..c8b4b6a6382cb --- /dev/null +++ b/Zend/tests/require_directory.phpt @@ -0,0 +1,21 @@ +--TEST-- +Including a directory generates an error +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Notice: require(): Read of %d bytes failed with errno=21 Is a directory in %s on line %d + +Fatal error: Uncaught Error: Failed opening required '%s' (include_path='.:') in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/require_directory_windows.phpt b/Zend/tests/require_directory_windows.phpt new file mode 100644 index 0000000000000..221d418ff568a --- /dev/null +++ b/Zend/tests/require_directory_windows.phpt @@ -0,0 +1,21 @@ +--TEST-- +Including a directory generates an error (Windows variant) +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: require(%s): Failed to open stream: Permission denied in %s on line %d + +Fatal error: Uncaught Error: Failed opening required '%s' (include_path='.:') in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/ext/standard/tests/file/bug35740.phpt b/ext/standard/tests/file/bug35740.phpt index 29ab0248d8dcf..d06b53be06ed8 100644 --- a/ext/standard/tests/file/bug35740.phpt +++ b/ext/standard/tests/file/bug35740.phpt @@ -1,5 +1,9 @@ --TEST-- Bug #35740 (memory leak when including a directory) +--SKIPIF-- + --FILE-- --EXPECTF-- -Warning: include(%s): Failed to open stream: %s in %s on line %d +Notice: include(): Read of %s bytes failed with errno=21 Is a directory in %s on line %d Warning: include(): Failed opening '%s' for inclusion (include_path='%s') in %s on line %d Done diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index a8fd70b8d6ac2..a0471a8b9218c 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -135,7 +135,6 @@ typedef struct { unsigned is_pipe:1; /* stream is an actual pipe, currently Windows only*/ unsigned cached_fstat:1; /* sb is valid */ unsigned is_pipe_blocking:1; /* allow blocking read() on pipes, currently Windows only */ - unsigned no_forced_fstat:1; /* Use fstat cache even if forced */ unsigned is_seekable:1; /* don't try and seek, if not set */ unsigned _reserved:26; @@ -161,7 +160,7 @@ typedef struct { static int do_fstat(php_stdio_stream_data *d, int force) { - if (!d->cached_fstat || (force && !d->no_forced_fstat)) { + if (!d->cached_fstat || force) { int fd; int r; @@ -1188,30 +1187,7 @@ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zen efree(persistent_id); } - /* WIN32 always set ISREG flag */ #ifndef PHP_WIN32 - /* sanity checks for include/require. - * We check these after opening the stream, so that we save - * on fstat() syscalls */ - if (options & STREAM_OPEN_FOR_INCLUDE) { - php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract; - int r; - - r = do_fstat(self, 0); - if ((r == 0 && !S_ISREG(self->sb.st_mode))) { - if (opened_path) { - zend_string_release_ex(*opened_path, 0); - *opened_path = NULL; - } - php_stream_close(ret); - return NULL; - } - - /* Make sure the fstat result is reused when we later try to get the - * file size. */ - self->no_forced_fstat = 1; - } - if (options & STREAM_USE_BLOCKING_PIPE) { php_stdio_stream_data *self = (php_stdio_stream_data*)ret->abstract; self->is_pipe_blocking = 1; From 04f56188d39dae73923eac619c8576f96005ca01 Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Sun, 31 Aug 2025 11:39:05 +0200 Subject: [PATCH 2/2] Update error message to include include_path fix format for include path --- Zend/tests/require_directory_windows.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/tests/require_directory_windows.phpt b/Zend/tests/require_directory_windows.phpt index 221d418ff568a..d8b68c2064e57 100644 --- a/Zend/tests/require_directory_windows.phpt +++ b/Zend/tests/require_directory_windows.phpt @@ -15,7 +15,7 @@ require $dir; --EXPECTF-- Warning: require(%s): Failed to open stream: Permission denied in %s on line %d -Fatal error: Uncaught Error: Failed opening required '%s' (include_path='.:') in %s:%d +Fatal error: Uncaught Error: Failed opening required '%s' (include_path='%s') in %s:%d Stack trace: #0 {main} thrown in %s on line %d