Skip to content

Commit 27a698b

Browse files
committed
Fix main/streams
The php://memory seek handler negated the zend_off_t offset as size_t, which truncates where size_t is narrower, so a seek by PHP_INT_MIN or beyond 4GB succeeded at a wrapped position. Negate it as zend_ulong instead, and fail seeks whose target does not fit size_t.
1 parent bf7c013 commit 27a698b

2 files changed

Lines changed: 91 additions & 3 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
--TEST--
2+
fseek() on php://memory with offsets beyond size_t on narrow size_t
3+
--SKIPIF--
4+
<?php
5+
if (PHP_SYS_SIZE >= PHP_INT_SIZE) {
6+
die("skip size_t is not narrower than zend_long on this platform");
7+
}
8+
?>
9+
--FILE--
10+
<?php
11+
$sizeMax = 2 ** (PHP_SYS_SIZE * 8) - 1;
12+
13+
foreach (['php://memory', 'php://temp'] as $url) {
14+
echo "$url\n";
15+
$s = fopen($url, 'r+');
16+
fwrite($s, 'hello');
17+
18+
foreach ([
19+
'SET SIZE_MAX + 1' => [$sizeMax + 1, SEEK_SET],
20+
'SET SIZE_MAX + 3' => [$sizeMax + 3, SEEK_SET],
21+
'SET PHP_INT_MAX' => [PHP_INT_MAX, SEEK_SET],
22+
'CUR SIZE_MAX' => [$sizeMax, SEEK_CUR],
23+
'CUR PHP_INT_MAX' => [PHP_INT_MAX, SEEK_CUR],
24+
'CUR -(SIZE_MAX + 1)' => [-($sizeMax + 1), SEEK_CUR],
25+
'CUR PHP_INT_MIN' => [PHP_INT_MIN, SEEK_CUR],
26+
'END SIZE_MAX' => [$sizeMax, SEEK_END],
27+
'END -(SIZE_MAX + 1)' => [-($sizeMax + 1), SEEK_END],
28+
'END PHP_INT_MIN' => [PHP_INT_MIN, SEEK_END],
29+
] as $label => [$offset, $whence]) {
30+
fseek($s, 1);
31+
printf("%-20s %2d %d\n", $label, fseek($s, $offset, $whence), ftell($s));
32+
}
33+
34+
echo "Up to SIZE_MAX\n";
35+
var_dump(fseek($s, $sizeMax, SEEK_SET));
36+
var_dump(ftell($s) === $sizeMax);
37+
var_dump(fseek($s, 1, SEEK_CUR));
38+
var_dump(ftell($s) === $sizeMax);
39+
var_dump(fseek($s, $sizeMax - 5, SEEK_END));
40+
var_dump(ftell($s) === $sizeMax);
41+
42+
fclose($s);
43+
}
44+
?>
45+
--EXPECT--
46+
php://memory
47+
SET SIZE_MAX + 1 -1 1
48+
SET SIZE_MAX + 3 -1 1
49+
SET PHP_INT_MAX -1 1
50+
CUR SIZE_MAX -1 1
51+
CUR PHP_INT_MAX -1 1
52+
CUR -(SIZE_MAX + 1) -1 1
53+
CUR PHP_INT_MIN -1 1
54+
END SIZE_MAX -1 1
55+
END -(SIZE_MAX + 1) -1 1
56+
END PHP_INT_MIN -1 1
57+
Up to SIZE_MAX
58+
int(0)
59+
bool(true)
60+
int(-1)
61+
bool(true)
62+
int(0)
63+
bool(true)
64+
php://temp
65+
SET SIZE_MAX + 1 -1 1
66+
SET SIZE_MAX + 3 -1 1
67+
SET PHP_INT_MAX -1 1
68+
CUR SIZE_MAX -1 1
69+
CUR PHP_INT_MAX -1 1
70+
CUR -(SIZE_MAX + 1) -1 1
71+
CUR PHP_INT_MIN -1 1
72+
END SIZE_MAX -1 1
73+
END -(SIZE_MAX + 1) -1 1
74+
END PHP_INT_MIN -1 1
75+
Up to SIZE_MAX
76+
int(0)
77+
bool(true)
78+
int(-1)
79+
bool(true)
80+
int(0)
81+
bool(true)

‎main/streams/memory.c‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe
126126
switch(whence) {
127127
case SEEK_CUR:
128128
if (offset < 0) {
129-
if (ms->fpos < -(size_t)offset) {
129+
if (ms->fpos < -(zend_ulong)offset) {
130130
*newoffs = ms->fpos;
131131
return -1;
132132
} else {
@@ -136,6 +136,9 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe
136136
stream->fatal_error = 0;
137137
return 0;
138138
}
139+
} else if ((zend_ulong)offset > SIZE_MAX - ms->fpos) {
140+
*newoffs = ms->fpos;
141+
return -1;
139142
} else {
140143
stream->eof = 0;
141144
stream->fatal_error = 0;
@@ -144,7 +147,7 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe
144147
return 0;
145148
}
146149
case SEEK_SET:
147-
if (offset < 0) {
150+
if (offset < 0 || ZEND_LONG_SIZE_T_OVFL(offset)) {
148151
*newoffs = ms->fpos;
149152
return -1;
150153
} else {
@@ -156,12 +159,16 @@ static int php_stream_memory_seek(php_stream *stream, zend_off_t offset, int whe
156159
}
157160
case SEEK_END:
158161
if (offset > 0) {
162+
if ((zend_ulong)offset > SIZE_MAX - ZSTR_LEN(ms->data)) {
163+
*newoffs = ms->fpos;
164+
return -1;
165+
}
159166
ms->fpos = ZSTR_LEN(ms->data) + offset;
160167
*newoffs = ms->fpos;
161168
stream->eof = 0;
162169
stream->fatal_error = 0;
163170
return 0;
164-
} else if (ZSTR_LEN(ms->data) < -(size_t)offset) {
171+
} else if (ZSTR_LEN(ms->data) < -(zend_ulong)offset) {
165172
*newoffs = ms->fpos;
166173
return -1;
167174
} else {

0 commit comments

Comments
 (0)