Skip to content

Commit 23697ec

Browse files
committed
Add tests for uncastable streams and dataloss streams
Moreover, stop using php_stream_can_cast() just before a php_stream_cast()
1 parent 4cee2c0 commit 23697ec

14 files changed

+589
-31
lines changed

ext/gd/gd.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,11 +1562,9 @@ static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type,
15621562
pefree(pstr, 1);
15631563
zend_string_release_ex(buff, 0);
15641564
}
1565-
else if (php_stream_can_cast(stream, PHP_STREAM_AS_STDIO)) {
1566-
/* try and force the stream to be FILE* */
1567-
if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_TRY_HARD, (void **) &fp, REPORT_ERRORS)) {
1568-
goto out_err;
1569-
}
1565+
/* try and force the stream to be FILE* */
1566+
else if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_TRY_HARD, (void **) &fp, REPORT_ERRORS)) {
1567+
goto out_err;
15701568
}
15711569

15721570
if (!im && fp) {
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
--TEST--
2+
imagecreatefromxbm(): uncastable user stream
3+
--EXTENSIONS--
4+
gd
5+
--SKIPIF--
6+
<?php
7+
if (!GD_BUNDLED) die("skip requires bundled GD library\n");
8+
?>
9+
--FILE--
10+
<?php
11+
12+
class DummyStreamWrapper
13+
{
14+
/** @var resource|null */
15+
public $context;
16+
17+
/** @var resource|null */
18+
public $handle;
19+
20+
public function stream_cast(int $castAs)
21+
{
22+
return false;
23+
}
24+
25+
public function stream_close(): void { }
26+
27+
public function stream_open(string $path, string $mode, int $options = 0, ?string &$openedPath = null): bool
28+
{
29+
return true;
30+
}
31+
32+
public function stream_read(int $count)
33+
{
34+
return false;
35+
}
36+
37+
public function stream_seek(int $offset, int $whence = SEEK_SET): bool
38+
{
39+
return true;
40+
}
41+
42+
public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
43+
{
44+
return false;
45+
}
46+
47+
public function stream_stat()
48+
{
49+
return [];
50+
}
51+
52+
public function stream_tell()
53+
{
54+
return [];
55+
}
56+
57+
public function stream_truncate(int $newSize): bool
58+
{
59+
return true;
60+
}
61+
62+
public function stream_write(string $data) { }
63+
64+
65+
public function unlink(string $path): bool
66+
{
67+
return false;
68+
}
69+
}
70+
stream_wrapper_register('custom', DummyStreamWrapper::class);
71+
72+
$fp = fopen("custom://myvar", "r+");
73+
74+
/* This tests the underlying _php_image_create_from() C function */
75+
var_dump(imagecreatefromxbm("custom://myvar"));
76+
fclose($fp);
77+
78+
echo "Done";
79+
?>
80+
--EXPECTF--
81+
Warning: imagecreatefromxbm(): "custom://myvar" is not a valid XBM file in %s on line %d
82+
bool(false)
83+
Done
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
--TEST--
2+
imagecreatefromxbm(): casting stream emits data loss
3+
--EXTENSIONS--
4+
gd
5+
--SKIPIF--
6+
<?php
7+
if (!GD_BUNDLED) die("skip requires bundled GD library\n");
8+
//if (getenv('SKIP_ONLINE_TESTS')) die('skip online test');
9+
require __DIR__ . '/../../standard/tests/http/server.inc'; http_server_skipif();
10+
?>
11+
--INI--
12+
allow_url_fopen=1
13+
--FILE--
14+
<?php
15+
16+
require __DIR__ . '/../../standard/tests/http/server.inc';
17+
18+
$responses = array(
19+
"data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\nSome: Header\r\n\r\nBody",
20+
);
21+
22+
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
23+
24+
$handle = fopen($uri, 'r');
25+
/* This tests the underlying _php_image_create_from() C function */
26+
/* Doesn't seem like it's possible to trigger the same data loss as somehow it cannot connect to the host? */
27+
var_dump(imagecreatefromxbm($uri));
28+
var_dump(fread($handle, 20));
29+
fclose($handle);
30+
31+
http_server_kill($pid);
32+
?>
33+
--EXPECTF--
34+
Warning: imagecreatefromxbm(%s): Failed to open stream: %s in %s on line %d
35+
bool(false)
36+
string(4) "Body"

ext/posix/posix.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -426,11 +426,10 @@ static int php_posix_stream_get_fd(zval *zfp, int *fd) /* {{{ */
426426
if (stream == NULL) {
427427
return 0;
428428
}
429-
if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT) == SUCCESS) {
430-
php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT, (void*)fd, 0);
431-
} else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD) == SUCCESS) {
432-
php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)fd, 0);
433-
} else {
429+
if (
430+
php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT, (void*)&fd, 0) == FAILURE &&
431+
php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd, 0) == FAILURE
432+
) {
434433
php_error_docref(NULL, E_WARNING, "Could not use stream of type '%s'",
435434
stream->ops->label);
436435
return 0;

ext/posix/tests/posix_isatty.phpt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,11 @@
22
posix_isatty(): Basic tests
33
--EXTENSIONS--
44
posix
5-
--SKIPIF--
6-
<?php
7-
if (!function_exists('posix_isatty')) die('skip posix_isatty() not found');
8-
?>
95
--FILE--
106
<?php
117

12-
var_dump(posix_isatty(0));
8+
var_dump(posix_isatty(STDIN));
139

1410
?>
15-
--EXPECTF--
16-
bool(%s)
11+
--EXPECT--
12+
bool(false)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
--TEST--
2+
posix_isatty(): uncastable user stream
3+
--EXTENSIONS--
4+
posix
5+
--FILE--
6+
<?php
7+
class DummyStreamWrapper
8+
{
9+
/** @var resource|null */
10+
public $context;
11+
12+
/** @var resource|null */
13+
public $handle;
14+
15+
public function stream_cast(int $castAs)
16+
{
17+
return false;
18+
}
19+
20+
public function stream_close(): void { }
21+
22+
public function stream_open(string $path, string $mode, int $options = 0, ?string &$openedPath = null): bool
23+
{
24+
return true;
25+
}
26+
27+
public function stream_read(int $count)
28+
{
29+
return false;
30+
}
31+
32+
public function stream_seek(int $offset, int $whence = SEEK_SET): bool
33+
{
34+
return true;
35+
}
36+
37+
public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
38+
{
39+
return false;
40+
}
41+
42+
public function stream_stat()
43+
{
44+
return [];
45+
}
46+
47+
public function stream_tell()
48+
{
49+
return [];
50+
}
51+
52+
public function stream_truncate(int $newSize): bool
53+
{
54+
return true;
55+
}
56+
57+
public function stream_write(string $data) { }
58+
59+
60+
public function unlink(string $path): bool
61+
{
62+
return false;
63+
}
64+
}
65+
stream_wrapper_register('custom', DummyStreamWrapper::class);
66+
67+
$fp = fopen("custom://myvar", "r+");
68+
var_dump(posix_isatty($fp));
69+
fclose($fp);
70+
71+
echo "Done";
72+
?>
73+
--EXPECTF--
74+
Warning: posix_isatty(): Could not use stream of type 'user-space' in %s on line %d
75+
bool(false)
76+
Done
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
posix_isatty(): casting stream emits data loss
3+
--EXTENSIONS--
4+
posix
5+
--SKIPIF--
6+
<?php
7+
//if (getenv('SKIP_ONLINE_TESTS')) die('skip online test');
8+
require __DIR__ . '/../../standard/tests/http/server.inc'; http_server_skipif();
9+
?>
10+
--INI--
11+
allow_url_fopen=1
12+
--FILE--
13+
<?php
14+
15+
require __DIR__ . '/../../standard/tests/http/server.inc';
16+
17+
$responses = array(
18+
"data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\nSome: Header\r\n\r\nBody",
19+
);
20+
21+
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
22+
23+
$handle = fopen($uri, 'r');
24+
var_dump(posix_isatty($handle));
25+
var_dump(fread($handle, 20));
26+
fclose($handle);
27+
28+
http_server_kill($pid);
29+
?>
30+
--EXPECTF--
31+
Warning: posix_isatty(): %d bytes of buffered data lost during stream conversion! in %s on line %d
32+
bool(false)
33+
string(4) "Body"
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
--TEST--
2+
posix_ttyname(): uncastable user stream
3+
--EXTENSIONS--
4+
posix
5+
--FILE--
6+
<?php
7+
class DummyStreamWrapper
8+
{
9+
/** @var resource|null */
10+
public $context;
11+
12+
/** @var resource|null */
13+
public $handle;
14+
15+
public function stream_cast(int $castAs)
16+
{
17+
return false;
18+
}
19+
20+
public function stream_close(): void { }
21+
22+
public function stream_open(string $path, string $mode, int $options = 0, ?string &$openedPath = null): bool
23+
{
24+
return true;
25+
}
26+
27+
public function stream_read(int $count)
28+
{
29+
return false;
30+
}
31+
32+
public function stream_seek(int $offset, int $whence = SEEK_SET): bool
33+
{
34+
return true;
35+
}
36+
37+
public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
38+
{
39+
return false;
40+
}
41+
42+
public function stream_stat()
43+
{
44+
return [];
45+
}
46+
47+
public function stream_tell()
48+
{
49+
return [];
50+
}
51+
52+
public function stream_truncate(int $newSize): bool
53+
{
54+
return true;
55+
}
56+
57+
public function stream_write(string $data) { }
58+
59+
60+
public function unlink(string $path): bool
61+
{
62+
return false;
63+
}
64+
}
65+
stream_wrapper_register('custom', DummyStreamWrapper::class);
66+
67+
$fp = fopen("custom://myvar", "r+");
68+
var_dump(posix_ttyname($fp));
69+
fclose($fp);
70+
71+
echo "Done";
72+
?>
73+
--EXPECTF--
74+
Warning: posix_ttyname(): Could not use stream of type 'user-space' in %s on line %d
75+
bool(false)
76+
Done
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
posix_ttyname(): casting stream emits data loss
3+
--EXTENSIONS--
4+
posix
5+
--SKIPIF--
6+
<?php
7+
//if (getenv('SKIP_ONLINE_TESTS')) die('skip online test');
8+
require __DIR__ . '/../../standard/tests/http/server.inc'; http_server_skipif();
9+
?>
10+
--INI--
11+
allow_url_fopen=1
12+
--FILE--
13+
<?php
14+
15+
require __DIR__ . '/../../standard/tests/http/server.inc';
16+
17+
$responses = array(
18+
"data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\nSome: Header\r\n\r\nBody",
19+
);
20+
21+
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
22+
23+
$handle = fopen($uri, 'r');
24+
var_dump(posix_ttyname($handle));
25+
var_dump(fread($handle, 20));
26+
fclose($handle);
27+
28+
http_server_kill($pid);
29+
?>
30+
--EXPECTF--
31+
Warning: posix_ttyname(): %d bytes of buffered data lost during stream conversion! in %s on line %d
32+
bool(false)
33+
string(4) "Body"

0 commit comments

Comments
 (0)