Commit d3c95d1745d for php.net
commit d3c95d1745de99b39034f8147f4659182143e6e9
Author: Gina Peter Banyard <girgias@php.net>
Date: Thu Jul 17 22:33:01 2025 +0100
ext/standard: Throw ValueError for filenames with null bytes
This should never happen in the first place
diff --git a/NEWS b/NEWS
index cb70bda237f..d90af7f5e64 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,13 @@ PHP NEWS
error handler instead of emitting a warning and continuing with an empty
string. (Weilin Du)
+- Standard:
+ . The following functions now raise a ValueError when the $filename argument
+ contains NUL bytes: fileperms(), fileinode(), filesize(), fileowner(),
+ filegroup(), fileatime(), filemtime(), filectime(), filetype(),
+ is_writable(), is_readable(), is_executable(), is_file(), is_dir(),
+ is_link(), file_exists(), lstat(), stat(). (Girgias)
+
30 Jul 2026, PHP 8.6.0alpha3
- Core:
diff --git a/UPGRADING b/UPGRADING
index 599ae2290d6..b516f24db97 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -228,6 +228,26 @@ PHP 8.6 UPGRADE NOTES
bytes.
. base_convert(), bindex(), hexdec() and octdec() now raise a notice when
they cannot precisely convert the given number.
+ . The following functions now raise a ValueError when the $filename argument
+ contains NUL bytes:
+ - fileperms()
+ - fileinode()
+ - filesize()
+ - fileowner()
+ - filegroup()
+ - fileatime()
+ - filemtime()
+ - filectime()
+ - filetype()
+ - is_writable()
+ - is_readable()
+ - is_executable()
+ - is_file()
+ - is_dir()
+ - is_link()
+ - file_exists()
+ - lstat()
+ - stat()
- Sysvshm:
. shm_attach() now raises a ValueError when the $key argument is outside the
diff --git a/ext/standard/filestat.c b/ext/standard/filestat.c
index 7dcfecf1f87..de4fcd82a62 100644
--- a/ext/standard/filestat.c
+++ b/ext/standard/filestat.c
@@ -996,7 +996,7 @@ ZEND_NAMED_FUNCTION(name) { \
zend_string *filename; \
\
ZEND_PARSE_PARAMETERS_START(1, 1) \
- Z_PARAM_STR(filename) \
+ Z_PARAM_PATH_STR(filename) \
ZEND_PARSE_PARAMETERS_END(); \
\
php_stat(filename, funcnum, return_value); \
diff --git a/ext/standard/tests/file/bug39863.phpt b/ext/standard/tests/file/bug39863.phpt
index 88c569473a7..2a0ca01ea65 100644
--- a/ext/standard/tests/file/bug39863.phpt
+++ b/ext/standard/tests/file/bug39863.phpt
@@ -6,7 +6,11 @@
<?php
$filename = __FILE__ . chr(0). ".ridiculous";
-var_dump(file_exists($filename));
+try {
+ var_dump(file_exists($filename));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+}
?>
--EXPECT--
-bool(false)
+ValueError: file_exists(): Argument #1 ($filename) must not contain any null bytes
diff --git a/ext/standard/tests/file/filegroup_null_byte.phpt b/ext/standard/tests/file/filegroup_null_byte.phpt
new file mode 100644
index 00000000000..4cb80399c61
--- /dev/null
+++ b/ext/standard/tests/file/filegroup_null_byte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+filegroup() with filenames with null bytes
+--FILE--
+<?php
+
+try {
+ var_dump(filegroup("file_with_null_byte.tmp\0"));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+ValueError: filegroup(): Argument #1 ($filename) must not contain any null bytes
diff --git a/ext/standard/tests/file/filegroup_variation3.phpt b/ext/standard/tests/file/filegroup_variation3.phpt
index e844be07721..94562850f58 100644
--- a/ext/standard/tests/file/filegroup_variation3.phpt
+++ b/ext/standard/tests/file/filegroup_variation3.phpt
@@ -26,10 +26,6 @@
"//filegroup_variation3//filegroup_variation3.tmp",
"/filegroup_variation3/*.tmp",
"filegroup_variation3/filegroup*.tmp",
-
- /* Testing Binary safe */
- "/filegroup_variation3/filegroup_variation3.tmp".chr(0),
- "/filegroup_variation3/filegroup_variation3.tmp\0"
);
$count = 1;
@@ -74,13 +70,5 @@
Warning: filegroup(): stat failed for %s/filegroup_variation3/filegroup*.tmp in %s on line %d
bool(false)
-- Iteration 7 -
-
-Warning: filegroup(): Filename contains null byte in %s on line %d
-bool(false)
-- Iteration 8 -
-
-Warning: filegroup(): Filename contains null byte in %s on line %d
-bool(false)
*** Done ***
diff --git a/ext/standard/tests/file/fileinode_null_byte.phpt b/ext/standard/tests/file/fileinode_null_byte.phpt
new file mode 100644
index 00000000000..48b8272d97c
--- /dev/null
+++ b/ext/standard/tests/file/fileinode_null_byte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+fileinode() with filenames with null bytes
+--FILE--
+<?php
+
+try {
+ var_dump(fileinode("file_with_null_byte.tmp\0"));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+ValueError: fileinode(): Argument #1 ($filename) must not contain any null bytes
diff --git a/ext/standard/tests/file/fileinode_variation3.phpt b/ext/standard/tests/file/fileinode_variation3.phpt
index 92357a3619e..506984d4177 100644
--- a/ext/standard/tests/file/fileinode_variation3.phpt
+++ b/ext/standard/tests/file/fileinode_variation3.phpt
@@ -25,10 +25,6 @@
"//fileinode_variation3//fileinode_variation3.tmp",
"/fileinode_variation3/*.tmp",
"fileinode_variation3/fileinode*.tmp",
-
- /* Testing Binary safe */
- "/fileinode_variation3/fileinode_variation3.tmp".chr(0),
- "/fileinode_variation3/fileinode_variation3.tmp\0"
);
$count = 1;
@@ -73,13 +69,5 @@
Warning: fileinode(): stat failed for %s/fileinode_variation3/fileinode*.tmp in %s on line %d
bool(false)
-- Iteration 7 -
-
-Warning: fileinode(): Filename contains null byte in %s on line %d
-bool(false)
-- Iteration 8 -
-
-Warning: fileinode(): Filename contains null byte in %s on line %d
-bool(false)
*** Done ***
diff --git a/ext/standard/tests/file/fileowner_null_byte.phpt b/ext/standard/tests/file/fileowner_null_byte.phpt
new file mode 100644
index 00000000000..909ac0fe310
--- /dev/null
+++ b/ext/standard/tests/file/fileowner_null_byte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+fileowner() with filenames with null bytes
+--FILE--
+<?php
+
+try {
+ var_dump(fileowner("file_with_null_byte.tmp\0"));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+ValueError: fileowner(): Argument #1 ($filename) must not contain any null bytes
diff --git a/ext/standard/tests/file/fileowner_variation3.phpt b/ext/standard/tests/file/fileowner_variation3.phpt
index 63ba6936aaa..e6360b47571 100644
--- a/ext/standard/tests/file/fileowner_variation3.phpt
+++ b/ext/standard/tests/file/fileowner_variation3.phpt
@@ -26,10 +26,6 @@
"//fileowner_variation3//fileowner_variation3.tmp",
"/fileowner_variation3/*.tmp",
"fileowner_variation3/fileowner*.tmp",
-
- /* Testing Binary safe */
- "/fileowner_variation3/fileowner_variation3.tmp".chr(0),
- "/fileowner_variation3/fileowner_variation3.tmp\0"
);
$count = 1;
@@ -74,13 +70,5 @@
Warning: fileowner(): stat failed for %s/fileowner_variation3/fileowner*.tmp in %s on line %d
bool(false)
-- Iteration 7 -
-
-Warning: fileowner(): Filename contains null byte in %s on line %d
-bool(false)
-- Iteration 8 -
-
-Warning: fileowner(): Filename contains null byte in %s on line %d
-bool(false)
*** Done ***
diff --git a/ext/standard/tests/file/fileperms_null_byte.phpt b/ext/standard/tests/file/fileperms_null_byte.phpt
new file mode 100644
index 00000000000..1530d0e5bf9
--- /dev/null
+++ b/ext/standard/tests/file/fileperms_null_byte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+fileperms() with filenames with null bytes
+--FILE--
+<?php
+
+try {
+ var_dump(fileperms("file_with_null_byte.tmp\0"));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+ValueError: fileperms(): Argument #1 ($filename) must not contain any null bytes
diff --git a/ext/standard/tests/file/fileperms_variation3.phpt b/ext/standard/tests/file/fileperms_variation3.phpt
index 5e981e9b863..6c1da17d6bf 100644
--- a/ext/standard/tests/file/fileperms_variation3.phpt
+++ b/ext/standard/tests/file/fileperms_variation3.phpt
@@ -25,10 +25,6 @@
"//fileperms_variation3//fileperms_variation3.tmp",
"/fileperms_variation3/*.tmp",
"fileperms_variation3/fileperms*.tmp",
-
- /* Testing Binary safe */
- "/fileperms_variation3/fileperms_variation3.tmp".chr(0),
- "/fileperms_variation3/fileperms_variation3.tmp\0"
);
$count = 1;
@@ -73,13 +69,5 @@
Warning: fileperms(): stat failed for %s/fileperms_variation3/fileperms*.tmp in %s on line %d
bool(false)
-- Iteration 7 -
-
-Warning: fileperms(): Filename contains null byte in %s on line %d
-bool(false)
-- Iteration 8 -
-
-Warning: fileperms(): Filename contains null byte in %s on line %d
-bool(false)
*** Done ***
diff --git a/ext/standard/tests/file/is_dir_null_byte.phpt b/ext/standard/tests/file/is_dir_null_byte.phpt
new file mode 100644
index 00000000000..a49ccc0e5c4
--- /dev/null
+++ b/ext/standard/tests/file/is_dir_null_byte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+is_dir() with filenames with null bytes
+--FILE--
+<?php
+
+try {
+ var_dump(is_dir("file_with_null_byte.tmp\0"));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+ValueError: is_dir(): Argument #1 ($filename) must not contain any null bytes
diff --git a/ext/standard/tests/file/is_dir_variation4.phpt b/ext/standard/tests/file/is_dir_variation4.phpt
index d6e64efe211..30285804a33 100644
--- a/ext/standard/tests/file/is_dir_variation4.phpt
+++ b/ext/standard/tests/file/is_dir_variation4.phpt
@@ -23,10 +23,6 @@
"./is_dir_variation4//",
".//is_dir_variation4//",
"is_dir_vari*",
-
- /* Testing Binary safe */
- "./is_dir_variation4/".chr(0),
- "is_dir_variation4\0"
);
$count = 1;
@@ -75,10 +71,4 @@
-- Iteration 8 --
bool(false)
--- Iteration 9 --
-bool(false)
-
--- Iteration 10 --
-bool(false)
-
*** Done ***
diff --git a/ext/standard/tests/file/is_executable_null_byte.phpt b/ext/standard/tests/file/is_executable_null_byte.phpt
new file mode 100644
index 00000000000..7c26d53061d
--- /dev/null
+++ b/ext/standard/tests/file/is_executable_null_byte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+is_executable() with filenames with null bytes
+--FILE--
+<?php
+
+try {
+ var_dump(is_executable("file_with_null_byte.tmp\0"));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+ValueError: is_executable(): Argument #1 ($filename) must not contain any null bytes
diff --git a/ext/standard/tests/file/is_executable_variation1.phpt b/ext/standard/tests/file/is_executable_variation1.phpt
index a85b9a2ef02..289abb1a265 100644
--- a/ext/standard/tests/file/is_executable_variation1.phpt
+++ b/ext/standard/tests/file/is_executable_variation1.phpt
@@ -33,11 +33,6 @@
"$file_path/is_executable_variation1/*.tmp",
"$file_path/is_executable_variation1/b*.tmp",
- /* Testing Binary safe */
- "$file_path/is_executable_variation1".chr(0)."bar.temp",
- "$file_path".chr(0)."is_executable_variation1/bar.temp",
- "$file_path/is_executable_variation1x000/",
-
/* Testing directories */
".", // current directory, exp: bool(true)
"$file_path/is_executable_variation1" // temp directory, exp: bool(true)
@@ -76,13 +71,7 @@
-- Iteration 5 --
bool(false)
-- Iteration 6 --
-bool(false)
--- Iteration 7 --
-bool(false)
--- Iteration 8 --
-bool(false)
--- Iteration 9 --
bool(true)
--- Iteration 10 --
+-- Iteration 7 --
bool(true)
Done
diff --git a/ext/standard/tests/file/is_file_null_byte.phpt b/ext/standard/tests/file/is_file_null_byte.phpt
new file mode 100644
index 00000000000..f544d7bcf58
--- /dev/null
+++ b/ext/standard/tests/file/is_file_null_byte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+is_file() with filenames with null bytes
+--FILE--
+<?php
+
+try {
+ var_dump(is_file("file_with_null_byte.tmp\0"));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+ValueError: is_file(): Argument #1 ($filename) must not contain any null bytes
diff --git a/ext/standard/tests/file/is_file_variation4.phpt b/ext/standard/tests/file/is_file_variation4.phpt
index 1afc34dd03b..9d98b7fd90d 100644
--- a/ext/standard/tests/file/is_file_variation4.phpt
+++ b/ext/standard/tests/file/is_file_variation4.phpt
@@ -23,10 +23,6 @@
"//is_file_variation4//is_file_variation4.tmp",
"/is_file_variation4/*.tmp",
"is_file_variation4/is_file*.tmp",
-
- /* Testing Binary safe */
- "/is_file_variation4/is_file_variation4.tmp".chr(0),
- "/is_file_variation4/is_file_variation4.tmp\0"
);
$count = 1;
@@ -65,9 +61,5 @@
bool(false)
- Iteration 6 -
bool(false)
-- Iteration 7 -
-bool(false)
-- Iteration 8 -
-bool(false)
*** Done ***
diff --git a/ext/standard/tests/file/is_readable_null_byte.phpt b/ext/standard/tests/file/is_readable_null_byte.phpt
new file mode 100644
index 00000000000..711700f4e7e
--- /dev/null
+++ b/ext/standard/tests/file/is_readable_null_byte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+is_readable() with filenames with null bytes
+--FILE--
+<?php
+
+try {
+ var_dump(is_readable("file_with_null_byte.tmp\0"));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+ValueError: is_readable(): Argument #1 ($filename) must not contain any null bytes
diff --git a/ext/standard/tests/file/is_readable_variation1.phpt b/ext/standard/tests/file/is_readable_variation1.phpt
index 9c25213f8ab..8548a840ccb 100644
--- a/ext/standard/tests/file/is_readable_variation1.phpt
+++ b/ext/standard/tests/file/is_readable_variation1.phpt
@@ -32,11 +32,6 @@
"$file_path/is_readable_variation1/*.tmp",
"$file_path/is_readable_variation1/b*.tmp",
- /* Testing Binary safe */
- "$file_path/is_readable_variation1".chr(0)."bar.tmp",
- "$file_path".chr(0)."is_readable_variation1/bar.tmp",
- "$file_path".chr(0)."is_readable_variation1/bar.tmp",
-
/* Testing directories */
".", // current directory, exp: bool(true)
"$file_path/is_readable_variation1" // temp directory, exp: bool(true)
@@ -77,13 +72,7 @@
-- Iteration 6 --
bool(false)
-- Iteration 7 --
-bool(false)
--- Iteration 8 --
-bool(false)
--- Iteration 9 --
-bool(false)
--- Iteration 10 --
bool(true)
--- Iteration 11 --
+-- Iteration 8 --
bool(true)
Done
diff --git a/ext/standard/tests/file/is_writable_null_byte.phpt b/ext/standard/tests/file/is_writable_null_byte.phpt
new file mode 100644
index 00000000000..319db04cc15
--- /dev/null
+++ b/ext/standard/tests/file/is_writable_null_byte.phpt
@@ -0,0 +1,14 @@
+--TEST--
+is_writable() with filenames with null bytes
+--FILE--
+<?php
+
+try {
+ var_dump(is_writable("file_with_null_byte.tmp\0"));
+} catch (Throwable $e) {
+ echo $e::class, ': ', $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+ValueError: is_writable(): Argument #1 ($filename) must not contain any null bytes
diff --git a/ext/standard/tests/file/is_writable_variation1.phpt b/ext/standard/tests/file/is_writable_variation1.phpt
index 80695d6d45a..181b1ba4e26 100644
--- a/ext/standard/tests/file/is_writable_variation1.phpt
+++ b/ext/standard/tests/file/is_writable_variation1.phpt
@@ -31,11 +31,6 @@
"$file_path/is_writable_variation1/*.tmp",
"$file_path/is_writable_variation1/b*.tmp",
- /* Testing Binary safe */
- "$file_path/is_writable_variation1".chr(0)."bar.tmp",
- "$file_path".chr(0)."is_writable_variation1/bar.tmp",
- "$file_path".chr(0)."is_writable_variation1/bar.tmp",
-
/* Testing directories */
".", // current directory, exp: bool(true)
"$file_path/is_writable_variation1" // temp directory, exp: bool(true)
@@ -87,18 +82,9 @@
bool(false)
bool(false)
-- Iteration 7 --
-bool(false)
-bool(false)
--- Iteration 8 --
-bool(false)
-bool(false)
--- Iteration 9 --
-bool(false)
-bool(false)
--- Iteration 10 --
bool(true)
bool(true)
--- Iteration 11 --
+-- Iteration 8 --
bool(true)
bool(true)
Done