// secblock.h - originally written and placed in the public domain by Wei Dai /// \file secblock.h /// \brief Classes and functions for secure memory allocations. #ifndef CRYPTOPP_SECBLOCK_H #define CRYPTOPP_SECBLOCK_H #include "config.h" #include "allocate.h" #include "misc.h" #include "stdcpp.h" #if defined(CRYPTOPP_MSC_VERSION) # pragma warning(push) # pragma warning(disable: 4231 4275 4700) # if (CRYPTOPP_MSC_VERSION >= 1400) # pragma warning(disable: 6011 6386 28193) # endif #endif NAMESPACE_BEGIN(CryptoPP) // ************** secure memory allocation *************** /// \brief Base class for all allocators used by SecBlock /// \tparam T the class or type template class AllocatorBase { public: typedef T value_type; typedef size_t size_type; typedef std::ptrdiff_t difference_type; typedef T * pointer; typedef const T * const_pointer; typedef T & reference; typedef const T & const_reference; pointer address(reference r) const {return (&r);} const_pointer address(const_reference r) const {return (&r); } void construct(pointer p, const T& val) {new (p) T(val);} void destroy(pointer p) {CRYPTOPP_UNUSED(p); p->~T();} /// \brief Returns the maximum number of elements the allocator can provide /// \details ELEMS_MAX is the maximum number of elements the /// Allocator can provide. The value of ELEMS_MAX is /// SIZE_MAX/sizeof(T). std::numeric_limits was avoided /// due to lack of constexpr-ness in C++03 and below. /// \note In C++03 and below ELEMS_MAX is a static data member of type /// size_type. In C++11 and above ELEMS_MAX is an enum /// inheriting from size_type. In both cases ELEMS_MAX can be /// used before objects are fully constructed, and it does not suffer the /// limitations of class methods like max_size. /// \sa Issue 346/CVE-2016-9939 /// \since Crypto++ 6.0 #if defined(CRYPTOPP_DOXYGEN_PROCESSING) static const size_type ELEMS_MAX = ...; #elif defined(CRYPTOPP_MSC_VERSION) && (CRYPTOPP_MSC_VERSION <= 1400) static const size_type ELEMS_MAX = (~(size_type)0)/sizeof(T); #elif defined(CRYPTOPP_CXX11_STRONG_ENUM) enum : size_type {ELEMS_MAX = SIZE_MAX/sizeof(T)}; #else static const size_type ELEMS_MAX = SIZE_MAX/sizeof(T); #endif /// \brief Returns the maximum number of elements the allocator can provide /// \return the maximum number of elements the allocator can provide /// \details Internally, preprocessor macros are used rather than std::numeric_limits /// because the latter is not a constexpr. Some compilers, like Clang, do not /// optimize it well under all circumstances. Compilers like GCC, ICC and MSVC appear /// to optimize it well in either form. CRYPTOPP_CONSTEXPR size_type max_size() const {return ELEMS_MAX;} #if defined(__SUNPRO_CC) // https://github.com/weidai11/cryptopp/issues/770 // and https://stackoverflow.com/q/53999461/608639 CRYPTOPP_CONSTEXPR size_type max_size(size_type n) const {return SIZE_MAX/n;} #endif #if defined(CRYPTOPP_CXX11_VARIADIC_TEMPLATES) || defined(CRYPTOPP_DOXYGEN_PROCESSING) /// \brief Constructs a new V using variadic arguments /// \tparam V the type to be forwarded /// \tparam Args the arguments to be forwarded /// \param ptr pointer to type V /// \param args variadic arguments /// \details This is a C++11 feature. It is available when CRYPTOPP_CXX11_VARIADIC_TEMPLATES /// is defined. The define is controlled by compiler versions detected in config.h. template void construct(V* ptr, Args&&... args) {::new ((void*)ptr) V(std::forward(args)...);} /// \brief Destroys an V constructed with variadic arguments /// \tparam V the type to be forwarded /// \details This is a C++11 feature. It is available when CRYPTOPP_CXX11_VARIADIC_TEMPLATES /// is defined. The define is controlled by compiler versions detected in config.h. template void destroy(V* ptr) {if (ptr) ptr->~V();} #endif protected: /// \brief Verifies the allocator can satisfy a request based on size /// \param size the size of the allocation, in elements /// \throw InvalidArgument /// \details CheckSize verifies the number of elements requested is valid. /// \details If size is greater than max_size(), then InvalidArgument is thrown. /// The library throws InvalidArgument if the size is too large to satisfy. /// \details Internally, preprocessor macros are used rather than std::numeric_limits /// because the latter is not a constexpr. Some compilers, like Clang, do not /// optimize it well under all circumstances. Compilers like GCC, ICC and MSVC appear /// to optimize it well in either form. /// \details The sizeof(T) != 1 in the condition attempts to help the /// compiler optimize the check for byte types. Coverity findings for /// CONSTANT_EXPRESSION_RESULT were generated without it. For byte types, /// size never exceeded ELEMS_MAX but the code was not removed. /// \note size is the count of elements, and not the number of bytes static void CheckSize(size_t size) { // Squash MSC C4100 warning for size. Also see commit 42b7c4ea5673. CRYPTOPP_UNUSED(size); // C++ throws std::bad_alloc (C++03) or std::bad_array_new_length (C++11) here. if (sizeof(T) != 1 && size > ELEMS_MAX) throw InvalidArgument("AllocatorBase: requested size would cause integer overflow"); } }; #define CRYPTOPP_INHERIT_ALLOCATOR_TYPES(T_type) \ typedef typename AllocatorBase::value_type value_type;\ typedef typename AllocatorBase::size_type size_type;\ typedef typename AllocatorBase::difference_type difference_type;\ typedef typename AllocatorBase::pointer pointer;\ typedef typename AllocatorBase::const_pointer const_pointer;\ typedef typename AllocatorBase::reference reference;\ typedef typename AllocatorBase::const_reference const_reference; /// \brief Reallocation function /// \tparam T the class or type /// \tparam A the class or type's allocator /// \param alloc the allocator /// \param oldPtr the previous allocation /// \param oldSize the size of the previous allocation /// \param newSize the new, requested size /// \param preserve flag that indicates if the old allocation should be preserved /// \note oldSize and newSize are the count of elements, and not the /// number of bytes. template typename A::pointer StandardReallocate(A& alloc, T *oldPtr, typename A::size_type oldSize, typename A::size_type newSize, bool preserve) { // Avoid assert on pointer in reallocate. SecBlock regularly uses NULL // pointers rather returning non-NULL 0-sized pointers. if (oldSize == newSize) return oldPtr; if (preserve) { typename A::pointer newPtr = alloc.allocate(newSize, NULLPTR); const typename A::size_type copySize = STDMIN(oldSize, newSize) * sizeof(T); if (oldPtr && newPtr) memcpy_s(newPtr, copySize, oldPtr, copySize); if (oldPtr) alloc.deallocate(oldPtr, oldSize); return newPtr; } else { if (oldPtr) alloc.deallocate(oldPtr, oldSize); return alloc.allocate(newSize, NULLPTR); } } /// \brief Allocates a block of memory with cleanup /// \tparam T class or type /// \tparam T_Align16 boolean that determines whether allocations should be aligned on a 16-byte boundary /// \details If T_Align16 is true, then AllocatorWithCleanup calls AlignedAllocate() /// for memory allocations. If T_Align16 is false, then AllocatorWithCleanup() calls /// UnalignedAllocate() for memory allocations. /// \details Template parameter T_Align16 is effectively controlled by cryptlib.h and mirrors /// CRYPTOPP_BOOL_ALIGN16. CRYPTOPP_BOOL_ALIGN16 is often used as the template parameter. template class AllocatorWithCleanup : public AllocatorBase { public: CRYPTOPP_INHERIT_ALLOCATOR_TYPES(T) /// \brief Allocates a block of memory /// \param ptr the size of the allocation /// \param size the size of the allocation, in elements /// \return a memory block /// \throw InvalidArgument /// \details allocate() first checks the size of the request. If it is non-0 /// and less than max_size(), then an attempt is made to fulfill the request /// using either AlignedAllocate() or UnalignedAllocate(). AlignedAllocate() is /// used if T_Align16 is true. UnalignedAllocate() used if T_Align16 is false. /// \details This is the C++ *Placement New* operator. ptr is not used, and the /// function asserts in Debug builds if ptr is non-NULL. /// \sa CallNewHandler() for the methods used to recover from a failed /// allocation attempt. /// \note size is the count of elements, and not the number of bytes pointer allocate(size_type size, const void *ptr = NULLPTR) { CRYPTOPP_UNUSED(ptr); CRYPTOPP_ASSERT(ptr == NULLPTR); this->CheckSize(size); if (size == 0) return NULLPTR; #if CRYPTOPP_BOOL_ALIGN16 if (T_Align16) return reinterpret_cast(AlignedAllocate(size*sizeof(T))); #endif return reinterpret_cast(UnalignedAllocate(size*sizeof(T))); } /// \brief Deallocates a block of memory /// \param ptr the pointer for the allocation /// \param size the size of the allocation, in elements /// \details Internally, SecureWipeArray() is called before deallocating the /// memory. Once the memory block is wiped or zeroized, AlignedDeallocate() /// or UnalignedDeallocate() is called. /// \details AlignedDeallocate() is used if T_Align16 is true. /// UnalignedDeallocate() used if T_Align16 is false. void deallocate(void *ptr, size_type size) { // Avoid assert on pointer in deallocate. SecBlock regularly uses NULL // pointers rather returning non-NULL 0-sized pointers. if (ptr) { SecureWipeArray(reinterpret_cast(ptr), size); #if CRYPTOPP_BOOL_ALIGN16 if (T_Align16) return AlignedDeallocate(ptr); #endif UnalignedDeallocate(ptr); } } /// \brief Reallocates a block of memory /// \param oldPtr the previous allocation /// \param oldSize the size of the previous allocation /// \param newSize the new, requested size /// \param preserve flag that indicates if the old allocation should be preserved /// \return pointer to the new memory block /// \details Internally, reallocate() calls StandardReallocate(). /// \details If preserve is true, then index 0 is used to begin copying the /// old memory block to the new one. If the block grows, then the old array /// is copied in its entirety. If the block shrinks, then only newSize /// elements are copied from the old block to the new one. /// \note oldSize and newSize are the count of elements, and not the /// number of bytes. pointer reallocate(T *oldPtr, size_type oldSize, size_type newSize, bool preserve) { CRYPTOPP_ASSERT((oldPtr && oldSize) || !(oldPtr || oldSize)); return StandardReallocate(*this, oldPtr, oldSize, newSize, preserve); } /// \brief Template class member Rebind /// \tparam V bound class or type /// \details Rebind allows a container class to allocate a different type of object /// to store elements. For example, a std::list will allocate std::list_node to /// store elements in the list. /// \details VS.NET STL enforces the policy of "All STL-compliant allocators /// have to provide a template class member called rebind". template struct rebind { typedef AllocatorWithCleanup other; }; #if (CRYPTOPP_MSC_VERSION >= 1500) AllocatorWithCleanup() {} template AllocatorWithCleanup(const AllocatorWithCleanup &) {} #endif }; CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; #if defined(CRYPTOPP_WORD128_AVAILABLE) CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; // for Integer #endif #if CRYPTOPP_BOOL_X86 CRYPTOPP_DLL_TEMPLATE_CLASS AllocatorWithCleanup; // for Integer #endif /// \brief NULL allocator /// \tparam T class or type /// \details A NullAllocator is useful for fixed-size, stack based allocations /// (i.e., static arrays used by FixedSizeAllocatorWithCleanup). /// \details A NullAllocator always returns 0 for max_size(), and always returns /// NULL for allocation requests. Though the allocator does not allocate at /// runtime, it does perform a secure wipe or zeroization during cleanup. template class NullAllocator : public AllocatorBase { public: //LCOV_EXCL_START CRYPTOPP_INHERIT_ALLOCATOR_TYPES(T) // TODO: should this return NULL or throw bad_alloc? Non-Windows C++ standard // libraries always throw. And late mode Windows throws. Early model Windows // (circa VC++ 6.0) returned NULL. pointer allocate(size_type n, const void* unused = NULLPTR) { CRYPTOPP_UNUSED(n); CRYPTOPP_UNUSED(unused); CRYPTOPP_ASSERT(false); return NULLPTR; } void deallocate(void *p, size_type n) { CRYPTOPP_UNUSED(p); CRYPTOPP_UNUSED(n); CRYPTOPP_ASSERT(false); } CRYPTOPP_CONSTEXPR size_type max_size() const {return 0;} //LCOV_EXCL_STOP }; /// \brief Static secure memory block with cleanup /// \tparam T class or type /// \tparam S fixed-size of the stack-based memory block, in elements /// \tparam T_Align16 boolean that determines whether allocations should /// be aligned on a 16-byte boundary /// \details FixedSizeAllocatorWithCleanup provides a fixed-size, stack- /// based allocation at compile time. The class can grow its memory /// block at runtime if a suitable allocator is available. If size /// grows beyond S and a suitable allocator is available, then the /// statically allocated array is obsoleted. /// \note This allocator can't be used with standard collections because /// they require that all objects of the same allocator type are equivalent. template , bool T_Align16 = false> class FixedSizeAllocatorWithCleanup : public AllocatorBase { // The body of FixedSizeAllocatorWithCleanup is provided in the two // partial specializations that follow. The two specializations // pivot on the boolean template parameter T_Align16. }; /// \brief Static secure memory block with cleanup /// \tparam T class or type /// \tparam S fixed-size of the stack-based memory block, in elements /// \details FixedSizeAllocatorWithCleanup provides a fixed-size, stack- /// based allocation at compile time. The class can grow its memory /// block at runtime if a suitable allocator is available. If size /// grows beyond S and a suitable allocator is available, then the /// statically allocated array is obsoleted. /// \note This allocator can't be used with standard collections because /// they require that all objects of the same allocator type are equivalent. template class FixedSizeAllocatorWithCleanup : public AllocatorBase { public: CRYPTOPP_INHERIT_ALLOCATOR_TYPES(T) /// \brief Constructs a FixedSizeAllocatorWithCleanup FixedSizeAllocatorWithCleanup() : m_allocated(false) {} /// \brief Allocates a block of memory /// \param size the count elements in the memory block /// \details FixedSizeAllocatorWithCleanup provides a fixed-size, stack-based /// allocation at compile time. If size is less than or equal to /// S, then a pointer to the static array is returned. /// \details The class can grow its memory block at runtime if a suitable /// allocator is available. If size grows beyond S and a suitable /// allocator is available, then the statically allocated array is /// obsoleted. If a suitable allocator is not available, as with a /// NullAllocator, then the function returns NULL and a runtime error /// eventually occurs. /// \sa reallocate(), SecBlockWithHint pointer allocate(size_type size) { CRYPTOPP_ASSERT(IsAlignedOn(m_array, 8)); if (size <= S && !m_allocated) { m_allocated = true; return GetAlignedArray(); } else return m_fallbackAllocator.allocate(size); } /// \brief Allocates a block of memory /// \param size the count elements in the memory block /// \param hint an unused hint /// \details FixedSizeAllocatorWithCleanup provides a fixed-size, stack- /// based allocation at compile time. If size is less than or equal to /// S, then a pointer to the static array is returned. /// \details The class can grow its memory block at runtime if a suitable /// allocator is available. If size grows beyond S and a suitable /// allocator is available, then the statically allocated array is /// obsoleted. If a suitable allocator is not available, as with a /// NullAllocator, then the function returns NULL and a runtime error /// eventually occurs. /// \sa reallocate(), SecBlockWithHint pointer allocate(size_type size, const void *hint) { CRYPTOPP_ASSERT(IsAlignedOn(m_array, 8)); if (size <= S && !m_allocated) { m_allocated = true; return GetAlignedArray(); } else return m_fallbackAllocator.allocate(size, hint); } /// \brief Deallocates a block of memory /// \param ptr a pointer to the memory block to deallocate /// \param size the count elements in the memory block /// \details The memory block is wiped or zeroized before deallocation. /// If the statically allocated memory block is active, then no /// additional actions are taken after the wipe. /// \details If a dynamic memory block is active, then the pointer and /// size are passed to the allocator for deallocation. void deallocate(void *ptr, size_type size) { // Avoid assert on pointer in deallocate. SecBlock regularly uses NULL // pointers rather returning non-NULL 0-sized pointers. if (ptr == GetAlignedArray()) { // If the m_allocated assert fires then the bit twiddling for // GetAlignedArray() is probably incorrect for the platform. // Be sure to check CRYPTOPP_ALIGN_DATA(8). The platform may // not have a way to declaratively align data to 8. CRYPTOPP_ASSERT(size <= S); CRYPTOPP_ASSERT(m_allocated); m_allocated = false; SecureWipeArray(reinterpret_cast(ptr), size); } else { if (ptr) m_fallbackAllocator.deallocate(ptr, size); } } /// \brief Reallocates a block of memory /// \param oldPtr the previous allocation /// \param oldSize the size of the previous allocation /// \param newSize the new, requested size /// \param preserve flag that indicates if the old allocation should /// be preserved /// \return pointer to the new memory block /// \details FixedSizeAllocatorWithCleanup provides a fixed-size, stack- /// based allocation at compile time. If size is less than or equal to /// S, then a pointer to the static array is returned. /// \details The class can grow its memory block at runtime if a suitable /// allocator is available. If size grows beyond S and a suitable /// allocator is available, then the statically allocated array is /// obsoleted. If a suitable allocator is not available, as with a /// NullAllocator, then the function returns NULL and a runtime error /// eventually occurs. /// \note size is the count of elements, and not the number of bytes. /// \sa reallocate(), SecBlockWithHint pointer reallocate(pointer oldPtr, size_type oldSize, size_type newSize, bool preserve) { if (oldPtr == GetAlignedArray() && newSize <= S) { CRYPTOPP_ASSERT(oldSize <= S); if (oldSize > newSize) SecureWipeArray(oldPtr+newSize, oldSize-newSize); return oldPtr; } pointer newPtr = allocate(newSize, NULLPTR); if (preserve && newSize) { const size_type copySize = STDMIN(oldSize, newSize); if (newPtr && oldPtr) // GCC analyzer warning memcpy_s(newPtr, sizeof(T)*newSize, oldPtr, sizeof(T)*copySize); } deallocate(oldPtr, oldSize); return newPtr; } CRYPTOPP_CONSTEXPR size_type max_size() const { return STDMAX(m_fallbackAllocator.max_size(), S); } private: #if CRYPTOPP_BOOL_ALIGN16 // There be demons here... We cannot use CRYPTOPP_ALIGN_DATA(16) // because linkers on 32-bit machines and some 64-bit machines // align the stack to 8-bytes or less, and not 16-bytes as // requested. We can only count on a smaller alignment. All // toolchains tested appear to honor CRYPTOPP_ALIGN_DATA(8). Also // see http://stackoverflow.com/a/1468656/608639. // // The 16-byte alignment is achieved by padding the requested // size with extra elements so we have at least 8-bytes of slack // to work with. Then the array pointer is moved to achieve a // 16-byte alignment. // // The additional 8-bytes introduces a small secondary issue. // The secondary issue is, a large T results in 0 = 8/sizeof(T). // The library is OK but users may hit it. So we need to guard // for a large T, and that is what the enum and PAD achieves. T* GetAlignedArray() { // m_array is aligned on 8 byte boundaries due to // CRYPTOPP_ALIGN_DATA(8). If m_array%16 is 0, then the buffer // is 16-byte aligned and nothing needs to be done. if // m_array%16 is 8, then the buffer is not 16-byte aligned and // we need to add 8. 8 has that nice symmetric property. // // If we needed to use CRYPTOPP_ALIGN_DATA(4) due to toolchain // limitations, then the calculation would be slightly more // costly: ptr = m_array + (16 - (m_array % 16)) % 16; CRYPTOPP_ASSERT(IsAlignedOn(m_array, 8)); int off = reinterpret_cast(m_array) % 16; byte* ptr = reinterpret_cast(m_array) + off; // Verify the 16-byte alignment. This is the point // of these extra gyrations. CRYPTOPP_ASSERT(IsAlignedOn(ptr, 16)); // Verify the lower bound. This is Issue 982/988. CRYPTOPP_ASSERT( reinterpret_cast(ptr) >= reinterpret_cast(m_array) ); // Verify the upper bound. Allocated array with // pad is large enough. CRYPTOPP_ASSERT( reinterpret_cast(ptr+S*sizeof(T)) <= reinterpret_cast(m_array+(S+PAD)) ); // void* to silence Clang warnings return reinterpret_cast( static_cast(ptr) ); } // PAD is elements, not bytes, and rounded up to ensure no overflow. enum { Q = sizeof(T), PAD = (Q >= 8) ? 1 : (Q >= 4) ? 2 : (Q >= 2) ? 4 : 8 }; // enum { Q = sizeof(T), PAD = (Q >= 16) ? 1 : (Q >= 8) ? 2 : (Q >= 4) ? 4 : (Q >= 2) ? 8 : 16 }; CRYPTOPP_ALIGN_DATA(8) T m_array[S+PAD]; #else // CRYPTOPP_BOOL_ALIGN16 is 0. If we are here then the user // probably compiled with CRYPTOPP_DISABLE_ASM. Normally we // would use the natural alignment of T. The problem we are // having is, some toolchains are changing the boundary for // 64-bit arrays. 64-bit elements require 8-byte alignment, // but the toolchain is laying the array out on a 4 byte // boundary. See GH #992 for mystery alignment, // https://github.com/weidai11/cryptopp/issues/992 T* GetAlignedArray() {return m_array;} CRYPTOPP_ALIGN_DATA(8) T m_array[S]; #endif A m_fallbackAllocator; bool m_allocated; }; /// \brief Static secure memory block with cleanup /// \tparam T class or type /// \tparam S fixed-size of the stack-based memory block, in elements /// \details FixedSizeAllocatorWithCleanup provides a fixed-size, stack- /// based allocation at compile time. The class can grow its memory /// block at runtime if a suitable allocator is available. If size /// grows beyond S and a suitable allocator is available, then the /// statically allocated array is obsoleted. /// \note This allocator can't be used with standard collections because /// they require that all objects of the same allocator type are equivalent. template class FixedSizeAllocatorWithCleanup : public AllocatorBase { public: CRYPTOPP_INHERIT_ALLOCATOR_TYPES(T) /// \brief Constructs a FixedSizeAllocatorWithCleanup FixedSizeAllocatorWithCleanup() : m_allocated(false) {} /// \brief Allocates a block of memory /// \param size the count elements in the memory block /// \details FixedSizeAllocatorWithCleanup provides a fixed-size, stack-based /// allocation at compile time. If size is less than or equal to /// S, then a pointer to the static array is returned. /// \details The class can grow its memory block at runtime if a suitable /// allocator is available. If size grows beyond S and a suitable /// allocator is available, then the statically allocated array is /// obsoleted. If a suitable allocator is not available, as with a /// NullAllocator, then the function returns NULL and a runtime error /// eventually occurs. /// \sa reallocate(), SecBlockWithHint pointer allocate(size_type size) { CRYPTOPP_ASSERT(IsAlignedOn(m_array, 8)); if (size <= S && !m_allocated) { m_allocated = true; return GetAlignedArray(); } else return m_fallbackAllocator.allocate(size); } /// \brief Allocates a block of memory /// \param size the count elements in the memory block /// \param hint an unused hint /// \details FixedSizeAllocatorWithCleanup provides a fixed-size, stack- /// based allocation at compile time. If size is less than or equal to /// S, then a pointer to the static array is returned. /// \details The class can grow its memory block at runtime if a suitable /// allocator is available. If size grows beyond S and a suitable /// allocator is available, then the statically allocated array is /// obsoleted. If a suitable allocator is not available, as with a /// NullAllocator, then the function returns NULL and a runtime error /// eventually occurs. /// \sa reallocate(), SecBlockWithHint pointer allocate(size_type size, const void *hint) { if (size <= S && !m_allocated) { m_allocated = true; return GetAlignedArray(); } else return m_fallbackAllocator.allocate(size, hint); } /// \brief Deallocates a block of memory /// \param ptr a pointer to the memory block to deallocate /// \param size the count elements in the memory block /// \details The memory block is wiped or zeroized before deallocation. /// If the statically allocated memory block is active, then no /// additional actions are taken after the wipe. /// \details If a dynamic memory block is active, then the pointer and /// size are passed to the allocator for deallocation. void deallocate(void *ptr, size_type size) { // Avoid assert on pointer in deallocate. SecBlock regularly uses NULL // pointers rather returning non-NULL 0-sized pointers. if (ptr == GetAlignedArray()) { // If the m_allocated assert fires then // something overwrote the flag. CRYPTOPP_ASSERT(size <= S); CRYPTOPP_ASSERT(m_allocated); m_allocated = false; SecureWipeArray((pointer)ptr, size); } else { if (ptr) m_fallbackAllocator.deallocate(ptr, size); m_allocated = false; } } /// \brief Reallocates a block of memory /// \param oldPtr the previous allocation /// \param oldSize the size of the previous allocation /// \param newSize the new, requested size /// \param preserve flag that indicates if the old allocation should /// be preserved /// \return pointer to the new memory block /// \details FixedSizeAllocatorWithCleanup provides a fixed-size, stack- /// based allocation at compile time. If size is less than or equal to /// S, then a pointer to the static array is returned. /// \details The class can grow its memory block at runtime if a suitable /// allocator is available. If size grows beyond S and a suitable /// allocator is available, then the statically allocated array is /// obsoleted. If a suitable allocator is not available, as with a /// NullAllocator, then the function returns NULL and a runtime error /// eventually occurs. /// \note size is the count of elements, and not the number of bytes. /// \sa reallocate(), SecBlockWithHint pointer reallocate(pointer oldPtr, size_type oldSize, size_type newSize, bool preserve) { if (oldPtr == GetAlignedArray() && newSize <= S) { CRYPTOPP_ASSERT(oldSize <= S); if (oldSize > newSize) SecureWipeArray(oldPtr+newSize, oldSize-newSize); return oldPtr; } pointer newPtr = allocate(newSize, NULLPTR); if (preserve && newSize) { const size_type copySize = STDMIN(oldSize, newSize); if (newPtr && oldPtr) // GCC analyzer warning memcpy_s(newPtr, sizeof(T)*newSize, oldPtr, sizeof(T)*copySize); } deallocate(oldPtr, oldSize); return newPtr; } CRYPTOPP_CONSTEXPR size_type max_size() const { return STDMAX(m_fallbackAllocator.max_size(), S); } private: // T_Align16 is false. Normally we would use the natural // alignment of T. The problem we are having is, some toolchains // are changing the boundary for 64-bit arrays. 64-bit elements // require 8-byte alignment, but the toolchain is laying the array // out on a 4 byte boundary. See GH #992 for mystery alignment, // https://github.com/weidai11/cryptopp/issues/992 T* GetAlignedArray() {return m_array;} CRYPTOPP_ALIGN_DATA(8) T m_array[S]; A m_fallbackAllocator; bool m_allocated; }; /// \brief Secure memory block with allocator and cleanup /// \tparam T a class or type /// \tparam A AllocatorWithCleanup derived class for allocation and cleanup /// \sa SecBlock /// on the Crypto++ wiki. /// \since Crypto++ 2.0 template > class SecBlock { public: typedef typename A::value_type value_type; typedef typename A::pointer iterator; typedef typename A::const_pointer const_iterator; typedef typename A::size_type size_type; /// \brief Returns the maximum number of elements the block can hold /// \details ELEMS_MAX is the maximum number of elements the /// SecBlock can hold. The value of ELEMS_MAX is /// SIZE_MAX/sizeof(T). std::numeric_limits was avoided /// due to lack of constexpr-ness in C++03 and below. /// \note In C++03 and below ELEMS_MAX is a static data member of type /// size_type. In C++11 and above ELEMS_MAX is an enum /// inheriting from size_type. In both cases ELEMS_MAX can be /// used before objects are fully constructed, and it does not suffer the /// limitations of class methods like max_size. /// \sa Issue 346/CVE-2016-9939 /// \since Crypto++ 6.0 #if defined(CRYPTOPP_DOXYGEN_PROCESSING) static const size_type ELEMS_MAX = ...; #elif defined(CRYPTOPP_MSC_VERSION) && (CRYPTOPP_MSC_VERSION <= 1400) static const size_type ELEMS_MAX = (~(size_type)0)/sizeof(T); #elif defined(CRYPTOPP_CXX11_STRONG_ENUM) enum : size_type {ELEMS_MAX = A::ELEMS_MAX}; #else static const size_type ELEMS_MAX = SIZE_MAX/sizeof(T); #endif /// \brief Construct a SecBlock with space for size elements. /// \param size the size of the allocation, in elements /// \throw std::bad_alloc /// \details The elements are not initialized. /// \since Crypto++ 2.0 /// \note size is the count of elements, and not the number of bytes explicit SecBlock(size_type size=0) : m_mark(ELEMS_MAX), m_size(size), m_ptr(m_alloc.allocate(size, NULLPTR)) { } /// \brief Copy construct a SecBlock from another SecBlock /// \param t the other SecBlock /// \throw std::bad_alloc /// \since Crypto++ 2.0 SecBlock(const SecBlock &t) : m_mark(t.m_mark), m_size(t.m_size), m_ptr(m_alloc.allocate(t.m_size, NULLPTR)) { CRYPTOPP_ASSERT((!t.m_ptr && !m_size) || (t.m_ptr && m_size)); if (m_ptr && t.m_ptr) memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); } /// \brief Construct a SecBlock from an array of elements. /// \param ptr a pointer to an array of T /// \param len the number of elements in the memory block /// \throw std::bad_alloc /// \details If ptr!=NULL and len!=0, then the block is initialized from the pointer /// ptr. If ptr==NULL and len!=0, then the block is initialized to 0. /// Otherwise, the block is empty and not initialized. /// \since Crypto++ 2.0 /// \note size is the count of elements, and not the number of bytes SecBlock(const T *ptr, size_type len) : m_mark(ELEMS_MAX), m_size(len), m_ptr(m_alloc.allocate(len, NULLPTR)) { CRYPTOPP_ASSERT((!m_ptr && !m_size) || (m_ptr && m_size)); if (m_ptr && ptr) memcpy_s(m_ptr, m_size*sizeof(T), ptr, len*sizeof(T)); else if (m_ptr && m_size) std::memset(m_ptr, 0, m_size*sizeof(T)); } ~SecBlock() {m_alloc.deallocate(m_ptr, STDMIN(m_size, m_mark));} #ifdef __BORLANDC__ /// \brief Cast operator /// \return block pointer cast to non-const T * /// \since Crypto++ 2.0 operator T *() const {return (T*)m_ptr;} #else /// \brief Cast operator /// \return block pointer cast to const void * /// \since Crypto++ 2.0 operator const void *() const {return m_ptr;} /// \brief Cast operator /// \return block pointer cast to non-const void * /// \since Crypto++ 2.0 operator void *() {return m_ptr;} /// \brief Cast operator /// \return block pointer cast to const T * /// \since Crypto++ 2.0 operator const T *() const {return m_ptr;} /// \brief Cast operator /// \return block pointer cast to non-const T * /// \since Crypto++ 2.0 operator T *() {return m_ptr;} #endif /// \brief Provides an iterator pointing to the first element in the memory block /// \return iterator pointing to the first element in the memory block /// \since Crypto++ 2.0 iterator begin() {return m_ptr;} /// \brief Provides a constant iterator pointing to the first element in the memory block /// \return constant iterator pointing to the first element in the memory block /// \since Crypto++ 2.0 const_iterator begin() const {return m_ptr;} /// \brief Provides an iterator pointing beyond the last element in the memory block /// \return iterator pointing beyond the last element in the memory block /// \since Crypto++ 2.0 iterator end() {return m_ptr+m_size;} /// \brief Provides a constant iterator pointing beyond the last element in the memory block /// \return constant iterator pointing beyond the last element in the memory block /// \since Crypto++ 2.0 const_iterator end() const {return m_ptr+m_size;} /// \brief Provides a pointer to the first element in the memory block /// \return pointer to the first element in the memory block /// \since Crypto++ 2.0 typename A::pointer data() {return m_ptr;} /// \brief Provides a pointer to the first element in the memory block /// \return constant pointer to the first element in the memory block /// \since Crypto++ 2.0 typename A::const_pointer data() const {return m_ptr;} /// \brief Provides the count of elements in the SecBlock /// \return number of elements in the memory block /// \note the return value is the count of elements, and not the number of bytes /// \since Crypto++ 2.0 size_type size() const {return m_size;} /// \brief Determines if the SecBlock is empty /// \return true if number of elements in the memory block is 0, false otherwise /// \since Crypto++ 2.0 bool empty() const {return m_size == 0;} /// \brief Provides a byte pointer to the first element in the memory block /// \return byte pointer to the first element in the memory block /// \since Crypto++ 2.0 byte * BytePtr() {return (byte *)m_ptr;} /// \brief Return a byte pointer to the first element in the memory block /// \return constant byte pointer to the first element in the memory block /// \since Crypto++ 2.0 const byte * BytePtr() const {return (const byte *)m_ptr;} /// \brief Provides the number of bytes in the SecBlock /// \return the number of bytes in the memory block /// \note the return value is the number of bytes, and not count of elements. /// \since Crypto++ 2.0 size_type SizeInBytes() const {return m_size*sizeof(T);} /// \brief Set contents and size from an array /// \param ptr a pointer to an array of T /// \param len the number of elements in the memory block /// \details The array pointed to by ptr must be distinct /// from this SecBlock because Assign() calls New() and then std::memcpy(). /// The call to New() will invalidate all pointers and iterators, like /// the pointer returned from data(). /// \details If the memory block is reduced in size, then the reclaimed /// memory is set to 0. If an assignment occurs, then Assign() resets /// the element count after the previous block is zeroized. /// \since Crypto++ 2.0 void Assign(const T *ptr, size_type len) { New(len); if (m_ptr && ptr) // GCC analyzer warning memcpy_s(m_ptr, m_size*sizeof(T), ptr, len*sizeof(T)); m_mark = ELEMS_MAX; } /// \brief Set contents from a value /// \param count the number of values to copy /// \param value the value, repeated count times /// \details If the memory block is reduced in size, then the reclaimed /// memory is set to 0. If an assignment occurs, then Assign() resets /// the element count after the previous block is zeroized. /// \since Crypto++ 6.0 void Assign(size_type count, T value) { New(count); for (size_t i=0; i &t) { if (this != &t) { New(t.m_size); if (m_ptr && t.m_ptr) // GCC analyzer warning memcpy_s(m_ptr, m_size*sizeof(T), t, t.m_size*sizeof(T)); } m_mark = ELEMS_MAX; } /// \brief Append contents from an array /// \param ptr a pointer to an array of T /// \param len the number of elements in the memory block /// \throw InvalidArgument if resulting size would overflow /// \details The array pointed to by ptr must be distinct /// from this SecBlock because Append() calls Grow() and then std::memcpy(). /// The call to Grow() will invalidate all pointers and iterators, like /// the pointer returned from data(). /// \details Append() may be less efficient than a ByteQueue because /// Append() must Grow() the internal array and then copy elements. /// The ByteQueue can copy elements without growing. /// \sa ByteQueue /// \since Crypto++ 8.6 void Append(const T *ptr, size_type len) { if (ELEMS_MAX - m_size < len) throw InvalidArgument("SecBlock: buffer overflow"); const size_type oldSize = m_size; Grow(m_size+len); if (m_ptr && ptr) // GCC analyzer warning memcpy_s(m_ptr+oldSize, (m_size-oldSize)*sizeof(T), ptr, len*sizeof(T)); m_mark = ELEMS_MAX; } /// \brief Append contents from another SecBlock /// \param t the other SecBlock /// \throw InvalidArgument if resulting size would overflow /// \details Internally, this SecBlock calls Grow() and then appends t. /// \details Append() may be less efficient than a ByteQueue because /// Append() must Grow() the internal array and then copy elements. /// The ByteQueue can copy elements without growing. /// \sa ByteQueue /// \since Crypto++ 8.6 void Append(const SecBlock &t) { if (ELEMS_MAX - m_size < t.m_size) throw InvalidArgument("SecBlock: buffer overflow"); const size_type oldSize = m_size; if (this != &t) // s += t { Grow(m_size+t.m_size); if (m_ptr && t.m_ptr) // GCC analyzer warning memcpy_s(m_ptr+oldSize, (m_size-oldSize)*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); } else // t += t { Grow(m_size*2); if (m_ptr) // GCC analyzer warning memmove_s(m_ptr+oldSize, (m_size-oldSize)*sizeof(T), m_ptr, oldSize*sizeof(T)); } m_mark = ELEMS_MAX; } /// \brief Append contents from a value /// \param count the number of values to copy /// \param value the value, repeated count times /// \throw InvalidArgument if resulting size would overflow /// \details Internally, this SecBlock calls Grow() and then appends value. /// \details Append() may be less efficient than a ByteQueue because /// Append() must Grow() the internal array and then copy elements. /// The ByteQueue can copy elements without growing. /// \sa ByteQueue /// \since Crypto++ 8.6 void Append(size_type count, T value) { if (ELEMS_MAX - m_size < count) throw InvalidArgument("SecBlock: buffer overflow"); const size_type oldSize = m_size; Grow(m_size+count); for (size_t i=oldSize; icount controls the number of /// elements zeroized, which can be less than size or 0. /// \details An internal variable, m_mark, is initialized to the maximum number /// of elements. The maximum number of elements is ELEMS_MAX. Deallocation /// triggers a zeroization, and the number of elements zeroized is /// STDMIN(m_size, m_mark). After zeroization, the memory is returned to the /// system. /// \details The ASN.1 decoder uses SetMark() to set the element count to 0 /// before throwing an exception. In this case, the attacker provides a large /// BER encoded length (say 64MB) but only a small number of content octets /// (say 16). If the allocator zeroized all 64MB, then a transient DoS could /// occur as CPU cycles are spent zeroizing uninitialized memory. /// \details Generally speaking, any operation which changes the size of the SecBlock /// results in the mark being reset to ELEMS_MAX. In particular, if Assign(), /// New(), Grow(), CleanNew(), CleanGrow() are called, then the count is reset to /// ELEMS_MAX. The list is not exhaustive. /// \since Crypto++ 6.0 /// \sa Issue 346/CVE-2016-9939 void SetMark(size_t count) {m_mark = count;} /// \brief Assign contents from another SecBlock /// \param t the other SecBlock /// \return reference to this SecBlock /// \details Internally, operator=() calls Assign(). /// \details If the memory block is reduced in size, then the reclaimed /// memory is set to 0. If an assignment occurs, then Assign() resets /// the element count after the previous block is zeroized. /// \since Crypto++ 2.0 SecBlock& operator=(const SecBlock &t) { // Assign guards for self-assignment Assign(t); return *this; } /// \brief Append contents from another SecBlock /// \param t the other SecBlock /// \return reference to this SecBlock /// \details Internally, operator+=() calls Append(). /// \since Crypto++ 2.0 SecBlock& operator+=(const SecBlock &t) { // Append guards for overflow Append(t); return *this; } /// \brief Construct a SecBlock from this and another SecBlock /// \param t the other SecBlock /// \return a newly constructed SecBlock that is a concatenation of this /// and t. /// \details Internally, a new SecBlock is created from this and a /// concatenation of t. /// \since Crypto++ 2.0 SecBlock operator+(const SecBlock &t) { CRYPTOPP_ASSERT((!m_ptr && !m_size) || (m_ptr && m_size)); CRYPTOPP_ASSERT((!t.m_ptr && !t.m_size) || (t.m_ptr && t.m_size)); if(!t.m_size) return SecBlock(*this); SecBlock result(m_size+t.m_size); if (m_size) memcpy_s(result.m_ptr, result.m_size*sizeof(T), m_ptr, m_size*sizeof(T)); if (result.m_ptr && t.m_ptr) // GCC analyzer warning memcpy_s(result.m_ptr+m_size, (result.m_size-m_size)*sizeof(T), t.m_ptr, t.m_size*sizeof(T)); return result; } /// \brief Bitwise compare two SecBlocks /// \param t the other SecBlock /// \return true if the size and bits are equal, false otherwise /// \details Uses a constant time compare if the arrays are equal size. /// The constant time compare is VerifyBufsEqual() found in /// misc.h. /// \sa operator!=() /// \since Crypto++ 2.0 bool operator==(const SecBlock &t) const { return m_size == t.m_size && VerifyBufsEqual( reinterpret_cast(m_ptr), reinterpret_cast(t.m_ptr), m_size*sizeof(T)); } /// \brief Bitwise compare two SecBlocks /// \param t the other SecBlock /// \return true if the size and bits are equal, false otherwise /// \details Uses a constant time compare if the arrays are equal size. /// The constant time compare is VerifyBufsEqual() found in /// misc.h. /// \details Internally, operator!=() returns the inverse of operator==(). /// \sa operator==() /// \since Crypto++ 2.0 bool operator!=(const SecBlock &t) const { return !operator==(t); } /// \brief Change size without preserving contents /// \param newSize the new size of the memory block /// \details Old content is not preserved. If the memory block is /// reduced in size, then the reclaimed content is set to 0. If the /// memory block grows in size, then the new memory is initialized /// to 0. New() resets the element count after the previous block /// is zeroized. /// \details Internally, this SecBlock calls reallocate(). /// \sa New(), CleanNew(), Grow(), CleanGrow(), resize() /// \since Crypto++ 2.0 void New(size_type newSize) { m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, false); m_size = newSize; m_mark = ELEMS_MAX; } /// \brief Change size without preserving contents /// \param newSize the new size of the memory block /// \details Old content is not preserved. If the memory block is /// reduced in size, then the reclaimed content is set to 0. If the /// memory block grows in size, then the new memory is initialized /// to 0. CleanNew() resets the element count after the previous /// block is zeroized. /// \details Internally, this SecBlock calls New(). /// \sa New(), CleanNew(), Grow(), CleanGrow(), resize() /// \since Crypto++ 2.0 void CleanNew(size_type newSize) { New(newSize); if (m_ptr) {memset_z(m_ptr, 0, m_size*sizeof(T));} m_mark = ELEMS_MAX; } /// \brief Change size and preserve contents /// \param newSize the new size of the memory block /// \details Old content is preserved. New content is not initialized. /// \details Internally, this SecBlock calls reallocate() when size must /// increase. If the size does not increase, then CleanGrow() does not /// take action. If the size must change, then use resize(). CleanGrow() /// resets the element count after the previous block is zeroized. /// \sa New(), CleanNew(), Grow(), CleanGrow(), resize() /// \sa New(), CleanNew(), Grow(), CleanGrow(), resize() /// \since Crypto++ 2.0 void Grow(size_type newSize) { if (newSize > m_size) { m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); m_size = newSize; } m_mark = ELEMS_MAX; } /// \brief Change size and preserve contents /// \param newSize the new size of the memory block /// \details Old content is preserved. New content is initialized to 0. /// \details Internally, this SecBlock calls reallocate() when size must /// increase. If the size does not increase, then CleanGrow() does not /// take action. If the size must change, then use resize(). CleanGrow() /// resets the element count after the previous block is zeroized. /// \sa New(), CleanNew(), Grow(), CleanGrow(), resize() /// \since Crypto++ 2.0 void CleanGrow(size_type newSize) { if (newSize > m_size) { m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); memset_z(m_ptr+m_size, 0, (newSize-m_size)*sizeof(T)); m_size = newSize; } m_mark = ELEMS_MAX; } /// \brief Change size and preserve contents /// \param newSize the new size of the memory block /// \details Old content is preserved. If the memory block grows in size, then /// new memory is not initialized. resize() resets the element count after /// the previous block is zeroized. /// \details Internally, this SecBlock calls reallocate(). /// \sa New(), CleanNew(), Grow(), CleanGrow(), resize() /// \since Crypto++ 2.0 void resize(size_type newSize) { m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true); m_size = newSize; m_mark = ELEMS_MAX; } /// \brief Swap contents with another SecBlock /// \param b the other SecBlock /// \details Internally, std::swap() is called on m_alloc, m_size and m_ptr. /// \since Crypto++ 2.0 void swap(SecBlock &b) { // Swap must occur on the allocator in case its FixedSize that spilled into the heap. std::swap(m_alloc, b.m_alloc); std::swap(m_mark, b.m_mark); std::swap(m_size, b.m_size); std::swap(m_ptr, b.m_ptr); } protected: A m_alloc; size_type m_mark, m_size; T *m_ptr; }; #ifdef CRYPTOPP_DOXYGEN_PROCESSING /// \brief \ref SecBlock "SecBlock" typedef. class SecByteBlock : public SecBlock {}; /// \brief \ref SecBlock "SecBlock" typedef. class SecWordBlock : public SecBlock {}; /// \brief SecBlock using \ref AllocatorWithCleanup "AllocatorWithCleanup" typedef class AlignedSecByteBlock : public SecBlock > {}; #else typedef SecBlock SecByteBlock; typedef SecBlock SecWordBlock; typedef SecBlock > AlignedSecByteBlock; #endif // No need for move semantics on derived class *if* the class does not add any // data members; see http://stackoverflow.com/q/31755703, and Rule of {0|3|5}. /// \brief Fixed size stack-based SecBlock /// \tparam T class or type /// \tparam S fixed-size of the stack-based memory block, in elements /// \tparam A AllocatorBase derived class for allocation and cleanup template > class FixedSizeSecBlock : public SecBlock { public: /// \brief Construct a FixedSizeSecBlock explicit FixedSizeSecBlock() : SecBlock(S) {} }; /// \brief Fixed size stack-based SecBlock with 16-byte alignment /// \tparam T class or type /// \tparam S fixed-size of the stack-based memory block, in elements /// \tparam T_Align16 boolean that determines whether allocations should be /// aligned on a 16-byte boundary template class FixedSizeAlignedSecBlock : public FixedSizeSecBlock, T_Align16> > { }; /// \brief Stack-based SecBlock that grows into the heap /// \tparam T class or type /// \tparam S fixed-size of the stack-based memory block, in elements /// \tparam A AllocatorBase derived class for allocation and cleanup template > > class SecBlockWithHint : public SecBlock { public: /// construct a SecBlockWithHint with a count of elements explicit SecBlockWithHint(size_t size) : SecBlock(size) {} }; template inline bool operator==(const CryptoPP::AllocatorWithCleanup&, const CryptoPP::AllocatorWithCleanup&) {return (true);} template inline bool operator!=(const CryptoPP::AllocatorWithCleanup&, const CryptoPP::AllocatorWithCleanup&) {return (false);} NAMESPACE_END NAMESPACE_BEGIN(std) /// \brief Swap two SecBlocks /// \tparam T class or type /// \tparam A AllocatorBase derived class for allocation and cleanup /// \param a the first SecBlock /// \param b the second SecBlock template inline void swap(CryptoPP::SecBlock &a, CryptoPP::SecBlock &b) { a.swap(b); } #if defined(_STLP_DONT_SUPPORT_REBIND_MEMBER_TEMPLATE) || (defined(_STLPORT_VERSION) && !defined(_STLP_MEMBER_TEMPLATE_CLASSES)) // working for STLport 5.1.3 and MSVC 6 SP5 template inline CryptoPP::AllocatorWithCleanup<_Tp2>& __stl_alloc_rebind(CryptoPP::AllocatorWithCleanup<_Tp1>& __a, const _Tp2*) { return (CryptoPP::AllocatorWithCleanup<_Tp2>&)(__a); } #endif NAMESPACE_END #if defined(CRYPTOPP_MSC_VERSION) # pragma warning(pop) #endif #endif