Skip to content

Commit 032af9a

Browse files
authored
PHPC-1529: Reset libmongocrypt key vault client after forking (#1189)
* PHPC-1529: Reset libmongocrypt key vault client after forking * Run ping command instead of find * Update wrong comment * Only run tests when libmongocrypt is present * Add mongocryptd.pid to gitignore * Fix test failures due to missing mongocryptd
1 parent 69efd5c commit 032af9a

File tree

3 files changed

+111
-0
lines changed

3 files changed

+111
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ modules
3131
run-tests.php
3232
tags
3333
.lvimrc
34+
mongocryptd.pid
3435

3536
# phpt leftovers
3637
*.diff

php_phongo.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3603,6 +3603,11 @@ void php_phongo_client_reset_once(php_phongo_manager_t* manager, int pid)
36033603
{
36043604
php_phongo_pclient_t* pclient;
36053605

3606+
/* Reset associated key vault client */
3607+
if (!Z_ISUNDEF(manager->key_vault_client_manager)) {
3608+
php_phongo_client_reset_once(Z_MANAGER_OBJ_P(&manager->key_vault_client_manager), pid);
3609+
}
3610+
36063611
if (manager->use_persistent_client) {
36073612
pclient = zend_hash_str_find_ptr(&MONGODB_G(persistent_clients), manager->client_hash, manager->client_hash_len);
36083613

tests/cursor/bug1529-001.phpt

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
--TEST--
2+
PHPC-1529: Resetting a client should also reset the keyVaultClient
3+
--SKIPIF--
4+
<?php if (!function_exists('pcntl_fork')) { die('skip pcntl_fork() not available'); } ?>
5+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
6+
<?php skip_if_not_live(); ?>
7+
<?php skip_if_not_clean(); ?>
8+
<?php skip_if_server_version('<', '4.2'); ?>
9+
<?php skip_if_not_libmongocrypt(); ?>
10+
--FILE--
11+
<?php
12+
require_once __DIR__ . "/../utils/basic.inc";
13+
14+
class CommandLogger implements MongoDB\Driver\Monitoring\CommandSubscriber
15+
{
16+
private $pid;
17+
18+
public function __construct()
19+
{
20+
$this->pid = getmypid();
21+
}
22+
23+
public function commandStarted(MongoDB\Driver\Monitoring\CommandStartedEvent $event)
24+
{
25+
$command = $event->getCommand();
26+
$commandName = $event->getCommandName();
27+
$process = $this->pid === getmypid() ? 'Parent' : 'Child';
28+
29+
if ($commandName === 'find' || $commandName === 'getMore') {
30+
printf("%s executes %s with batchSize: %d\n", $process, $commandName, $command->batchSize);
31+
return;
32+
}
33+
34+
printf("%s executes %s\n", $process, $commandName);
35+
}
36+
37+
public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event)
38+
{
39+
}
40+
41+
public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event)
42+
{
43+
}
44+
}
45+
46+
$keyVaultClient = new MongoDB\Driver\Manager(URI, [], ['disableClientPersistence' => true]);
47+
$autoEncryptionOpts = [
48+
'keyVaultClient' => $keyVaultClient,
49+
'keyVaultNamespace' => 'default.keys',
50+
'kmsProviders' => ['local' => ['key' => new MongoDB\BSON\Binary(str_repeat('0', 96), 0)]],
51+
];
52+
53+
$manager = new MongoDB\Driver\Manager(URI, [], ['autoEncryption' => $autoEncryptionOpts, 'disableClientPersistence' => true]);
54+
55+
$bulk = new MongoDB\Driver\BulkWrite();
56+
$bulk->insert(['x' => 1]);
57+
$bulk->insert(['x' => 2]);
58+
$bulk->insert(['x' => 3]);
59+
$keyVaultClient->executeBulkWrite(NS, $bulk);
60+
61+
MongoDB\Driver\Monitoring\addSubscriber(new CommandLogger);
62+
63+
$query = new MongoDB\Driver\Query([], ['batchSize' => 2]);
64+
$cursor = $keyVaultClient->executeQuery(NS, $query);
65+
66+
$childPid = pcntl_fork();
67+
68+
if ($childPid === 0) {
69+
/* Executing any operation with the parent's client resets this client as well as
70+
* the keyVaultClient. Continuing iteration of the cursor opened on the
71+
* keyVaultClient before resetting it should then result in an error due to
72+
* the client having been reset. */
73+
$manager->executeCommand(DATABASE_NAME, new MongoDB\Driver\Command(['ping' => 1]));
74+
75+
echo throws(
76+
function () use ($cursor) { iterator_count($cursor); },
77+
MongoDB\Driver\Exception\RuntimeException::class
78+
), "\n";
79+
80+
echo "Child exits\n";
81+
exit;
82+
}
83+
84+
if ($childPid > 0) {
85+
$waitPid = pcntl_waitpid($childPid, $status);
86+
87+
if ($waitPid === $childPid) {
88+
echo "Parent waited for child to exit\n";
89+
}
90+
91+
unset($cursor);
92+
}
93+
94+
?>
95+
===DONE===
96+
<?php exit(0); ?>
97+
--EXPECT--
98+
Parent executes find with batchSize: 2
99+
Child executes ping
100+
OK: Got MongoDB\Driver\Exception\RuntimeException
101+
Cannot advance cursor after client reset
102+
Child exits
103+
Parent waited for child to exit
104+
Parent executes killCursors
105+
===DONE===

0 commit comments

Comments
 (0)