Skip to content

Commit aa34e0a

Browse files
committed
Fix GH-13685: Unexpected null pointer in zend_string.h
Regressed in 6fbf81c. There is a missing error check on spl_filesystem_file_read_line(), which means that if the line could not be read (e.g. because we're at the end of the file), it will not set intern->u.file.current_line, which will cause a NULL pointer deref later on. Fix it by adding a check, and reintroducing the silent flag partially to be able to throw an exception like it did in the past. Closes GH-13692.
1 parent afdabb1 commit aa34e0a

File tree

3 files changed

+81
-18
lines changed

3 files changed

+81
-18
lines changed

NEWS

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ PHP NEWS
4141
- SPL:
4242
. Fixed bug GH-13531 (Unable to resize SplfixedArray after being unserialized
4343
in PHP 8.2.15). (nielsdos)
44+
. Fixed bug GH-13685 (Unexpected null pointer in zend_string.h). (nielsdos)
4445

4546
- Standard:
4647
. Fixed bug GH-11808 (Live filesystem modified by tests). (nielsdos)

ext/spl/spl_directory.c

+28-18
Original file line numberDiff line numberDiff line change
@@ -1866,6 +1866,11 @@ zend_object_iterator *spl_filesystem_tree_get_iterator(zend_class_entry *ce, zva
18661866
}
18671867
/* }}} */
18681868

1869+
static ZEND_COLD void spl_filesystem_file_cannot_read(spl_filesystem_object *intern)
1870+
{
1871+
zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot read from file %s", ZSTR_VAL(intern->file_name));
1872+
}
1873+
18691874
static zend_result spl_filesystem_file_read_ex(spl_filesystem_object *intern, bool silent, zend_long line_add, bool csv)
18701875
{
18711876
char *buf;
@@ -1875,7 +1880,7 @@ static zend_result spl_filesystem_file_read_ex(spl_filesystem_object *intern, bo
18751880

18761881
if (php_stream_eof(intern->u.file.stream)) {
18771882
if (!silent) {
1878-
zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot read from file %s", ZSTR_VAL(intern->file_name));
1883+
spl_filesystem_file_cannot_read(intern);
18791884
}
18801885
return FAILURE;
18811886
}
@@ -1930,10 +1935,10 @@ static bool is_line_empty(spl_filesystem_object *intern)
19301935
|| (current_line_len == 2 && current_line[0] == '\r' && current_line[1] == '\n'))));
19311936
}
19321937

1933-
static zend_result spl_filesystem_file_read_csv(spl_filesystem_object *intern, char delimiter, char enclosure, int escape, zval *return_value) /* {{{ */
1938+
static zend_result spl_filesystem_file_read_csv(spl_filesystem_object *intern, char delimiter, char enclosure, int escape, zval *return_value, bool silent) /* {{{ */
19341939
{
19351940
do {
1936-
zend_result ret = spl_filesystem_file_read(intern, /* silent */ true, /* csv */ true);
1941+
zend_result ret = spl_filesystem_file_read(intern, silent, /* csv */ true);
19371942
if (ret != SUCCESS) {
19381943
return ret;
19391944
}
@@ -1959,19 +1964,21 @@ static zend_result spl_filesystem_file_read_csv(spl_filesystem_object *intern, c
19591964
}
19601965
/* }}} */
19611966

1962-
/* Call to this function reads a line in a "silent" fashion and does not throw an exception */
1963-
static zend_result spl_filesystem_file_read_line_ex(zval * this_ptr, spl_filesystem_object *intern) /* {{{ */
1967+
static zend_result spl_filesystem_file_read_line_ex(zval * this_ptr, spl_filesystem_object *intern, bool silent) /* {{{ */
19641968
{
19651969
zval retval;
19661970

19671971
/* 1) use fgetcsv? 2) overloaded call the function, 3) do it directly */
19681972
if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV)) {
1969-
return spl_filesystem_file_read_csv(intern, intern->u.file.delimiter, intern->u.file.enclosure, intern->u.file.escape, NULL);
1973+
return spl_filesystem_file_read_csv(intern, intern->u.file.delimiter, intern->u.file.enclosure, intern->u.file.escape, NULL, silent);
19701974
}
19711975
if (intern->u.file.func_getCurr->common.scope != spl_ce_SplFileObject) {
19721976
spl_filesystem_file_free_line(intern);
19731977

19741978
if (php_stream_eof(intern->u.file.stream)) {
1979+
if (!silent) {
1980+
spl_filesystem_file_cannot_read(intern);
1981+
}
19751982
return FAILURE;
19761983
}
19771984
zend_call_method_with_0_params(Z_OBJ_P(this_ptr), Z_OBJCE_P(this_ptr), &intern->u.file.func_getCurr, "getCurrentLine", &retval);
@@ -1995,18 +2002,17 @@ static zend_result spl_filesystem_file_read_line_ex(zval * this_ptr, spl_filesys
19952002
zval_ptr_dtor(&retval);
19962003
return SUCCESS;
19972004
} else {
1998-
return spl_filesystem_file_read(intern, /* silent */ true, /* csv */ false);
2005+
return spl_filesystem_file_read(intern, silent, /* csv */ false);
19992006
}
20002007
} /* }}} */
20012008

2002-
/* Call to this function reads a line in a "silent" fashion and does not throw an exception */
2003-
static zend_result spl_filesystem_file_read_line(zval * this_ptr, spl_filesystem_object *intern) /* {{{ */
2009+
static zend_result spl_filesystem_file_read_line(zval * this_ptr, spl_filesystem_object *intern, bool silent) /* {{{ */
20042010
{
2005-
zend_result ret = spl_filesystem_file_read_line_ex(this_ptr, intern);
2011+
zend_result ret = spl_filesystem_file_read_line_ex(this_ptr, intern, silent);
20062012

20072013
while (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_SKIP_EMPTY) && ret == SUCCESS && is_line_empty(intern)) {
20082014
spl_filesystem_file_free_line(intern);
2009-
ret = spl_filesystem_file_read_line_ex(this_ptr, intern);
2015+
ret = spl_filesystem_file_read_line_ex(this_ptr, intern, silent);
20102016
}
20112017

20122018
return ret;
@@ -2028,7 +2034,7 @@ static void spl_filesystem_file_rewind(zval * this_ptr, spl_filesystem_object *i
20282034
intern->u.file.current_line_num = 0;
20292035

20302036
if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) {
2031-
spl_filesystem_file_read_line(this_ptr, intern);
2037+
spl_filesystem_file_read_line(this_ptr, intern, true);
20322038
}
20332039
} /* }}} */
20342040

