Skip to content

Commit 52d1386

Browse files
committed
Do not inherit open FDs to SSH child process by overwriting and closing
This fixes a possible race condition where open FDs where in fact inherited to the wrapping shell before it had a chance to close them again when it is being replaced with the actual SSH binary. This builds on top of reactphp/child-process#65
1 parent e7512e6 commit 52d1386

File tree

4 files changed

+20
-15
lines changed

4 files changed

+20
-15
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"require": {
1818
"php": ">=5.3",
1919
"clue/socks-react": "^1.0",
20-
"react/child-process": "^0.5",
20+
"react/child-process": "^0.6",
2121
"react/event-loop": "^1.0 || ^0.5",
2222
"react/promise": "^2.1 || ^1.2.1",
2323
"react/socket": "^1.1",

src/Io/functions.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,21 @@ function fds($path = '/dev/fd')
4242
*/
4343
function processWithoutFds($command)
4444
{
45+
// launch process with default STDIO pipes
46+
$pipes = array(
47+
array('pipe', 'r'),
48+
array('pipe', 'w'),
49+
array('pipe', 'w')
50+
);
51+
4552
// try to get list of all open FDs
4653
$fds = fds();
4754

48-
// do not inherit open FDs by explicitly closing all of them
55+
// do not inherit open FDs by explicitly overwriting existing FDs with dummy files
56+
// additionally, close all dummy files in the child process again
4957
foreach ($fds as $fd) {
5058
if ($fd > 2) {
59+
$pipes[$fd] = array('file', '/dev/null', 'r');
5160
$command .= ' ' . $fd . '>&-';
5261
}
5362
}
@@ -57,5 +66,5 @@ function processWithoutFds($command)
5766
$command = 'exec bash -c ' . escapeshellarg($command);
5867
}
5968

60-
return new Process($command);
69+
return new Process($command, null, null, $pipes);
6170
}

tests/FunctionalSshProcessConnectorTest.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function testConnectValidTargetWillReturnPromiseWhichResolvesToConnection
6969
$connection->close();
7070
}
7171

72-
public function testConnectValidTargetWillNotInheritActiveFileDescriptors()
72+
public function testConnectPendingWillNotInheritActiveFileDescriptors()
7373
{
7474
$server = stream_socket_server('tcp://127.0.0.1:0');
7575
$address = stream_socket_get_name($server, false);
@@ -83,17 +83,15 @@ public function testConnectValidTargetWillNotInheritActiveFileDescriptors()
8383
$this->markTestSkipped('Platform does not prevent binding to same address (Windows?)');
8484
}
8585

86-
// wait for successful connection
8786
$promise = $this->connector->connect('example.com:80');
88-
$connection = \Clue\React\Block\await($promise, $this->loop, self::TIMEOUT);
8987

9088
// close server and ensure we can start a new server on the previous address
91-
// the open SSH connection process should not inherit the existing server socket
89+
// the pending SSH connection process should not inherit the existing server socket
9290
fclose($server);
9391
$server = stream_socket_server('tcp://' . $address);
9492
$this->assertTrue(is_resource($server));
95-
9693
fclose($server);
97-
$connection->close();
94+
95+
$promise->cancel();
9896
}
9997
}

tests/FunctionalSshSocksConnectorTest.php

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public function testConnectValidTargetWillReturnPromiseWhichResolvesToConnection
8989
$connection->close();
9090
}
9191

92-
public function testConnectValidTargetWillNotInheritActiveFileDescriptors()
92+
public function testConnectPendingWillNotInheritActiveFileDescriptors()
9393
{
9494
$server = stream_socket_server('tcp://127.0.0.1:0');
9595
$address = stream_socket_get_name($server, false);
@@ -103,17 +103,15 @@ public function testConnectValidTargetWillNotInheritActiveFileDescriptors()
103103
$this->markTestSkipped('Platform does not prevent binding to same address (Windows?)');
104104
}
105105

106-
// wait for successful connection
107106
$promise = $this->connector->connect('example.com:80');
108-
$connection = \Clue\React\Block\await($promise, $this->loop, self::TIMEOUT);
109107

110108
// close server and ensure we can start a new server on the previous address
111-
// the open SSH connection process should not inherit the existing server socket
109+
// the pending SSH connection process should not inherit the existing server socket
112110
fclose($server);
113111
$server = stream_socket_server('tcp://' . $address);
114112
$this->assertTrue(is_resource($server));
115-
116113
fclose($server);
117-
$connection->close();
114+
115+
$promise->cancel();
118116
}
119117
}

0 commit comments

Comments
 (0)