Skip to content

Commit 9bcabe2

Browse files
committed
CDRIVER-755 leaks in _mongoc_cluster_ismaster
mongoc_cluster_node_t.tags and replSet are overwritten with the new ismaster response, without being destroyed first. If the node represents a replica set member then its replica set name will be leaked. Same for "tags" if the tags document is large enough to spill to heap.
1 parent a967e8e commit 9bcabe2

File tree

5 files changed

+233
-75
lines changed

5 files changed

+233
-75
lines changed

src/mongoc/mongoc-cluster.c

Lines changed: 116 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,9 @@ _mongoc_cluster_ismaster (mongoc_cluster_t *cluster,
10771077
bson_free (node->replSet);
10781078
node->replSet = NULL;
10791079

1080+
bson_destroy (&node->tags);
1081+
bson_init (&node->tags);
1082+
10801083
if (bson_iter_init_find_case (&iter, &reply, "isMaster") &&
10811084
BSON_ITER_HOLDS_BOOL (&iter) &&
10821085
bson_iter_bool (&iter)) {
@@ -2067,6 +2070,95 @@ host_list_destroy (mongoc_host_list_t *hl)
20672070
}
20682071

20692072

2073+
static uint32_t
2074+
list_len (const mongoc_list_t *list)
2075+
{
2076+
const mongoc_list_t *liter;
2077+
uint32_t i;
2078+
2079+
for (liter = list, i = 0; liter; liter = liter->next, i++) {}
2080+
2081+
return i;
2082+
}
2083+
2084+
2085+
static mongoc_cluster_node_t *
2086+
save_nodes (mongoc_cluster_node_t *nodes,
2087+
size_t nodes_len)
2088+
{
2089+
mongoc_cluster_node_t *saved_nodes;
2090+
int i;
2091+
2092+
saved_nodes = bson_malloc0 (nodes_len * sizeof (mongoc_cluster_node_t));
2093+
2094+
for (i = 0; i < nodes_len; i++) {
2095+
_mongoc_cluster_node_init (&saved_nodes[i]);
2096+
if (nodes[i].stream) {
2097+
saved_nodes[i].host = nodes[i].host;
2098+
saved_nodes[i].stream = nodes[i].stream;
2099+
nodes [i].stream = NULL;
2100+
}
2101+
}
2102+
2103+
return saved_nodes;
2104+
}
2105+
2106+
2107+
2108+
static mongoc_cluster_node_t *
2109+
nodes_list_new (size_t nodes_len)
2110+
{
2111+
mongoc_cluster_node_t *nodes;
2112+
size_t i;
2113+
2114+
nodes = bson_malloc0 (sizeof (mongoc_cluster_node_t) * nodes_len);
2115+
for (i = 0; i < nodes_len; i++) {
2116+
_mongoc_cluster_node_init (&nodes[i]);
2117+
/* guard against counter errors, see CDRIVER-695 */
2118+
nodes[i].valid = false;
2119+
}
2120+
2121+
return nodes;
2122+
}
2123+
2124+
2125+
static mongoc_stream_t *
2126+
restore_stream (mongoc_cluster_node_t *saved_nodes,
2127+
size_t saved_nodes_len,
2128+
char *host_and_port)
2129+
{
2130+
size_t i;
2131+
mongoc_stream_t *stream;
2132+
2133+
for (i = 0; i < saved_nodes_len; i++) {
2134+
if (0 == strcmp (saved_nodes [i].host.host_and_port,
2135+
host_and_port)) {
2136+
stream = saved_nodes [i].stream;
2137+
saved_nodes [i].stream = NULL;
2138+
return stream;
2139+
}
2140+
}
2141+
2142+
return NULL;
2143+
}
2144+
2145+
2146+
static void
2147+
destroy_nodes_list (mongoc_cluster_node_t **nodes_ptr,
2148+
size_t nodes_len)
2149+
{
2150+
mongoc_cluster_node_t *nodes = *nodes_ptr;
2151+
size_t i;
2152+
2153+
for (i = 0; i < nodes_len; i++) {
2154+
_mongoc_cluster_node_destroy (&nodes[i]);
2155+
}
2156+
2157+
bson_free (nodes);
2158+
*nodes_ptr = NULL;
2159+
}
2160+
2161+
20702162
/*
20712163
*--------------------------------------------------------------------------
20722164
*
@@ -2098,26 +2190,23 @@ _mongoc_cluster_reconnect_replica_set (mongoc_cluster_t *cluster,
20982190
const mongoc_host_list_t *iter;
20992191
mongoc_host_list_t *failed_hosts = NULL;
21002192
mongoc_cluster_node_t node;
2101-
mongoc_cluster_node_t *saved_nodes;
2102-
size_t saved_nodes_len;
2193+
mongoc_cluster_node_t *saved_nodes = NULL;
2194+
size_t saved_nodes_len = 0;
2195+
uint32_t peers_len;
21032196
mongoc_host_list_t host;
21042197
mongoc_stream_t *stream;
2105-
mongoc_list_t *list;
2198+
mongoc_list_t *list = NULL;
21062199
mongoc_list_t *liter;
21072200
int32_t ping;
21082201
const char *replSet;
21092202
uint32_t i;
2110-
uint32_t j;
21112203
bool rval = false;
21122204

21132205
ENTRY;
21142206

21152207
BSON_ASSERT(cluster);
21162208
BSON_ASSERT(cluster->mode == MONGOC_CLUSTER_REPLICA_SET);
21172209

2118-
saved_nodes = bson_malloc0(cluster->nodes_len * sizeof(*saved_nodes));
2119-
saved_nodes_len = cluster->nodes_len;
2120-
21212210
MONGOC_DEBUG("Reconnecting to replica set.");
21222211

21232212
if (!(hosts = mongoc_uri_get_hosts(cluster->uri))) {
@@ -2135,26 +2224,18 @@ _mongoc_cluster_reconnect_replica_set (mongoc_cluster_t *cluster,
21352224
* Replica Set (Re)Connection Strategy
21362225
* ===================================
21372226
*
2138-
* First we break all existing connections. This may change.
2227+
* First we save existing connections.
21392228
*
21402229
* To perform the replica set connection, we connect to each of the
21412230
* pre-configured replicaSet nodes. (There may in fact only be one).
21422231
*
2143-
* TODO: We should perform this initial connection in parallel.
2144-
*
21452232
* Using the result of an "isMaster" on each of these nodes, we can
2146-
* prime the cluster nodes we want to connect to.
2147-
*
2148-
* We then connect to all of these nodes in parallel. Once we have
2149-
* all of the working nodes established, we can complete the process.
2233+
* prime the cluster nodes we want to connect to. Then we connect to
2234+
* them.
21502235
*
21512236
* We return true if any of the connections were successful, however
21522237
* we must update the cluster health appropriately so that callers
21532238
* that need a PRIMARY node can force reconnection.
2154-
*
2155-
* TODO: At some point in the future, we will need to authenticate
2156-
* before calling an "isMaster". But that is dependent on a
2157-
* few server "features" first.
21582239
*/
21592240

21602241
cluster->last_reconnect = bson_get_monotonic_time();
@@ -2202,28 +2283,18 @@ _mongoc_cluster_reconnect_replica_set (mongoc_cluster_t *cluster,
22022283

22032284
/*
22042285
* To avoid reconnecting to all of the peers, we will save the
2205-
* functional connections (and save their ping times) so that
2206-
* we don't waste time doing that again.
2286+
* functional connections.
22072287
*/
22082288

2209-
for (i = 0; i < cluster->nodes_len; i++) {
2210-
if (cluster->nodes [i].stream) {
2211-
saved_nodes [i].host = cluster->nodes [i].host;
2212-
saved_nodes [i].stream = cluster->nodes [i].stream;
2213-
cluster->nodes [i].stream = NULL;
2214-
}
2215-
}
2289+
saved_nodes_len = cluster->nodes_len;
2290+
saved_nodes = save_nodes (cluster->nodes, cluster->nodes_len);
22162291

2217-
/* reinitialize nodes with same length as peer list. */
2218-
for (liter = list, i = 0; liter; liter = liter->next, i++) {}
2219-
cluster->nodes = bson_realloc (cluster->nodes, sizeof (*cluster->nodes) * i);
2220-
cluster->nodes_len = i;
2292+
destroy_nodes_list (&cluster->nodes, cluster->nodes_len);
22212293

2222-
for (j = 0; j < i; j++) {
2223-
_mongoc_cluster_node_init (&cluster->nodes[j]);
2224-
/* guard against counter errors, see CDRIVER-695 */
2225-
cluster->nodes[j].valid = false;
2226-
}
2294+
/* reinitialize nodes with same length as peer list. */
2295+
peers_len = list_len(list);
2296+
cluster->nodes = nodes_list_new (peers_len);
2297+
cluster->nodes_len = peers_len;
22272298

22282299
for (liter = list, i = 0; liter; liter = liter->next) {
22292300
if (!_mongoc_host_list_from_string(&host, liter->data)) {
@@ -2237,15 +2308,10 @@ _mongoc_cluster_reconnect_replica_set (mongoc_cluster_t *cluster,
22372308
continue;
22382309
}
22392310

2240-
stream = NULL;
2241-
2242-
for (j = 0; j < saved_nodes_len; j++) {
2243-
if (0 == strcmp (saved_nodes [j].host.host_and_port,
2244-
host.host_and_port)) {
2245-
stream = saved_nodes [j].stream;
2246-
saved_nodes [j].stream = NULL;
2247-
}
2248-
}
2311+
/* NULLs the saved node's stream if found and returns it */
2312+
stream = restore_stream (saved_nodes,
2313+
saved_nodes_len,
2314+
host.host_and_port);
22492315

22502316
if (!stream) {
22512317
stream = _mongoc_client_create_stream (cluster->client, &host, error);
@@ -2300,20 +2366,6 @@ _mongoc_cluster_reconnect_replica_set (mongoc_cluster_t *cluster,
23002366
cluster->nodes_len = i;
23012367
}
23022368

2303-
_mongoc_list_foreach(list, (void(*)(void*,void*))bson_free, NULL);
2304-
_mongoc_list_destroy(list);
2305-
2306-
/*
2307-
* Cleanup all potential saved connections that were not used.
2308-
*/
2309-
2310-
for (j = 0; j < saved_nodes_len; j++) {
2311-
if (saved_nodes [j].stream) {
2312-
mongoc_stream_destroy (saved_nodes [j].stream);
2313-
saved_nodes [j].stream = NULL;
2314-
}
2315-
}
2316-
23172369
if (i == 0) {
23182370
bson_set_error(error,
23192371
MONGOC_ERROR_CLIENT,
@@ -2328,8 +2380,12 @@ _mongoc_cluster_reconnect_replica_set (mongoc_cluster_t *cluster,
23282380

23292381
CLEANUP:
23302382

2331-
bson_free(saved_nodes);
2383+
if (list) {
2384+
_mongoc_list_foreach (list, (void (*) (void *, void *)) bson_free, NULL);
2385+
_mongoc_list_destroy (list);
2386+
}
23322387

2388+
destroy_nodes_list (&saved_nodes, saved_nodes_len);
23332389
host_list_destroy (failed_hosts);
23342390

23352391
RETURN(rval);

tests/mock-server.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct _mock_server_t
5959

6060
const char *setName;
6161
const mongoc_host_list_t *hosts;
62+
const bson_t *tags;
6263

6364
bool stopping;
6465
bool stopped;
@@ -182,6 +183,10 @@ handle_ismaster (mock_server_t *server,
182183
bson_append_array_end (&reply_doc, &hosts_array);
183184
}
184185

186+
if (server->tags) {
187+
BSON_APPEND_DOCUMENT (&reply_doc, "tags", server->tags);
188+
}
189+
185190
mock_server_reply_simple (server, client, rpc, MONGOC_REPLY_NONE, &reply_doc);
186191

187192
bson_destroy (&reply_doc);
@@ -339,7 +344,8 @@ mock_server_new_rs (const char *address,
339344
const char *setName,
340345
bool isMaster,
341346
bool isSecondary,
342-
const mongoc_host_list_t *hosts)
347+
const mongoc_host_list_t *hosts,
348+
const bson_t *tags)
343349
{
344350
mock_server_t *server;
345351

@@ -369,6 +375,7 @@ mock_server_new_rs (const char *address,
369375

370376
server->setName = setName;
371377
server->hosts = hosts;
378+
server->tags = tags;
372379

373380
mongoc_mutex_init (&server->mutex);
374381
mongoc_cond_init (&server->cond);
@@ -387,7 +394,7 @@ mock_server_new (const char *address,
387394
void *handler_data)
388395
{
389396
return mock_server_new_rs (address, port, handler, handler_data,
390-
NULL, true, false, NULL);
397+
NULL, true, false, NULL, NULL);
391398
}
392399

393400

@@ -462,7 +469,7 @@ mock_server_run (mock_server_t *server)
462469
}
463470

464471
mongoc_mutex_lock (&server->mutex);
465-
mongoc_socket_close (server->sock);
472+
mongoc_socket_destroy (server->sock);
466473
server->sock = NULL;
467474
server->stopped = true;
468475
mongoc_cond_signal (&server->stopped_condition);

tests/mock-server.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ mock_server_t *mock_server_new_rs (const char *address,
4949
const char *setName,
5050
bool isMaster,
5151
bool isSecondary,
52-
const mongoc_host_list_t *hosts);
52+
const mongoc_host_list_t *hosts,
53+
const bson_t *tags);
5354
void mock_server_set_wire_version (mock_server_t *server,
5455
int32_t min_wire_version,
5556
int32_t max_wire_version);

tests/test-mongoc-client.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ test_seed_list (bool rs,
534534
hosts = mongoc_uri_get_hosts (uri);
535535

536536
server = mock_server_new_rs ("127.0.0.1", port, NULL, NULL,
537-
rs ? "rs" : NULL, true, false, hosts);
537+
rs ? "rs" : NULL, true, false, hosts, NULL);
538538

539539
mock_server_run_in_thread (server);
540540

@@ -648,7 +648,7 @@ test_recovering (void)
648648

649649
/* server is "recovering": not master, not secondary */
650650
server = mock_server_new_rs ("127.0.0.1", port, NULL, NULL,
651-
"rs", false, false, hosts);
651+
"rs", false, false, hosts, NULL);
652652

653653
mock_server_set_verbose (server, false);
654654
mock_server_run_in_thread (server);

0 commit comments

Comments
 (0)