@@ -2182,7 +2188,7 @@ PHP_METHOD(SplFileObject, current)
21822188
CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
21832189

21842190
if (!intern->u.file.current_line && Z_ISUNDEF(intern->u.file.current_zval)) {
2185-
spl_filesystem_file_read_line(ZEND_THIS, intern);
2191+
spl_filesystem_file_read_line(ZEND_THIS, intern, true);
21862192
}
21872193
if (intern->u.file.current_line && (!SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV) || Z_ISUNDEF(intern->u.file.current_zval))) {
21882194
RETURN_STRINGL(intern->u.file.current_line, intern->u.file.current_line_len);
@@ -2221,7 +2227,7 @@ PHP_METHOD(SplFileObject, next)
22212227

22222228
spl_filesystem_file_free_line(intern);
22232229
if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) {
2224-
spl_filesystem_file_read_line(ZEND_THIS, intern);
2230+
spl_filesystem_file_read_line(ZEND_THIS, intern, true);
22252231
}
22262232
intern->u.file.current_line_num++;
22272233
} /* }}} */
@@ -2339,7 +2345,7 @@ PHP_METHOD(SplFileObject, fgetcsv)
23392345
}
23402346
}
23412347

2342-
if (spl_filesystem_file_read_csv(intern, delimiter, enclosure, escape, return_value) == FAILURE) {
2348+
if (spl_filesystem_file_read_csv(intern, delimiter, enclosure, escape, return_value, true) == FAILURE) {
23432349
RETURN_FALSE;
23442350
}
23452351
}
@@ -2720,7 +2726,7 @@ PHP_METHOD(SplFileObject, seek)
27202726
spl_filesystem_file_rewind(ZEND_THIS, intern);
27212727

27222728
for (i = 0; i < line_pos; i++) {
2723-
if (spl_filesystem_file_read_line(ZEND_THIS, intern) == FAILURE) {
2729+
if (spl_filesystem_file_read_line(ZEND_THIS, intern, true) == FAILURE) {
27242730
return;
27252731
}
27262732
}
@@ -2740,8 +2746,12 @@ PHP_METHOD(SplFileObject, __toString)
27402746

27412747
CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern);
27422748

2743-
if (!intern->u.file.current_line && Z_ISUNDEF(intern->u.file.current_zval)) {
2744-
spl_filesystem_file_read_line(ZEND_THIS, intern);
2749+
if (!intern->u.file.current_line) {
2750+
ZEND_ASSERT(Z_ISUNDEF(intern->u.file.current_zval));
2751+
zend_result result = spl_filesystem_file_read_line(ZEND_THIS, intern, false);
2752+
if (UNEXPECTED(result != SUCCESS)) {
2753+
RETURN_THROWS();
2754+
}
27452755
}
27462756

27472757
RETURN_STRINGL(intern->u.file.current_line, intern->u.file.current_line_len);

ext/spl/tests/gh13685.phpt

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
--TEST--
2+
GH-13685 (Unexpected null pointer in zend_string.h)
3+
--FILE--
4+
<?php
5+
6+
$contents = <<<EOS
7+
"A", "B", "C"
8+
"D", "E", "F"
9+
EOS;
10+
11+
echo "--- Directly call fgetcsv ---\n";
12+
13+
$file = new SplTempFileObject;
14+
$file->fwrite($contents);
15+
$file->rewind();
16+
while (($data = $file->fgetcsv(',', '"', ''))) {
17+
var_dump((string) $file);
18+
}
19+
try {
20+
var_dump((string) $file);
21+
} catch (Exception $e) {
22+
echo $e->getMessage(), "\n";
23+
}
24+
25+
echo "--- Use csv control ---\n";
26+
27+
$file = new SplTempFileObject;
28+
$file->fwrite($contents);
29+
$file->rewind();
30+
$file->setFlags(SplFileObject::READ_CSV);
31+
$file->setCsvControl(',', '"', '');
32+
foreach ($file as $row) {
33+
var_dump((string) $file);
34+
}
35+
try {
36+
var_dump((string) $file);
37+
} catch (Exception $e) {
38+
echo $e->getMessage(), "\n";
39+
}
40+
41+
?>
42+
--EXPECT--
43+
--- Directly call fgetcsv ---
44+
string(14) ""A", "B", "C"
45+
"
46+
string(13) ""D", "E", "F""
47+
Cannot read from file php://temp
48+
--- Use csv control ---
49+
string(14) ""A", "B", "C"
50+
"
51+
string(13) ""D", "E", "F""
52+
Cannot read from file php://temp

0 commit comments

Comments
 (0)