Skip to content

Commit 34b9d4d

Browse files
committed
PHPC-1274: Reset mongoc_client_t for child processes
Check pid before operations that could interfere with resources owned by the parent process. This includes freeing Cursor and Session objects, executing operations on the Manager or Server, or iterating a Cursor.
1 parent e655a13 commit 34b9d4d

File tree

9 files changed

+263
-0
lines changed

9 files changed

+263
-0
lines changed

php_phongo.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,13 @@ zend_bool phongo_writeconcernerror_init(zval* return_value, bson_t* bson TSRMLS_
212212
#define PHONGO_ZVAL_CLASS_OR_TYPE_NAME(zv) (Z_TYPE(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE(zv)->name) : zend_get_type_by_const(Z_TYPE(zv)))
213213
#define PHONGO_ZVAL_CLASS_OR_TYPE_NAME_P(zvp) PHONGO_ZVAL_CLASS_OR_TYPE_NAME(*(zvp))
214214

215+
#define PHONGO_CLIENT_RESET_IF_CHIlD_PID(client, created_by_pid) \
216+
do { \
217+
if ((created_by_pid) != getpid()) { \
218+
mongoc_client_reset(client); \
219+
} \
220+
} while (0);
221+
215222
#endif /* PHONGO_H */
216223

217224
/*

php_phongo_structs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ typedef struct {
5555
PHONGO_ZEND_OBJECT_PRE
5656
mongoc_cursor_t* cursor;
5757
mongoc_client_t* client;
58+
int created_by_pid;
5859
uint32_t server_id;
5960
bool advanced;
6061
php_phongo_bson_state visitor_data;
@@ -78,6 +79,7 @@ typedef struct {
7879
typedef struct {
7980
PHONGO_ZEND_OBJECT_PRE
8081
mongoc_client_t* client;
82+
int created_by_pid;
8183
PHONGO_ZEND_OBJECT_POST
8284
} php_phongo_manager_t;
8385

@@ -108,12 +110,14 @@ typedef struct {
108110
PHONGO_ZEND_OBJECT_PRE
109111
mongoc_client_t* client;
110112
uint32_t server_id;
113+
int created_by_pid;
111114
PHONGO_ZEND_OBJECT_POST
112115
} php_phongo_server_t;
113116

114117
typedef struct {
115118
PHONGO_ZEND_OBJECT_PRE
116119
mongoc_client_session_t* client_session;
120+
int created_by_pid;
117121
PHONGO_ZEND_OBJECT_POST
118122
} php_phongo_session_t;
119123

src/MongoDB/Cursor.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ static void php_phongo_cursor_iterator_move_forward(zend_object_iterator* iter T
122122
cursor->advanced = true;
123123
}
124124

125+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(cursor->client, cursor->created_by_pid);
126+
125127
if (mongoc_cursor_next(cursor->cursor, &doc)) {
126128
php_phongo_bson_to_zval_ex(bson_get_data(doc), doc->len, &cursor->visitor_data);
127129
} else {
@@ -382,6 +384,8 @@ static void php_phongo_cursor_free_object(phongo_free_object_arg* object TSRMLS_
382384

383385
zend_object_std_dtor(&intern->std TSRMLS_CC);
384386

387+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
388+
385389
if (intern->cursor) {
386390
mongoc_cursor_destroy(intern->cursor);
387391
}
@@ -428,6 +432,8 @@ static phongo_create_object_retval php_phongo_cursor_create_object(zend_class_en
428432
zend_object_std_init(&intern->std, class_type TSRMLS_CC);
429433
object_properties_init(&intern->std, class_type);
430434

435+
intern->created_by_pid = (int) getpid();
436+
431437
#if PHP_VERSION_ID >= 70000
432438
intern->std.handlers = &php_phongo_handler_cursor;
433439

src/MongoDB/Manager.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ static PHP_METHOD(Manager, executeCommand)
357357
goto cleanup;
358358
}
359359

360+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
360361
phongo_execute_command(intern->client, PHONGO_COMMAND_RAW, db, command, options, server_id, return_value, return_value_used TSRMLS_CC);
361362

362363
cleanup:
@@ -394,6 +395,7 @@ static PHP_METHOD(Manager, executeReadCommand)
394395
return;
395396
}
396397

398+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
397399
phongo_execute_command(intern->client, PHONGO_COMMAND_READ, db, command, options, server_id, return_value, return_value_used TSRMLS_CC);
398400
} /* }}} */
399401

@@ -420,6 +422,7 @@ static PHP_METHOD(Manager, executeWriteCommand)
420422
return;
421423
}
422424

