From ebd0ec786e6986ecd407c48928e6866736675021 Mon Sep 17 00:00:00 2001 From: Spencer Jackson Date: Thu, 24 Aug 2017 18:03:27 -0400 Subject: SERVER-30821: Allow configurable use of secure memory --- src/mongo/base/secure_allocator.h | 405 +++++++++++++++++++++----------------- 1 file changed, 224 insertions(+), 181 deletions(-) (limited to 'src/mongo/base/secure_allocator.h') 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 #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 -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> - * - * where the vectors were stored securely, but the strings spilled to the - * heap - * - */ - MONGO_STATIC_ASSERT_MSG(std::is_trivially_copyable::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 - struct rebind { - using other = SecureAllocator; - }; +template +struct SecureAllocatorDomain { + template + 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> + * + * where the vectors were stored securely, but the strings spilled to the + * heap + * + */ + MONGO_STATIC_ASSERT_MSG(std::is_trivially_copyable::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 + struct rebind { + using other = SecureAllocator; + }; + + pointer allocate(size_type n) { + return static_cast(secure_allocator_details::allocateWrapper( + sizeof(value_type) * n, std::alignment_of::value, DomainTraits::peg())); + } - pointer allocate(size_type n) { - return static_cast(secure_allocator_details::allocate( - sizeof(value_type) * n, std::alignment_of::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(ptr), sizeof(value_type) * n, DomainTraits::peg()); + } - void deallocate(pointer ptr, size_type n) { - return secure_allocator_details::deallocate(static_cast(ptr), - sizeof(value_type) * n); - } + size_type max_size() { + return std::numeric_limits::max(); + } - size_type max_size() { - return std::numeric_limits::max(); - } + SecureAllocator() = default; - SecureAllocator() = default; + template + SecureAllocator(const SecureAllocator& other) {} - template - SecureAllocator(const SecureAllocator& other) {} + template + void construct(U* p, Args&&... args) { + ::new (static_cast(p)) U(std::forward(args)...); + } - template - void construct(U* p, Args&&... args) { - ::new (static_cast(p)) U(std::forward(args)...); - } + template + void destroy(U* p) { + p->~U(); + } - template - 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 + friend bool operator==(const SecureAllocator& lhs, const SecureAllocator& 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 + friend bool operator!=(const SecureAllocator& lhs, const SecureAllocator& rhs) { + return !(lhs == rhs); + } -template -bool operator==(const SecureAllocator& lhs, const SecureAllocator& 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 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 + 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, typename std::decay::type>::value, + int> = 0> + SecureHandle(Arg&& arg, Args&&... args) + : _t(_new(std::forward(arg), std::forward(args)...)) {} + + SecureHandle(const SecureHandle& other) : _t(_new(*other)) {} + + SecureHandle& operator=(const SecureHandle& other) { + if (_t) { + *_t = *other; + } else { + _t = _new(*other); + } -template -bool operator!=(const SecureAllocator& lhs, const SecureAllocator& 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 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 -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, typename std::decay::type>::value, - int> = 0> - SecureHandle(Arg&& arg, Args&&... args) - : _t(_new(std::forward(arg), std::forward(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 + static T* _new(Args&&... args) { + return ::new (secure_allocator_details::allocateWrapper( + sizeof(T), std::alignment_of::value, DomainTraits::peg())) + T(std::forward(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 - static T* _new(Args&&... args) { - return ::new (secure_allocator_details::allocate(sizeof(T), std::alignment_of::value)) - T(std::forward(args)...); - } + template + using SecureVector = SecureHandle>>; - void _delete() { - if (_t) { - _t->~T(); - secure_allocator_details::deallocate(_t, sizeof(T)); - } + using SecureString = + SecureHandle, SecureAllocator>>; +}; + +struct SecureAllocatorDefaultDomainTrait { + constexpr static bool peg() { + return true; } +}; - T* _t; +struct SecureAllocatorAuthDomainTrait { + static constexpr StringData DomainType = "auth"_sd; }; -template -using SecureVector = SecureHandle>>; +using SecureAllocatorAuthDomain = + SecureAllocatorDomain>; -using SecureString = - SecureHandle, SecureAllocator>>; +using SecureAllocatorDefaultDomain = SecureAllocatorDomain; + +template +using SecureVector = SecureAllocatorDefaultDomain::SecureVector; +using SecureString = SecureAllocatorDefaultDomain::SecureString; } // namespace mongo -- cgit v1.2.1