Skip to content

Commit 9be6b16

Browse files
committed
Merge branch 'PHP-7.3'
2 parents 662243c + 0da1308 commit 9be6b16

File tree

3 files changed

+184
-49
lines changed

3 files changed

+184
-49
lines changed

ext/openssl/tests/ServerClientTestCase.inc

+52-28
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22

33
const WORKER_ARGV_VALUE = 'RUN_WORKER';
44

5-
function phpt_notify()
5+
const WORKER_DEFAULT_NAME = 'server';
6+
7+
function phpt_notify($worker = WORKER_DEFAULT_NAME)
68
{
7-
ServerClientTestCase::getInstance()->notify();
9+
ServerClientTestCase::getInstance()->notify($worker);
810
}
911

10-
function phpt_wait()
12+
function phpt_wait($worker = WORKER_DEFAULT_NAME)
1113
{
12-
ServerClientTestCase::getInstance()->wait();
14+
ServerClientTestCase::getInstance()->wait($worker);
1315
}
1416

1517
/**
@@ -20,11 +22,11 @@ class ServerClientTestCase
2022
{
2123
private $isWorker = false;
2224

23-
private $workerHandle;
25+
private $workerHandle = [];
2426

25-
private $workerStdIn;
27+
private $workerStdIn = [];
2628

27-
private $workerStdOut;
29+
private $workerStdOut = [];
2830

2931
private static $instance;
3032

@@ -46,26 +48,41 @@ class ServerClientTestCase
4648
$this->isWorker = $isWorker;
4749
}
4850

49-
private function spawnWorkerProcess($code)
51+
private function spawnWorkerProcess($worker, $code)
5052
{
5153
if (defined("PHP_WINDOWS_VERSION_MAJOR")) {
52-
$ini = php_ini_loaded_file();
53-
$cmd = sprintf('%s %s "%s" %s', PHP_BINARY, $ini ? "-n -c $ini" : "", __FILE__, WORKER_ARGV_VALUE);
54+
$ini = php_ini_loaded_file();
55+
$cmd = sprintf(
56+
'%s %s "%s" %s',
57+
PHP_BINARY, $ini ? "-n -c $ini" : "",
58+
__FILE__,
59+
WORKER_ARGV_VALUE
60+
);
5461
} else {
55-
$cmd = sprintf('%s "%s" %s', PHP_BINARY, __FILE__, WORKER_ARGV_VALUE);
62+
$cmd = sprintf(
63+
'%s "%s" %s %s',
64+
PHP_BINARY,
65+
__FILE__,
66+
WORKER_ARGV_VALUE,
67+
$worker
68+
);
5669
}
57-
$this->workerHandle = proc_open($cmd, [['pipe', 'r'], ['pipe', 'w'], STDERR], $pipes);
58-
$this->workerStdIn = $pipes[0];
59-
$this->workerStdOut = $pipes[1];
60-
61-
fwrite($this->workerStdIn, $code . "\n---\n");
70+
$this->workerHandle[$worker] = proc_open(
71+
$cmd,
72+
[['pipe', 'r'], ['pipe', 'w'], STDERR],
73+
$pipes
74+
);
75+
$this->workerStdIn[$worker] = $pipes[0];
76+
$this->workerStdOut[$worker] = $pipes[1];
77+
78+
fwrite($this->workerStdIn[$worker], $code . "\n---\n");
6279
}
6380

64-
private function cleanupWorkerProcess()
81+
private function cleanupWorkerProcess($worker)
6582
{
66-
fclose($this->workerStdIn);
67-
fclose($this->workerStdOut);
68-
proc_close($this->workerHandle);
83+
fclose($this->workerStdIn[$worker]);
84+
fclose($this->workerStdOut[$worker]);
85+
proc_close($this->workerHandle[$worker]);
6986
}
7087

7188
private function stripPhpTagsFromCode($code)
@@ -90,21 +107,28 @@ class ServerClientTestCase
90107
eval($code);
91108
}
92109

93-
public function run($proc1Code, $proc2Code)
110+
public function run($masterCode, $workerCode)
94111
{
95-
$this->spawnWorkerProcess($this->stripPhpTagsFromCode($proc2Code));
96-
eval($this->stripPhpTagsFromCode($proc1Code));
97-
$this->cleanupWorkerProcess();
112+
if (!is_array($workerCode)) {
113+
$workerCode = [WORKER_DEFAULT_NAME => $workerCode];
114+
}
115+
foreach ($workerCode as $worker => $code) {
116+
$this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code));
117+
}
118+
eval($this->stripPhpTagsFromCode($masterCode));
119+
foreach ($workerCode as $worker => $code) {
120+
$this->cleanupWorkerProcess($worker);
121+
}
98122
}
99123

100-
public function wait()
124+
public function wait($worker)
101125
{
102-
fgets($this->isWorker ? STDIN : $this->workerStdOut);
126+
fgets($this->isWorker ? STDIN : $this->workerStdOut[$worker]);
103127
}
104128

105-
public function notify()
129+
public function notify($worker)
106130
{
107-
fwrite($this->isWorker ? STDOUT : $this->workerStdIn, "\n");
131+
fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "\n");
108132
}
109133
}
110134

ext/openssl/tests/bug77390.phpt

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
--TEST--
2+
Bug #76705: feof might hang on TLS streams in case of fragmented TLS records
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("openssl")) die("skip openssl not loaded");
6+
if (!function_exists("proc_open")) die("skip no proc_open");
7+
?>
8+
--FILE--
9+
<?php
10+
$certFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug77390.pem.tmp';
11+
$cacertFile = __DIR__ . DIRECTORY_SEPARATOR . 'bug77390-ca.pem.tmp';
12+
13+
$peerName = 'bug77390';
14+
$clientCode = <<<'CODE'
15+
$context = stream_context_create(['ssl' => ['verify_peer' => false, 'peer_name' => '%s']]);
16+
17+
phpt_wait('server');
18+
phpt_notify('proxy');
19+
20+
phpt_wait('proxy');
21+
$fp = stream_socket_client("ssl://127.0.0.1:10012", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT, $context);
22+
stream_set_blocking($fp, false);
23+
24+
$read = [$fp];
25+
$buf = '';
26+
$printed = false;
27+
while (stream_select($read, $write, $except, 1000)) {
28+
$chunk = stream_get_contents($fp, 4096);
29+
if ($chunk !== "") {
30+
var_dump($chunk);
31+
$buf .= $chunk;
32+
} elseif (!$printed) {
33+
$printed = true;
34+
var_dump($chunk);
35+
}
36+
if ($buf === 'hello, world') {
37+
break;
38+
}
39+
}
40+
41+
phpt_notify('server');
42+
phpt_notify('proxy');
43+
CODE;
44+
$clientCode = sprintf($clientCode, $peerName);
45+
46+
$serverCode = <<<'CODE'
47+
$context = stream_context_create(['ssl' => ['local_cert' => '%s']]);
48+
49+
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
50+
$fp = stream_socket_server("ssl://127.0.0.1:10011", $errornum, $errorstr, $flags, $context);
51+
phpt_notify();
52+
53+
$conn = stream_socket_accept($fp);
54+
fwrite($conn, 'hello, world');
55+
56+
phpt_wait();
57+
fclose($conn);
58+
CODE;
59+
$serverCode = sprintf($serverCode, $certFile);
60+
61+
$proxyCode = <<<'CODE'
62+
phpt_wait();
63+
64+
$upstream = stream_socket_client("tcp://127.0.0.1:10011", $errornum, $errorstr, 3000, STREAM_CLIENT_CONNECT);
65+
stream_set_blocking($upstream, false);
66+
67+
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
68+
$server = stream_socket_server("tcp://127.0.0.1:10012", $errornum, $errorstr, $flags);
69+
phpt_notify();
70+
71+
$conn = stream_socket_accept($server);
72+
stream_set_blocking($conn, false);
73+
74+
$read = [$upstream, $conn];
75+
while (stream_select($read, $write, $except, 1)) {
76+
foreach ($read as $fp) {
77+
$data = stream_get_contents($fp);
78+
if ($fp === $conn) {
79+
fwrite($upstream, $data);
80+
} else {
81+
if ($data !== '' && $data[0] === chr(23)) {
82+
$parts = str_split($data, (int) ceil(strlen($data) / 3));
83+
foreach ($parts as $part) {
84+
fwrite($conn, $part);
85+
usleep(1000);
86+
}
87+
} else {
88+
fwrite($conn, $data);
89+
}
90+
}
91+
}
92+
if (feof($upstream)) {
93+
break;
94+
}
95+
$read = [$upstream, $conn];
96+
}
97+
98+
phpt_wait();
99+
CODE;
100+
101+
include 'CertificateGenerator.inc';
102+
$certificateGenerator = new CertificateGenerator();
103+
$certificateGenerator->saveCaCert($cacertFile);
104+
$certificateGenerator->saveNewCertAsFileWithKey($peerName, $certFile);
105+
106+
include 'ServerClientTestCase.inc';
107+
ServerClientTestCase::getInstance()->run($clientCode, [
108+
'server' => $serverCode,
109+
'proxy' => $proxyCode,
110+
]);
111+
?>
112+
--CLEAN--
113+
<?php
114+
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'bug77390.pem.tmp');
115+
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'bug77390-ca.pem.tmp');
116+
?>
117+
--EXPECT--
118+
string(0) ""
119+
string(12) "hello, world"

ext/openssl/xp_ssl.c

+13-21
Original file line numberDiff line numberDiff line change
@@ -2459,30 +2459,22 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val
24592459
alive = 0;
24602460
} else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
24612461
if (sslsock->ssl_active) {
2462-
int n;
2463-
2464-
do {
2465-
n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
2466-
if (n <= 0) {
2467-
int err = SSL_get_error(sslsock->ssl_handle, n);
2468-
2469-
if (err == SSL_ERROR_SYSCALL) {
2462+
int n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
2463+
if (n <= 0) {
2464+
int err = SSL_get_error(sslsock->ssl_handle, n);
2465+
switch (err) {
2466+
case SSL_ERROR_SYSCALL:
24702467
alive = php_socket_errno() == EAGAIN;
24712468
break;
2472-
}
2473-
2474-
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
2475-
/* re-negotiate */
2476-
continue;
2477-
}
2478-
2479-
/* any other problem is a fatal error */
2480-
alive = 0;
2469+
case SSL_ERROR_WANT_READ:
2470+
case SSL_ERROR_WANT_WRITE:
2471+
alive = 1;
2472+
break;
2473+
default:
2474+
/* any other problem is a fatal error */
2475+
alive = 0;
24812476
}
2482-
/* either peek succeeded or there was an error; we
2483-
* have set the alive flag appropriately */
2484-
break;
2485-
} while (1);
2477+
}
24862478
} else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
24872479
alive = 0;
24882480
}

0 commit comments

Comments
 (0)