diff options
author | Spencer Jackson <spencer.jackson@mongodb.com> | 2017-08-24 18:03:27 -0400 |
---|---|---|
committer | Spencer Jackson <spencer.jackson@mongodb.com> | 2017-08-31 10:24:52 -0400 |
commit | ebd0ec786e6986ecd407c48928e6866736675021 (patch) | |
tree | f5cc2e38086bab8ea445ec93acae7d0e8c7e6a08 | |
parent | 2730bd049022954bc7cd43392be20e6d54cf330d (diff) | |
download | mongo-ebd0ec786e6986ecd407c48928e6866736675021.tar.gz |
SERVER-30821: Allow configurable use of secure memory
-rw-r--r-- | src/mongo/base/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/base/secure_allocator.cpp | 3 | ||||
-rw-r--r-- | src/mongo/base/secure_allocator.h | 405 | ||||
-rw-r--r-- | src/mongo/base/secure_allocator_test.cpp | 51 | ||||
-rw-r--r-- | src/mongo/crypto/mechanism_scram.h | 9 | ||||
-rw-r--r-- | src/mongo/db/auth/authorization_manager.h | 2 | ||||
-rw-r--r-- | src/mongo/db/auth/sasl_plain_server_conversation.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/server_options.h | 14 | ||||
-rw-r--r-- | src/mongo/db/server_options_helpers.cpp | 5 |
9 files changed, 299 insertions, 195 deletions
diff --git a/src/mongo/base/SConscript b/src/mongo/base/SConscript index af40fdab72a..db569f842a5 100644 --- a/src/mongo/base/SConscript +++ b/src/mongo/base/SConscript @@ -73,6 +73,7 @@ env.Library( ], LIBDEPS=[ '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/server_options_core', '$BUILD_DIR/mongo/util/processinfo', '$BUILD_DIR/mongo/util/secure_zero_memory', ], diff --git a/src/mongo/base/secure_allocator.cpp b/src/mongo/base/secure_allocator.cpp index 982b24a12ef..68f8c190238 100644 --- a/src/mongo/base/secure_allocator.cpp +++ b/src/mongo/base/secure_allocator.cpp @@ -379,4 +379,7 @@ void deallocate(void* ptr, std::size_t bytes) { } } // namespace secure_allocator_details + +constexpr StringData SecureAllocatorAuthDomainTrait::DomainType; + } // namespace mongo diff --git a/src/mongo/base/secure_allocator.h b/src/mongo/base/secure_allocator.h index a642b47abb8..97b386c6168 100644 --- a/src/mongo/base/secure_allocator.h +++ b/src/mongo/base/secure_allocator.h @@ -37,6 +37,7 @@ #include <vector> #include "mongo/base/static_assert.h" +#include "mongo/db/server_options.h" #include "mongo/stdx/type_traits.h" #include "mongo/util/assert_util.h" @@ -47,6 +48,21 @@ namespace secure_allocator_details { void* allocate(std::size_t bytes, std::size_t alignOf); void deallocate(void* ptr, std::size_t bytes); +inline void* allocateWrapper(std::size_t bytes, std::size_t alignOf, bool secure) { + if (secure) { + return allocate(bytes, alignOf); + } else { + return mongoMalloc(bytes); + } +} +inline void deallocateWrapper(void* ptr, std::size_t bytes, bool secure) { + if (secure) { + return deallocate(ptr, bytes); + } else { + return free(ptr); + } +} + } // namespace secure_allocator_details /** @@ -70,221 +86,248 @@ void deallocate(void* ptr, std::size_t bytes); * * See also: http://howardhinnant.github.io/allocator_boilerplate.html */ -template <typename T> -struct SecureAllocator { - /** - * We only support trivially copyable types to avoid situations where the - * SecureAllocator is used in containers with complex types that do their - * own allocation. I.e. one could otherwise have a: - * - * std::vector<std::string, SecureAllocator<std::string>> - * - * where the vectors were stored securely, but the strings spilled to the - * heap - * - */ - MONGO_STATIC_ASSERT_MSG(std::is_trivially_copyable<T>::value, - "SecureAllocator can only be used with trivially copyable types"); - - // NOTE: The standard doesn't seem to require these, but libstdc++ - // definitly wants them. - using reference = T&; - using const_reference = const T&; - - - // NOTE: These members are defined in the same order as specified - // in the "Allocator Requirements" section of the standard. Please - // retain this ordering. - - using pointer = T*; - using const_pointer = const T*; - using void_pointer = void*; - using const_void_pointer = const void*; - using value_type = T; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - template <typename U> - struct rebind { - using other = SecureAllocator<U>; - }; +template <typename DomainTraits> +struct SecureAllocatorDomain { + template <typename T> + struct SecureAllocator { + /** + * We only support trivially copyable types to avoid situations where the + * SecureAllocator is used in containers with complex types that do their + * own allocation. I.e. one could otherwise have a: + * + * std::vector<std::string, SecureAllocator<std::string>> + * + * where the vectors were stored securely, but the strings spilled to the + * heap + * + */ + MONGO_STATIC_ASSERT_MSG(std::is_trivially_copyable<T>::value, + "SecureAllocator can only be used with trivially copyable types"); + + // NOTE: The standard doesn't seem to require these, but libstdc++ + // definitly wants them. + using reference = T&; + using const_reference = const T&; + + + // NOTE: These members are defined in the same order as specified + // in the "Allocator Requirements" section of the standard. Please + // retain this ordering. + + using pointer = T*; + using const_pointer = const T*; + using void_pointer = void*; + using const_void_pointer = const void*; + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + template <typename U> + struct rebind { + using other = SecureAllocator<U>; + }; + + pointer allocate(size_type n) { + return static_cast<pointer>(secure_allocator_details::allocateWrapper( + sizeof(value_type) * n, std::alignment_of<T>::value, DomainTraits::peg())); + } - pointer allocate(size_type n) { - return static_cast<pointer>(secure_allocator_details::allocate( - sizeof(value_type) * n, std::alignment_of<T>::value)); - } + pointer allocate(size_type n, const_void_pointer) { + return allocate(n); + } - pointer allocate(size_type n, const_void_pointer) { - return allocate(n); - } + void deallocate(pointer ptr, size_type n) { + return secure_allocator_details::deallocateWrapper( + static_cast<void*>(ptr), sizeof(value_type) * n, DomainTraits::peg()); + } - void deallocate(pointer ptr, size_type n) { - return secure_allocator_details::deallocate(static_cast<void*>(ptr), - sizeof(value_type) * n); - } + size_type max_size() { + return std::numeric_limits<size_type>::max(); + } - size_type max_size() { - return std::numeric_limits<size_type>::max(); - } + SecureAllocator() = default; - SecureAllocator() = default; + template <typename U> + SecureAllocator(const SecureAllocator<U>& other) {} - template <typename U> - SecureAllocator(const SecureAllocator<U>& other) {} + template <typename U, typename... Args> + void construct(U* p, Args&&... args) { + ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...); + } - template <typename U, typename... Args> - void construct(U* p, Args&&... args) { - ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...); - } + template <typename U> + void destroy(U* p) { + p->~U(); + } - template <typename U> - void destroy(U* p) { - p->~U(); - } + SecureAllocator select_on_container_copy_construction() { + // SecureAllocator is stateless, so just return a default + // constructed instance. + return SecureAllocator(); + } - SecureAllocator select_on_container_copy_construction() { - // SecureAllocator is stateless, so just return a default - // constructed instance. - return SecureAllocator(); + // For background: + // + // http://stackoverflow.com/questions/27471053/example-usage-of-propagate-on-container-move-assignment + // + // https://foonathan.github.io/blog/2015/10/05/allocatorawarecontainer-propagation-pitfalls.html + // + // This allocator is stateless, so we can avoid a runtime check + // (even though it would probably be optimized out based on the + // constrexpr-esque nature of our equality comparison operator), + // so we can set all of these to true. + using propagate_on_container_copy_assignment = std::true_type; + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; + + using is_always_equal = std::true_type; + }; + + template <typename T, typename U> + friend bool operator==(const SecureAllocator<T>& lhs, const SecureAllocator<U>& rhs) { + // Note: If you change this, you must re-evaluate the select_ and + // propagate_ methods and typedefs above. + return true; } - // For background: - // - // http://stackoverflow.com/questions/27471053/example-usage-of-propagate-on-container-move-assignment - // - // https://foonathan.github.io/blog/2015/10/05/allocatorawarecontainer-propagation-pitfalls.html - // - // This allocator is stateless, so we can avoid a runtime check - // (even though it would probably be optimized out based on the - // constrexpr-esque nature of our equality comparison operator), - // so we can set all of these to true. - using propagate_on_container_copy_assignment = std::true_type; - using propagate_on_container_move_assignment = std::true_type; - using propagate_on_container_swap = std::true_type; - - using is_always_equal = std::true_type; -}; + template <typename T, typename U> + friend bool operator!=(const SecureAllocator<T>& lhs, const SecureAllocator<U>& rhs) { + return !(lhs == rhs); + } -template <typename T, typename U> -bool operator==(const SecureAllocator<T>& lhs, const SecureAllocator<U>& rhs) { - // Note: If you change this, you must re-evaluate the select_ and - // propagate_ methods and typedefs above. - return true; -} + /** + * SecureHandle offers a smart pointer-ish interface to a type. + * + * It attempts to solve the problem of using container types with small object optimizations + * that + * might accidentally leave important data on the stack if they're too small to spill to the + * heap. + * It uses the secure allocator for allocations and deallocations. + * + * This type is meant to offer more value like semantics than a unique_ptr: + * - SecureHandle's are only default constructible if their T is + * - You can construct a SecureHandle like the underlying value: + * SecureHandle<Mytype> type(foo, bar baz); + * - SecureHandle's are copyable if their T's are + * - only moving can produce a non-usable T + * + * While still being cheap and convenient like a unique_ptr: + * - SecureHandle's move by pointer to their secure storage + * - T& operator*() + * - T* operator->() + * + * In a moved from state, SecureHandle's may be copy or move assigned and the destructor may + * run, + * but all other operations will invariant. + */ + template <typename T> + class SecureHandle { + public: + // NOTE: (jcarey) + // + // We have the default ctor and the perfect forwarding ctor because msvc 2013 ice's on some + // default constructed types without it (sfinae was falling over for some reason). + // + // For non-default constructible t's, we'll fail to substitute the forwarded call to new + SecureHandle() : _t(_new()) {} + + // Generic constructor that forwards to the underlying T if the first arg isn't a + // SecureHandle + template < + typename Arg, + typename... Args, + stdx::enable_if_t<!std::is_same<SecureHandle<T>, typename std::decay<Arg>::type>::value, + int> = 0> + SecureHandle(Arg&& arg, Args&&... args) + : _t(_new(std::forward<Arg>(arg), std::forward<Args>(args)...)) {} + + SecureHandle(const SecureHandle& other) : _t(_new(*other)) {} + + SecureHandle& operator=(const SecureHandle& other) { + if (_t) { + *_t = *other; + } else { + _t = _new(*other); + } -template <typename T, typename U> -bool operator!=(const SecureAllocator<T>& lhs, const SecureAllocator<U>& rhs) { - return !(lhs == rhs); -} + return *this; + } -/** - * SecureHandle offers a smart pointer-ish interface to a type. - * - * It attempts to solve the problem of using container types with small object optimizations that - * might accidentally leave important data on the stack if they're too small to spill to the heap. - * It uses the secure allocator for allocations and deallocations. - * - * This type is meant to offer more value like semantics than a unique_ptr: - * - SecureHandle's are only default constructible if their T is - * - You can construct a SecureHandle like the underlying value: - * SecureHandle<Mytype> type(foo, bar baz); - * - SecureHandle's are copyable if their T's are - * - only moving can produce a non-usable T - * - * While still being cheap and convenient like a unique_ptr: - * - SecureHandle's move by pointer to their secure storage - * - T& operator*() - * - T* operator->() - * - * In a moved from state, SecureHandle's may be copy or move assigned and the destructor may run, - * but all other operations will invariant. - */ -template <typename T> -class SecureHandle { -public: - // NOTE: (jcarey) - // - // We have the default ctor and the perfect forwarding ctor because msvc 2013 ice's on some - // default constructed types without it (sfinae was falling over for some reason). - // - // For non-default constructible t's, we'll fail to substitute the forwarded call to new - SecureHandle() : _t(_new()) {} - - // Generic constructor that forwards to the underlying T if the first arg isn't a SecureHandle - template < - typename Arg, - typename... Args, - stdx::enable_if_t<!std::is_same<SecureHandle<T>, typename std::decay<Arg>::type>::value, - int> = 0> - SecureHandle(Arg&& arg, Args&&... args) - : _t(_new(std::forward<Arg>(arg), std::forward<Args>(args)...)) {} - - SecureHandle(const SecureHandle& other) : _t(_new(*other)) {} - - SecureHandle& operator=(const SecureHandle& other) { - if (_t) { - *_t = *other; - } else { - _t = _new(*other); + SecureHandle(SecureHandle&& other) : _t(other._t) { + other._t = nullptr; } - return *this; - } + SecureHandle& operator=(SecureHandle&& other) { + if (&other == this) { + return *this; + } - SecureHandle(SecureHandle&& other) : _t(other._t) { - other._t = nullptr; - } + _delete(); + + _t = other._t; + other._t = nullptr; - SecureHandle& operator=(SecureHandle&& other) { - if (&other == this) { return *this; } - _delete(); + ~SecureHandle() { + _delete(); + } - _t = other._t; - other._t = nullptr; + T& operator*() const { + invariant(_t); - return *this; - } + return *_t; + } - ~SecureHandle() { - _delete(); - } + T* operator->() const { + invariant(_t); - T& operator*() const { - invariant(_t); + return _t; + } - return *_t; - } + private: + template <typename... Args> + static T* _new(Args&&... args) { + return ::new (secure_allocator_details::allocateWrapper( + sizeof(T), std::alignment_of<T>::value, DomainTraits::peg())) + T(std::forward<Args>(args)...); + } - T* operator->() const { - invariant(_t); + void _delete() { + if (_t) { + _t->~T(); + secure_allocator_details::deallocateWrapper(_t, sizeof(T), DomainTraits::peg()); + } + } - return _t; - } + T* _t; + }; -private: - template <typename... Args> - static T* _new(Args&&... args) { - return ::new (secure_allocator_details::allocate(sizeof(T), std::alignment_of<T>::value)) - T(std::forward<Args>(args)...); - } + template <typename T> + using SecureVector = SecureHandle<std::vector<T, SecureAllocator<T>>>; - void _delete() { - if (_t) { - _t->~T(); - secure_allocator_details::deallocate(_t, sizeof(T)); - } + using SecureString = + SecureHandle<std::basic_string<char, std::char_traits<char>, SecureAllocator<char>>>; +}; + +struct SecureAllocatorDefaultDomainTrait { + constexpr static bool peg() { + return true; } +}; - T* _t; +struct SecureAllocatorAuthDomainTrait { + static constexpr StringData DomainType = "auth"_sd; }; -template <typename T> -using SecureVector = SecureHandle<std::vector<T, SecureAllocator<T>>>; +using SecureAllocatorAuthDomain = + SecureAllocatorDomain<TraitNamedDomain<SecureAllocatorAuthDomainTrait>>; -using SecureString = - SecureHandle<std::basic_string<char, std::char_traits<char>, SecureAllocator<char>>>; +using SecureAllocatorDefaultDomain = SecureAllocatorDomain<SecureAllocatorDefaultDomainTrait>; + +template <typename T> +using SecureVector = SecureAllocatorDefaultDomain::SecureVector<T>; +using SecureString = SecureAllocatorDefaultDomain::SecureString; } // namespace mongo diff --git a/src/mongo/base/secure_allocator_test.cpp b/src/mongo/base/secure_allocator_test.cpp index 750dc8c5c78..9bc65d585fe 100644 --- a/src/mongo/base/secure_allocator_test.cpp +++ b/src/mongo/base/secure_allocator_test.cpp @@ -37,7 +37,7 @@ namespace mongo { TEST(SecureAllocator, SecureVector) { - SecureVector<int> vec; + SecureAllocatorDefaultDomain::SecureVector<int> vec; vec->push_back(1); vec->push_back(2); @@ -50,19 +50,19 @@ TEST(SecureAllocator, SecureVector) { } TEST(SecureAllocator, SecureString) { - SecureString str; + SecureAllocatorDefaultDomain::SecureString str; str->resize(2000, 'x'); - ASSERT_EQUALS(0, str->compare(*SecureString(2000, 'x'))); + ASSERT_EQUALS(0, str->compare(*SecureAllocatorDefaultDomain::SecureString(2000, 'x'))); - SecureString str2(str); + SecureAllocatorDefaultDomain::SecureString str2(str); ASSERT_NOT_EQUALS(&*str, &*str2); str2 = str; ASSERT_NOT_EQUALS(&*str, &*str2); auto strPtr = &*str; auto str2Ptr = &*str2; - SecureString str3(std::move(str)); + SecureAllocatorDefaultDomain::SecureString str3(std::move(str)); ASSERT_EQUALS(strPtr, &*str3); str3 = std::move(str2); ASSERT_EQUALS(str2Ptr, &*str3); @@ -72,8 +72,8 @@ TEST(SecureAllocator, SecureString) { // design (page per object), you couldn't make more than 8-50 objects before running out of lockable // pages. TEST(SecureAllocator, ManySecureBytes) { - std::array<SecureHandle<char>, 4096> chars; - std::vector<SecureHandle<char>> e_chars(4096, 'e'); + std::array<SecureAllocatorDefaultDomain::SecureHandle<char>, 4096> chars; + std::vector<SecureAllocatorDefaultDomain::SecureHandle<char>> e_chars(4096, 'e'); } TEST(SecureAllocator, NonDefaultConstructibleWorks) { @@ -82,7 +82,42 @@ TEST(SecureAllocator, NonDefaultConstructibleWorks) { Foo() = delete; }; - SecureHandle<Foo> foo(10); + SecureAllocatorDefaultDomain::SecureHandle<Foo> foo(10); +} + +TEST(SecureAllocator, allocatorCanBeDisabled) { + static size_t pegInvokationCountLast; + static size_t pegInvokationCount; + pegInvokationCountLast = 0; + pegInvokationCount = 0; + struct UnsecureAllocatorTrait { + static bool peg() { + pegInvokationCount++; + + return false; + } + }; + using UnsecureAllocatorDomain = SecureAllocatorDomain<UnsecureAllocatorTrait>; + + { + std::vector<UnsecureAllocatorDomain::SecureHandle<char>> more_e_chars(4096, 'e'); + ASSERT_GT(pegInvokationCount, pegInvokationCountLast); + pegInvokationCountLast = pegInvokationCount; + + UnsecureAllocatorDomain::SecureString str; + ASSERT_GT(pegInvokationCount, pegInvokationCountLast); + pegInvokationCountLast = pegInvokationCount; + + str->resize(2000, 'x'); + ASSERT_GT(pegInvokationCount, pegInvokationCountLast); + pegInvokationCountLast = pegInvokationCount; + + ASSERT_EQUALS(0, str->compare(*UnsecureAllocatorDomain::SecureString(2000, 'x'))); + ASSERT_GT(pegInvokationCount, pegInvokationCountLast); + pegInvokationCountLast = pegInvokationCount; + } + + ASSERT_GT(pegInvokationCount, pegInvokationCountLast); } } // namespace mongo diff --git a/src/mongo/crypto/mechanism_scram.h b/src/mongo/crypto/mechanism_scram.h index b12cebbf2e1..97875394df1 100644 --- a/src/mongo/crypto/mechanism_scram.h +++ b/src/mongo/crypto/mechanism_scram.h @@ -33,6 +33,7 @@ #include "mongo/base/secure_allocator.h" #include "mongo/base/status.h" #include "mongo/crypto/sha1_block.h" +#include "mongo/db/auth/authorization_manager.h" #include "mongo/db/jsobj.h" namespace mongo { @@ -97,7 +98,7 @@ public: // to a callback, which fills the memory. template <typename T> explicit SCRAMSecrets(T initializationFun) - : _ptr(std::make_shared<SecureHandle<SCRAMSecretsHolder>>()) { + : _ptr(std::make_shared<SecureAllocatorAuthDomain::SecureHandle<SCRAMSecretsHolder>>()) { initializationFun((*this)->clientKey, (*this)->storedKey, (*this)->serverKey); } @@ -106,20 +107,20 @@ public: return static_cast<bool>(_ptr); } - const SecureHandle<SCRAMSecretsHolder>& operator*() const& { + const SecureAllocatorAuthDomain::SecureHandle<SCRAMSecretsHolder>& operator*() const& { invariant(_ptr); return *_ptr; } void operator*() && = delete; - const SecureHandle<SCRAMSecretsHolder>& operator->() const& { + const SecureAllocatorAuthDomain::SecureHandle<SCRAMSecretsHolder>& operator->() const& { invariant(_ptr); return *_ptr; } void operator->() && = delete; private: - std::shared_ptr<SecureHandle<SCRAMSecretsHolder>> _ptr; + std::shared_ptr<SecureAllocatorAuthDomain::SecureHandle<SCRAMSecretsHolder>> _ptr; }; /* diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h index 578d9b384e5..8087bdbe849 100644 --- a/src/mongo/db/auth/authorization_manager.h +++ b/src/mongo/db/auth/authorization_manager.h @@ -32,6 +32,7 @@ #include <string> #include "mongo/base/disallow_copying.h" +#include "mongo/base/secure_allocator.h" #include "mongo/base/status.h" #include "mongo/bson/mutable/element.h" #include "mongo/bson/oid.h" @@ -44,6 +45,7 @@ #include "mongo/db/auth/user_name_hash.h" #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" +#include "mongo/db/server_options.h" #include "mongo/platform/unordered_map.h" #include "mongo/stdx/condition_variable.h" #include "mongo/stdx/functional.h" diff --git a/src/mongo/db/auth/sasl_plain_server_conversation.cpp b/src/mongo/db/auth/sasl_plain_server_conversation.cpp index 03b58dc1819..ecd24a4ac98 100644 --- a/src/mongo/db/auth/sasl_plain_server_conversation.cpp +++ b/src/mongo/db/auth/sasl_plain_server_conversation.cpp @@ -50,7 +50,7 @@ StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData, std::st // Expecting user input on the form: [authz-id]\0authn-id\0pwd std::string input = inputData.toString(); - SecureString pwd = ""; + SecureAllocatorAuthDomain::SecureString pwd = ""; try { size_t firstNull = inputData.find('\0'); if (firstNull == std::string::npos) { @@ -78,7 +78,7 @@ StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData, std::st str::stream() << "SASL authorization identity must match authentication identity"); } - pwd = SecureString(input.substr(secondNull + 1).c_str()); + pwd = SecureAllocatorAuthDomain::SecureString(input.substr(secondNull + 1).c_str()); if (pwd->empty()) { return Status(ErrorCodes::AuthenticationFailed, str::stream() diff --git a/src/mongo/db/server_options.h b/src/mongo/db/server_options.h index b4efdde5300..fe66db0f8c2 100644 --- a/src/mongo/db/server_options.h +++ b/src/mongo/db/server_options.h @@ -174,7 +174,21 @@ struct ServerGlobalParams { // "3.2" feature compatibility mode. AtomicWord<bool> validateFeaturesAsMaster{true}; } featureCompatibility; + + std::vector<std::string> disabledSecureAllocatorDomains; }; extern ServerGlobalParams serverGlobalParams; + +template <typename NameTrait> +struct TraitNamedDomain { + static bool peg() { + const auto& dsmd = serverGlobalParams.disabledSecureAllocatorDomains; + const auto contains = [&](StringData dt) { + return std::find(dsmd.begin(), dsmd.end(), dt) != dsmd.end(); + }; + static const bool ret = !(contains("*"_sd) || contains(NameTrait::DomainType)); + return ret; + } +}; } diff --git a/src/mongo/db/server_options_helpers.cpp b/src/mongo/db/server_options_helpers.cpp index 11a4f37008a..3b671684d73 100644 --- a/src/mongo/db/server_options_helpers.cpp +++ b/src/mongo/db/server_options_helpers.cpp @@ -1101,4 +1101,9 @@ Status storeServerOptions(const moe::Environment& params) { return Status::OK(); } +ExportedServerParameter<std::vector<std::string>, ServerParameterType::kStartupOnly> + SecureAllocatorDomains(ServerParameterSet::getGlobal(), + "disabledSecureAllocatorDomains", + &serverGlobalParams.disabledSecureAllocatorDomains); + } // namespace mongo |