425+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
423426
phongo_execute_command(intern->client, PHONGO_COMMAND_WRITE, db, command, options, server_id, return_value, return_value_used TSRMLS_CC);
424427
} /* }}} */
425428

@@ -446,6 +449,7 @@ static PHP_METHOD(Manager, executeReadWriteCommand)
446449
return;
447450
}
448451

452+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
449453
phongo_execute_command(intern->client, PHONGO_COMMAND_READ_WRITE, db, command, options, server_id, return_value, return_value_used TSRMLS_CC);
450454
} /* }}} */
451455

@@ -481,6 +485,7 @@ static PHP_METHOD(Manager, executeQuery)
481485
goto cleanup;
482486
}
483487

488+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
484489
phongo_execute_query(intern->client, namespace, query, options, server_id, return_value, return_value_used TSRMLS_CC);
485490

486491
cleanup:
@@ -517,6 +522,7 @@ static PHP_METHOD(Manager, executeBulkWrite)
517522
goto cleanup;
518523
}
519524

525+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
520526
phongo_execute_bulk_write(intern->client, namespace, bulk, options, server_id, return_value, return_value_used TSRMLS_CC);
521527

522528
cleanup:
@@ -690,6 +696,7 @@ static PHP_METHOD(Manager, startSession)
690696
}
691697
}
692698

699+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
693700
cs = mongoc_client_start_session(intern->client, cs_opts, &error);
694701

695702
if (cs) {
@@ -795,6 +802,8 @@ static phongo_create_object_retval php_phongo_manager_create_object(zend_class_e
795802
zend_object_std_init(&intern->std, class_type TSRMLS_CC);
796803
object_properties_init(&intern->std, class_type);
797804

805+
intern->created_by_pid = (int) getpid();
806+
798807
#if PHP_VERSION_ID >= 70000
799808
intern->std.handlers = &php_phongo_handler_manager;
800809

src/MongoDB/Server.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ static PHP_METHOD(Server, executeCommand)
4747

4848
options = php_phongo_prep_legacy_option(options, "readPreference", &free_options TSRMLS_CC);
4949

50+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
5051
phongo_execute_command(intern->client, PHONGO_COMMAND_RAW, db, command, options, intern->server_id, return_value, return_value_used TSRMLS_CC);
5152

5253
if (free_options) {
@@ -71,6 +72,7 @@ static PHP_METHOD(Server, executeReadCommand)
7172
return;
7273
}
7374

75+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
7476
phongo_execute_command(intern->client, PHONGO_COMMAND_READ, db, command, options, intern->server_id, return_value, return_value_used TSRMLS_CC);
7577
} /* }}} */
7678

@@ -91,6 +93,7 @@ static PHP_METHOD(Server, executeWriteCommand)
9193
return;
9294
}
9395

96+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
9497
phongo_execute_command(intern->client, PHONGO_COMMAND_WRITE, db, command, options, intern->server_id, return_value, return_value_used TSRMLS_CC);
9598
} /* }}} */
9699

@@ -111,6 +114,7 @@ static PHP_METHOD(Server, executeReadWriteCommand)
111114
return;
112115
}
113116

