[php-src] master: Guard uninitialized SplFileObject in fputcsv() and next()

Ilia Alshanetsky <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Date: 2026-06-25T18:14:24-04:00

Commit: https://github.com/php/php-src/commit/60d8e92ed4a0c1b310a81103f3b423713aece6c8
Raw diff: https://github.com/php/php-src/commit/60d8e92ed4a0c1b310a81103f3b423713aece6c8.diff

Guard uninitialized SplFileObject in fputcsv() and next()

fputcsv() and next() reached the file stream without the
CHECK_SPL_FILE_OBJECT_IS_INITIALIZED guard their siblings carry, so
invoking them via reflection on an object from
newInstanceWithoutConstructor() (constructor bypassed, stream NULL)
crashed instead of throwing. next() only touches the stream with the
READ_AHEAD flag, itself settable through the equally unguarded
setFlags(). Both now throw "Object not initialized".

Fixes GH-16217
Closes GH-22454

Changed paths:
  A  ext/spl/tests/gh16217.phpt
  M  NEWS
  M  ext/spl/spl_directory.c


Diff:

diff --git a/NEWS b/NEWS
index 7dd1aea4b881..39f2359152f0 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,8 @@ PHP                                                                        NEWS
     (jorgsowa)
   . Ignore leading back-slash in class_parents(), class_implements(), and
     class_uses(). (jorgsowa)
+  . Fixed bug GH-16217 (SplFileObject::fputcsv() on an uninitialized object
+    segfaults). (iliaal)
 
 - Standard:
   . Fixed bug GH-22360 (convert.base64-encode corruption on
diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c
index f83ea9f84aca..eb3fb8caef64 100644
--- a/ext/spl/spl_directory.c
+++ b/ext/spl/spl_directory.c
@@ -2224,6 +2224,8 @@ PHP_METHOD(SplFileObject, next)
 		RETURN_THROWS();
 	}
 
+	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
+
 	spl_filesystem_file_free_line(intern);
 	if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) {
 		spl_filesystem_file_read_line(ZEND_THIS, intern, true);
@@ -2376,6 +2378,8 @@ PHP_METHOD(SplFileObject, fputcsv)
 		RETURN_THROWS();
 	}
 
+	CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
+
 	if (delim) {
 		if (d_len != 1) {
 			zend_argument_value_error(2, "must be a single character");
diff --git a/ext/spl/tests/gh16217.phpt b/ext/spl/tests/gh16217.phpt
new file mode 100644
index 000000000000..71760389c8e4
--- /dev/null
+++ b/ext/spl/tests/gh16217.phpt
@@ -0,0 +1,35 @@
+--TEST--
+GH-16217 (SplFileObject methods on an uninitialized object segfault)
+--FILE--
+<?php
+function uninitialized(): SplFileObject {
+    return (new ReflectionClass(SplFileObject::class))->newInstanceWithoutConstructor();
+}
+
+try {
+    (new ReflectionMethod(SplFileObject::class, "fputcsv"))->invoke(uninitialized(), []);
+} catch (Error $e) {
+    echo "fputcsv: ", $e->getMessage(), "\n";
+}
+
+try {
+    (new ReflectionMethod(SplFileObject::class, "next"))->invoke(uninitialized());
+} catch (Error $e) {
+    echo "next: ", $e->getMessage(), "\n";
+}
+
+$obj = uninitialized();
+(new ReflectionMethod(SplFileObject::class, "setFlags"))->invoke($obj, SplFileObject::READ_AHEAD);
+try {
+    (new ReflectionMethod(SplFileObject::class, "next"))->invoke($obj);
+} catch (Error $e) {
+    echo "next (READ_AHEAD): ", $e->getMessage(), "\n";
+}
+
+echo "Done\n";
+?>
+--EXPECT--
+fputcsv: Object not initialized
+next: Object not initialized
+next (READ_AHEAD): Object not initialized
+Done
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.