-
Type: Bug
-
Resolution: Done
-
Priority: Critical - P2
-
Affects Version/s: 1.1.8
-
Component/s: None
-
None
-
Environment:PHP 5.5.37 (possible other PHP 5.x, but not PHP 7)
mongo-php-adapter uses generators to wrap a MongoDB\Driver\Cursor and implement the legacy driver's MongoCursor API:
On PHP 5.x, this can lead to use-after-free segfaults with mongoc_client_t. Tracing revealed that MongoDB\Driver\Manager may still be freed before MongoDB\Driver\Cursor, which was something previously addressed in PHPC-671; however, that issue did not involve generators.
As with PHPC-671, we should also confirm if a use-after-free crash can be triggered with just a Server object.
From jwage:
I was able to reproduce the segfault locally with this script:
<?php use Symfony\Component\ClassLoader\ApcClassLoader; $loader = require_once __DIR__.'/opensky/bootstrap.php.cache'; $loader = new ApcClassLoader('devo', $loader); $loader->register(true); $mongo = new \MongoClient('mongodb://localhost:27017'); $db = $mongo->selectDB('opensky_devo'); $collection = $db->selectCollection('sellables'); $cursor = $collection->find([], []); $cursor->batchSize(2); $cursor->limit(3); $cursor->next();If I call $cursor->next() exactly 4 times (2 batches) then it does not crash.
Here is the backtrace:
#0 mongoc_set_id_cmp (a_=0x7fff842d13a0, b_=0xeb8) at /var/tmp/mongodb/src/libmongoc/src/mongoc/mongoc-set.c:49 a = 0x7fff842d13a0 b = 0xeb8 #1 0x0000003572033f80 in bsearch (key=0x7fff842d13a0, base=0xb38, nmemb=<value optimized out>, size=16, compar=0x7f7f99d8b800 <mongoc_set_id_cmp>) at bsearch.c:38 l = <value optimized out> u = 113 idx = 56 p = 0xeb8 comparison = <value optimized out> #2 0x00007f7f99d8baf2 in mongoc_set_get (set=<value optimized out>, id=<value optimized out>) at /var/tmp/mongodb/src/libmongoc/src/mongoc/mongoc-set.c:114 ptr = <value optimized out> key = {id = 1, item = 0x14d6e00} #3 0x00007f7f99d93817 in mongoc_topology_description_server_by_id (description=<value optimized out>, id=1, error=0x0) at /var/tmp/mongodb/src/libmongoc/src/mongoc/mongoc-topology-description.c:557 sd = <value optimized out> __func__ = "mongoc_topology_description_server_by_id" #4 0x00007f7f99d914db in mongoc_topology_server_by_id (topology=0x7f7f8c7f0c40, id=1, error=0x0) at /var/tmp/mongodb/src/libmongoc/src/mongoc/mongoc-topology.c:571 sd = <value optimized out> #5 0x00007f7f99d777c5 in mongoc_cluster_stream_for_server (cluster=0x7f7f8c7f5cc0, server_id=1, reconnect_ok=false, error=0x0) at /var/tmp/mongodb/src/libmongoc/src/mongoc/mongoc-cluster.c:1418 topology = <value optimized out> sd = <value optimized out> server_stream = 0x0 __func__ = "mongoc_cluster_stream_for_server" #6 0x00007f7f99d71f8e in _mongoc_client_kill_cursor (client=0x7f7f8c7f5ca8, server_id=1, cursor_id=584888672639, db=0x7fff842d15e0 "opensky_devo", collection=0x7f7f8c837535 "sellables") at /var/tmp/mongodb/src/libmongoc/src/mongoc/mongoc-client.c:1282 server_stream = <value optimized out> __func__ = "_mongoc_client_kill_cursor" #7 0x00007f7f99d7e0db in _mongoc_cursor_destroy (cursor=0x7f7f8c8373e8) at /var/tmp/mongodb/src/libmongoc/src/mongoc/mongoc-cursor.c:273 db = "opensky_devo\000\000\000\000\340\001\000\000\000\000\000\000Ҧ[\000\000\000\000\000\320\004\000\000\000\000\000\000Ҧ[\000\000\000\000\000\250~\223\001\000\000\000\000Pʐ\001\000\000\000\000\020\002\000\000\000\000\000\000Ҧ[\000\000\000\000\000X\000\000\000\000\000\000\000Ҧ[\000\000\000\000\000\220\000\000\000\000\000\000\000Ҧ[\000\000\000\000\000X%\200\214\177\177\000\000\026\000\000\000\000\000\000" __func__ = "_mongoc_cursor_destroy" #8 0x00007f7f99d7e198 in mongoc_cursor_destroy (cursor=0x7f7f8c8373e8) at /var/tmp/mongodb/src/libmongoc/src/mongoc/mongoc-cursor.c:249 ---Type <return> to continue, or q <return> to quit--- __func__ = "mongoc_cursor_destroy" #9 0x00007f7f99d9ebe2 in php_phongo_cursor_free (cursor=0x7f7f8c844a20) at /var/tmp/mongodb/php_phongo.c:2142 No locals. #10 0x00007f7f99d50a41 in php_phongo_cursor_free_object (object=0x7f7f8c844a20) at /var/tmp/mongodb/src/MongoDB/Cursor.c:226 intern = 0x7f7f8c844a20 #11 0x000000000060bbac in zend_objects_store_free_object_storage () No symbol table info available. #12 0x00000000005d4493 in ?? () No symbol table info available. #13 0x00000000005e2652 in ?? () No symbol table info available. #14 0x00000000005820d1 in php_request_shutdown () No symbol table info available. #15 0x0000000000693b8f in ?? () No symbol table info available. #16 0x0000000000694f98 in ?? () No symbol table info available. #17 0x000000357201ed5d in __libc_start_main (main=0x694910, argc=2, ubp_av=0x7fff842d2fd8, init=<value optimized out>, fini=<value optimized out>, rtld_fini=<value optimized out>, stack_end=0x7fff842d2fc8) at libc-start.c:226 result = <value optimized out> unwind_buf = {cancel_jmp_buf = {{jmp_buf = {0, -4001403079134328888, 4331088, 140735410941904, 0, 0, 4001203369062123464, -4029933203728152632}, mask_was_saved = 0}}, priv = {pad = {0x0, 0x0, 0x0, 0x1}, data = {prev = 0x0, cleanup = 0x0, canceltype = 0}}} not_first_call = <value optimized out> #18 0x0000000000421679 in _start () No symbol table info available.
The following script reproduces a crash and may be useful for creating a test case:
function wrapCursor(MongoDB\Driver\Cursor $cursor) { foreach ($cursor as $key => $value) { yield $key => $value; } } $manager = new MongoDB\Driver\Manager(STANDALONE); $bulk = new MongoDB\Driver\BulkWrite(); $bulk->insert(['_id' => 1]); $bulk->insert(['_id' => 2]); $bulk->insert(['_id' => 3]); $manager->executeBulkWrite(NS, $bulk); $cursor = $manager->executeQuery(NS, new MongoDB\Driver\Query([], ['batchSize' => 2])); $generator = wrapCursor($cursor); foreach ($generator as $value) { exit(0); }
At present, the work-around for this crash is to ensure that the cursor is exhausted (i.e. all batches are fetched from the server), which will prevent libmongoc from killing it. For PHPC-671, a crash could be avoided by unsetting the cursor before the manager instance, but that may not apply if the cursor is within a generator.
- depends on
-
PHPC-433 Persist topology state between requests
- Closed
- is related to
-
PHPC-671 Avoid mongoc_client_t use-after-free by Cursor and Server
- Closed
- related to
-
PHPC-733 Possible mongoc_client_t use-after-free with shutdown functions
- Closed
-
PHPC-950 Skip APM callbacks if subscriber HashTable is uninitialized
- Closed
- links to