117+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
114118
phongo_execute_command(intern->client, PHONGO_COMMAND_READ_WRITE, db, command, options, intern->server_id, return_value, return_value_used TSRMLS_CC);
115119
} /* }}} */
116120

@@ -134,6 +138,7 @@ static PHP_METHOD(Server, executeQuery)
134138

135139
options = php_phongo_prep_legacy_option(options, "readPreference", &free_options TSRMLS_CC);
136140

141+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
137142
phongo_execute_query(intern->client, namespace, query, options, intern->server_id, return_value, return_value_used TSRMLS_CC);
138143

139144
if (free_options) {
@@ -165,6 +170,7 @@ static PHP_METHOD(Server, executeBulkWrite)
165170

166171
options = php_phongo_prep_legacy_option(options, "writeConcern", &free_options TSRMLS_CC);
167172

173+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(intern->client, intern->created_by_pid);
168174
phongo_execute_bulk_write(intern->client, namespace, bulk, options, intern->server_id, return_value, return_value_used TSRMLS_CC);
169175

170176
if (free_options) {
@@ -570,6 +576,8 @@ static phongo_create_object_retval php_phongo_server_create_object(zend_class_en
570576
zend_object_std_init(&intern->std, class_type TSRMLS_CC);
571577
object_properties_init(&intern->std, class_type);
572578

579+
intern->created_by_pid = (int) getpid();
580+
573581
#if PHP_VERSION_ID >= 70000
574582
intern->std.handlers = &php_phongo_handler_server;
575583

src/MongoDB/Session.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ static void php_phongo_session_free_object(phongo_free_object_arg* object TSRMLS
450450

451451
zend_object_std_dtor(&intern->std TSRMLS_CC);
452452

453+
PHONGO_CLIENT_RESET_IF_CHIlD_PID(mongoc_client_session_get_client(intern->client_session), intern->created_by_pid);
454+
453455
if (intern->client_session) {
454456
mongoc_client_session_destroy(intern->client_session);
455457
}
@@ -468,6 +470,8 @@ static phongo_create_object_retval php_phongo_session_create_object(zend_class_e
468470
zend_object_std_init(&intern->std, class_type TSRMLS_CC);
469471
object_properties_init(&intern->std, class_type);
470472

473+
intern->created_by_pid = (int) getpid();
474+
471475
#if PHP_VERSION_ID >= 70000
472476
intern->std.handlers = &php_phongo_handler_session;
473477

tests/cursor/bug1274-001.phpt

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
--TEST--
2+
PHPC-1274: Cursor destruct should not kill cursor from parent process
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+
--FILE--
9+
<?php
10+
require_once __DIR__ . "/../utils/basic.inc";
11+
12+
class CommandLogger implements MongoDB\Driver\Monitoring\CommandSubscriber
13+
{
14+
public function commandStarted(MongoDB\Driver\Monitoring\CommandStartedEvent $event)
15+
{
16+
$command = $event->getCommand();
17+
18+
if ($event->getCommandName() === 'find') {
19+
printf("find command specifies batchSize: %d\n", $command->batchSize);
20+
}
21+
22+
if ($event->getCommandName() === 'getMore') {
23+
printf("getMore command specifies batchSize: %d\n", $command->batchSize);
24+
}
25+
}
26+
27+
public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event)
28+
{
29+
}
30+
31+
public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event)
32+
{
33+
}
34+
}
35+
36+
MongoDB\Driver\Monitoring\addSubscriber(new CommandLogger);
37+
38+
$manager = new MongoDB\Driver\Manager(URI);
39+
40+
$bulk = new MongoDB\Driver\BulkWrite();
41+
$bulk->insert(['x' => 1]);
42+
$bulk->insert(['x' => 2]);
43+
$bulk->insert(['x' => 3]);
44+
$manager->executeBulkWrite(NS, $bulk);
45+
46+
$query = new MongoDB\Driver\Query([], ['batchSize' => 2]);
47+
$cursor = $manager->executeQuery(NS, $query);
48+
49+
$parentPid = getmypid();
50+
$childPid = pcntl_fork();
51+
52+
if ($childPid === 0) {
53+
echo "Child exits\n";
54+
exit;
55+
}
56+
57+
if ($childPid > 0) {
58+
$waitPid = pcntl_waitpid($childPid, $status);
59+
60+
if ($waitPid === $childPid) {
61+
echo "Parent waited for child to exit\n";
62+
}
63+
64+
printf("Parent fully iterated cursor for %d documents\n", iterator_count($cursor));
65+
}
66+
67+
?>
68+
===DONE===
69+
<?php exit(0); ?>
70+
--EXPECT--
71+
find command specifies batchSize: 2
72+
Child exits
73+
Parent waited for child to exit
74+
getMore command specifies batchSize: 2
75+
Parent fully iterated cursor for 3 documents
76+
===DONE===

tests/cursor/bug1274-002.phpt

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
--TEST--
2+
PHPC-1274: Child process cannot iterate cursor from parent process
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+
--FILE--
9+
<?php
10+
require_once __DIR__ . "/../utils/basic.inc";
11+
12+
class CommandLogger implements MongoDB\Driver\Monitoring\CommandSubscriber
13+
{
14+
public function commandStarted(MongoDB\Driver\Monitoring\CommandStartedEvent $event)
15+
{
16+
$command = $event->getCommand();
17+
18+
if ($event->getCommandName() === 'find') {
19+
printf("find command specifies batchSize: %d\n", $command->batchSize);
20+
}
21+
22+
if ($event->getCommandName() === 'getMore') {
23+
printf("getMore command specifies batchSize: %d\n", $command->batchSize);
24+
}
25+
}
26+
27+
public function commandSucceeded(MongoDB\Driver\Monitoring\CommandSucceededEvent $event)
28+
{
29+
}
30+
31+
public function commandFailed(MongoDB\Driver\Monitoring\CommandFailedEvent $event)
32+
{
33+
}
34+
}
35+
36+
MongoDB\Driver\Monitoring\addSubscriber(new CommandLogger);
37+
38+
$manager = new MongoDB\Driver\Manager(URI);
39+
40+
$bulk = new MongoDB\Driver\BulkWrite();
41+
$bulk->insert(['x' => 1]);
42+
$bulk->insert(['x' => 2]);
43+
$bulk->insert(['x' => 3]);
44+
$manager->executeBulkWrite(NS, $bulk);
45+
46+
$query = new MongoDB\Driver\Query([], ['batchSize' => 2]);
47+
$cursor = $manager->executeQuery(NS, $query);
48+
49+
$parentPid = getmypid();
50+
$childPid = pcntl_fork();
51+
52+
if ($childPid === 0) {
53+
echo throws(function() use ($cursor) {
54+
printf("Child fully iterated cursor for %d documents\n", iterator_count($cursor));
55+
}, 'MongoDB\Driver\Exception\RuntimeException'), "\n";
56+
echo "Child exits\n";
57+
exit;
58+
}
59+
60+
if ($childPid > 0) {
61+
$waitPid = pcntl_waitpid($childPid, $status);
62+
63+
if ($waitPid === $childPid) {
64+
echo "Parent waited for child to exit\n";
65+
}
66+
67+
printf("Parent fully iterated cursor for %d documents\n", iterator_count($cursor));
68+
}
69+
70+
?>
71+
===DONE===
72+
<?php exit(0); ?>
73+
--EXPECT--
74+
find command specifies batchSize: 2
75+
OK: Got MongoDB\Driver\Exception\RuntimeException
76+
Cannot advance cursor after client reset
77+
Child exits
78+
Parent waited for child to exit
79+
getMore command specifies batchSize: 2
80+
Parent fully iterated cursor for 3 documents
81+
===DONE===

0 commit comments

Comments
 (0)