Skip to content

Commit 81d6af7

Browse files
committed
CXX-813 Fix memory errors exposed by ASAN and Valgrind
- We can't assume that libmongoc has automatically called init/cleanup for us. It only does that on some platforms. That makes it mandatory to have an instance object. Many of our tests didn't do that, and it would be painful for users. Set things up so that an instance is implicitly created as needed, and provide ways to retrieve the instance. This required moving the tests for instance to its own process, among other nuisances. - The collection::create_index method was not cleaning up the keys that had been allocated by libbson. Add the needed bson_free call. - The collection::distinct method was not cleaning up the temorary database object that it constructs. Add the needed database_destroy call. - Fix some mocks so that they write to the bson_error_t out parameter when returning a non-successful code, as otherwise these lead to read-from-uninit errors when we promote the uninitialized bson_error_t to an exception.
1 parent aeb4f2c commit 81d6af7

File tree

12 files changed

+140
-24
lines changed

12 files changed

+140
-24
lines changed

.mci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ tasks:
116116
make
117117
${test_params} ./src/bsoncxx/test/test_bson
118118
${test_params} ./src/mongocxx/test/test_driver
119+
${test_params} ./src/mongocxx/test/test_instance
119120
make run-examples
120121
- func: "stop_mongod"
121122

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,8 @@ script:
7878
# Run mongocxx tests with catch
7979
- ./src/mongocxx/test/test_driver
8080

81+
# Run mongocxx instance tests with catch
82+
- ./src/mongocxx/test/test_instance
83+
8184
# Run all examples
8285
- make run-examples

src/mongocxx/client.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <mongocxx/exception/private/error_category.hpp>
2424
#include <mongocxx/exception/private/error_code.hpp>
2525
#include <mongocxx/exception/private/mongoc_error.hpp>
26+
#include <mongocxx/instance.hpp>
2627
#include <mongocxx/private/client.hpp>
2728
#include <mongocxx/private/read_concern.hpp>
2829
#include <mongocxx/private/read_preference.hpp>
@@ -36,7 +37,8 @@ MONGOCXX_INLINE_NAMESPACE_BEGIN
3637
client::client() noexcept = default;
3738

