While testing the session pinning implementation for the PHP driver, I uncovered a case where we attempt to free an uninitialised BSON reply. This not only affects the PHP driver, but also libmongoc itself if _mongoc_client_command_with_opts ends up being called without a reply object.
In _mongoc_client_command_with_opts it is assumed that the reply object is initialised if no server_stream was opened. Later in the function, the reply object is freed if no reply was passed to the function. If a reply was passed but not initialised, an empty bson object is created.
However, the assumption that not getting back a server_stream means an initialised reply (see comment in line 1941) is wrong in one case: passing a serverId in the options along with a session that is pinned to a different server triggers an error condition in mongoc_cluster_stream_for_server, which then returns NULL. This leads us to an uninitialised reply which is assumed to be initialised by _mongoc_client_command_with_opts. I believe that mongoc_cluster_stream_for_server needs a call to _mongoc_bson_init_if_set (reply) if the server pinning check fails.
To reproduce this, you need to pin a session to a server, then call mongoc_client_command_with_opts (or any other command function) with the session, as well as a server ID that is different from the one that the session is pinned to. Not passing a reply pointer to the function triggers the bug in libmongoc.
In the case of the PHP driver, a reply pointer is passed, which phongo_execute_command expects to be initialised during _mongoc_client_command_with_opts and subsequently frees it. This is consistent with the documentation:
reply is always initialized, and must be freed with bson_destroy().
- is depended on by
-
PHPC-1290 Support mongos pinning for sharded transactions
- Closed