3839
client::client(const class uri& uri, const options::client& options)
39-
: _impl(stdx::make_unique<impl>(libmongoc::client_new_from_uri(uri._impl->uri_t))) {
40+
: _instance(&instance::current()),
41+
_impl(stdx::make_unique<impl>(libmongoc::client_new_from_uri(uri._impl->uri_t))) {
4042
if (options.ssl_opts()) {
4143
#if defined(MONGOC_HAVE_SSL)
4244
auto mongoc_opts = options::make_ssl_opts(*options.ssl_opts());
@@ -47,8 +49,9 @@ client::client(const class uri& uri, const options::client& options)
4749
}
4850
}
4951

50-
client::client(void* implementation)
51-
: _impl{stdx::make_unique<impl>(static_cast<::mongoc_client_t*>(implementation))} {
52+
client::client(class instance* instance, void* implementation)
53+
: _instance(instance),
54+
_impl{stdx::make_unique<impl>(static_cast<::mongoc_client_t*>(implementation))} {
5255
}
5356

5457
client::client(client&&) noexcept = default;

src/mongocxx/client.hpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
namespace mongocxx {
3131
MONGOCXX_INLINE_NAMESPACE_BEGIN
3232

33+
class instance;
34+
3335
///
3436
/// Class representing a client connection to MongoDB.
3537
///
@@ -80,6 +82,11 @@ class MONGOCXX_API client {
8082
///
8183
~client();
8284

85+
///
86+
/// Obtain the instance associated with this client
87+
///
88+
MONGOCXX_INLINE class instance& instance() const noexcept;
89+
8390
///
8491
/// Returns true if the client is valid, meaning it was not default constructed
8592
/// or moved from.
@@ -202,16 +209,21 @@ class MONGOCXX_API client {
202209
friend class database;
203210
friend class pool;
204211

205-
MONGOCXX_PRIVATE explicit client(void* implementation);
212+
MONGOCXX_PRIVATE explicit client(class instance*, void* implementation);
206213

207214
class MONGOCXX_PRIVATE impl;
208215

209216
MONGOCXX_PRIVATE impl& _get_impl();
210217
MONGOCXX_PRIVATE const impl& _get_impl() const;
211218

219+
class instance* _instance;
212220
std::unique_ptr<impl> _impl;
213221
};
214222

223+
MONGOCXX_INLINE instance& client::instance() const noexcept {
224+
return *_instance;
225+
}
226+
215227
MONGOCXX_INLINE database client::operator[](bsoncxx::string::view_or_value name) const & {
216228
return database(name);
217229
}

src/mongocxx/collection.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -702,9 +702,16 @@ bsoncxx::document::value collection::create_index(view_or_value keys,
702702
return bsoncxx::builder::stream::document{} << "name" << *options.name()
703703
<< bsoncxx::builder::stream::finalize;
704704
} else {
705+
706+
const auto keys = libmongoc::collection_keys_to_index_string(bson_keys.bson());
707+
708+
const auto clean_keys = make_guard([&]{
709+
bson_free(keys);
710+
});
711+
705712
return bsoncxx::builder::stream::document{}
706713
<< "name"
707-
<< std::string{libmongoc::collection_keys_to_index_string(bson_keys.bson())}
714+
<< keys
708715
<< bsoncxx::builder::stream::finalize;
709716
}
710717
}
@@ -724,6 +731,10 @@ cursor collection::distinct(bsoncxx::string::view_or_value field_name, view_or_v
724731
auto database = libmongoc::client_get_database(_get_impl().client_impl->client_t,
725732
_get_impl().database_name.c_str());
726733

734+
const auto cleanup_database = make_guard([&] {
735+
libmongoc::database_destroy(database);
736+
});
737+
727738
auto result = libmongoc::database_command(database, MONGOC_QUERY_NONE, 0, 0, 0,
728739
command_bson.bson(), NULL, NULL);
729740

src/mongocxx/instance.cpp

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <mongocxx/instance.hpp>
1616

17+
#include <mutex>
1718
#include <utility>
1819

1920
#include <bsoncxx/stdx/make_unique.hpp>
@@ -56,6 +57,10 @@ void user_log_handler(::mongoc_log_level_t mongoc_log_level, const char *log_dom
5657
stdx::string_view{log_domain}, stdx::string_view{message});
5758
}
5859

60+
std::recursive_mutex instance_mutex;
61+
instance* current_instance = nullptr;
62+
std::unique_ptr<instance> global_instance;
63+
5964
} // namespace
6065

6166
class instance::impl {
@@ -84,13 +89,33 @@ class instance::impl {
8489
instance::instance() : instance(nullptr) {
8590
}
8691

87-
instance::instance(std::unique_ptr<logger> logger)
88-
: _impl(stdx::make_unique<impl>(std::move(logger))) {
92+
instance::instance(std::unique_ptr<logger> logger) {
93+
std::lock_guard<std::recursive_mutex> lock(instance_mutex);
94+
if (current_instance)
95+
std::abort();
96+
_impl = stdx::make_unique<impl>(std::move(logger));
97+
current_instance = this;
8998
}
9099

91100
instance::instance(instance &&) noexcept = default;
92101
instance &instance::operator=(instance &&) noexcept = default;
93-
instance::~instance() = default;
102+
103+
instance::~instance() {
104+
std::lock_guard<std::recursive_mutex> lock(instance_mutex);
105+
if (current_instance != this)
106+
std::abort();
107+
_impl.reset();
108+
current_instance = nullptr;
109+
}
110+
111+
instance& instance::current() {
112+
std::lock_guard<std::recursive_mutex> lock(instance_mutex);
113+
if (!current_instance) {
114+
global_instance.reset(new instance);
115+
current_instance = global_instance.get();
116+
}
117+
return *current_instance;
118+
}
94119

95120
MONGOCXX_INLINE_NAMESPACE_END
96121
} // namespace mongocxx

src/mongocxx/instance.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class logger;
2626
///
2727
/// Class representing an instance of the MongoDB driver.
2828
///
29-
/// Life cycle: An instance of the driver *MUST* be kept around.
29+
/// Life cycle: A unique instance of the driver *MUST* be kept around.
3030
///
3131
class MONGOCXX_API instance {
3232
public:
@@ -56,6 +56,14 @@ class MONGOCXX_API instance {
5656
///
5757
~instance();
5858

59+
///
60+
/// Returns the current unique instance of the driver. If an
61+
/// instance was explicitly created, that will be returned. If no
62+
/// instance has yet been created, a default instance will be
63+
/// constructed and returned.
64+
///
65+
static instance& current();
66+
5967
private:
6068
class MONGOCXX_PRIVATE impl;
6169
std::unique_ptr<impl> _impl;

src/mongocxx/pool.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <mongocxx/exception/error_code.hpp>
2525
#include <mongocxx/exception/exception.hpp>
2626
#include <mongocxx/exception/private/error_code.hpp>
27+
#include <mongocxx/instance.hpp>
2728
#include <mongocxx/private/client.hpp>
2829
#include <mongocxx/private/pool.hpp>
2930
#include <mongocxx/private/ssl.hpp>
@@ -42,7 +43,8 @@ void pool::_release(client* client) {
4243
pool::~pool() = default;
4344

4445
pool::pool(const uri& mongodb_uri, stdx::optional<options::ssl> ssl_options)
45-
: _impl{stdx::make_unique<impl>(libmongoc::client_pool_new(mongodb_uri._impl->uri_t),
46+
: _instance(&instance::current()),
47+
_impl{stdx::make_unique<impl>(libmongoc::client_pool_new(mongodb_uri._impl->uri_t),
4648
std::move(ssl_options))} {
4749
if (_impl->ssl_options) {
4850
#if defined(MONGOC_HAVE_SSL)
@@ -58,15 +60,15 @@ pool::pool(const uri& mongodb_uri, stdx::optional<options::ssl> ssl_options)
5860
}
5961

6062
pool::entry pool::acquire() {
61-
return entry(new client(libmongoc::client_pool_pop(_impl->client_pool_t)),
63+
return entry(new client(&instance(), libmongoc::client_pool_pop(_impl->client_pool_t)),
6264
[this](client* client) { _release(client); });
6365
}
6466

6567
stdx::optional<pool::entry> pool::try_acquire() {
6668
auto cli = libmongoc::client_pool_try_pop(_impl->client_pool_t);
6769
if (!cli) return stdx::nullopt;
6870

69-
return pool::entry{new client(cli), [this](client* client) { _release(client); }};
71+
return pool::entry{new client(&instance(), cli), [this](client* client) { _release(client); }};
7072
}
7173

7274
MONGOCXX_INLINE_NAMESPACE_END

src/mongocxx/pool.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace mongocxx {
2929
MONGOCXX_INLINE_NAMESPACE_BEGIN
3030

3131
class client;
32+
class instance;
3233

3334
///
3435
/// A pool of @c client objects associated with a MongoDB deployment.
@@ -64,6 +65,11 @@ class MONGOCXX_API pool {
6465
///
6566
~pool();
6667

68+
///
69+
/// Obtain the driver instance associated with this pool
70+
///
71+
MONGOCXX_INLINE class instance& instance() const noexcept;
72+
6773
///
6874
/// An entry is a handle on a @c client object acquired via the pool.
6975
///
@@ -88,9 +94,15 @@ class MONGOCXX_API pool {
8894
MONGOCXX_PRIVATE void _release(client* client);
8995

9096
class MONGOCXX_PRIVATE impl;
97+
98+
class instance* _instance;
9199
const std::unique_ptr<impl> _impl;
92100
};
93101

102+
MONGOCXX_INLINE instance& pool::instance() const noexcept {
103+
return *_instance;
104+
}
105+
94106
MONGOCXX_INLINE_NAMESPACE_END
95107
} // namespace mongocxx
96108

src/mongocxx/test/CMakeLists.txt

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
include_directories(
16+
${THIRD_PARTY_SOURCE_DIR}/catch/include
17+
)
18+
19+
link_directories(${LIBMONGOC_LIBRARY_DIRS} ${LIBBSON_LIBRARY_DIRS})
20+
1521
set(mongocxx_test_sources
1622
CMakeLists.txt
1723
bulk_write.cpp
@@ -20,7 +26,6 @@ set(mongocxx_test_sources
2026
collection_mocked.cpp
2127
database.cpp
2228
hint.cpp
23-
instance.cpp
2429
model/update_many.cpp
2530
options/aggregate.cpp
2631
options/create_collection.cpp
@@ -45,21 +50,23 @@ set(mongocxx_test_sources
4550
write_concern.cpp
4651
)
4752

48-
include_directories(
49-
${THIRD_PARTY_SOURCE_DIR}/catch/include
50-
)
51-
52-
link_directories(${LIBMONGOC_LIBRARY_DIRS} ${LIBBSON_LIBRARY_DIRS})
53-
5453
add_executable(test_driver
5554
${THIRD_PARTY_SOURCE_DIR}/catch/main.cpp
5655
${mongocxx_test_sources}
5756
)
5857

5958
target_link_libraries(test_driver mongocxx_mocked)
6059

60+
add_executable(test_instance
61+
${THIRD_PARTY_SOURCE_DIR}/catch/main.cpp
62+
instance.cpp
63+
)
64+
65+
target_link_libraries(test_instance mongocxx_mocked)
66+
6167
if (WIN32)
6268
target_compile_options(test_driver PRIVATE /bigobj)
6369
endif()
6470

6571
add_test(driver test_driver)
72+
add_test(instance test_instance)

0 commit comments

Comments
 (0)