diff --git a/secblock.h b/secblock.h
index 6eb3b5cf..f4059222 100644
--- a/secblock.h
+++ b/secblock.h
@@ -42,13 +42,29 @@ public:
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.
+ //! \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_CXX11) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)
+ 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
//! \returns 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 (SIZE_MAX/sizeof(T));}
+ CRYPTOPP_CONSTEXPR size_type max_size() const {return ELEMS_MAX;}
#if defined(CRYPTOPP_CXX11_VARIADIC_TEMPLATES) || defined(CRYPTOPP_DOXYGEN_PROCESSING)
@@ -87,7 +103,7 @@ protected:
static void CheckSize(size_t size)
{
// C++ throws std::bad_alloc (C++03) or std::bad_array_new_length (C++11) here.
- if (size > (SIZE_MAX/sizeof(T)))
+ if (size > ELEMS_MAX)
throw InvalidArgument("AllocatorBase: requested size would cause integer overflow");
}
};
@@ -486,13 +502,29 @@ public:
typedef typename A::const_pointer const_iterator;
typedef typename A::size_type size_type;
+ //! \brief Returns the maximum number of elements the allocator can provide
+ //! \details ELEMS_MAX is the maximum number of elements the
+ //! SecBlock can hold.
+ //! \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_CXX11) && !defined(CRYPTOPP_DOXYGEN_PROCESSING)
+ enum : size_type { ELEMS_MAX = A::ELEMS_MAX };
+#else
+ static const size_type ELEMS_MAX = A::ELEMS_MAX;
+#endif
+
//! \brief Construct a SecBlock with space for size elements.
//! \param size the size of the allocation, in elements
//! \throws std::bad_alloc
//! \details The elements are not initialized.
//! \note size is the count of elements, and not the number of bytes
explicit SecBlock(size_type size=0)
- : m_mark(SIZE_MAX/sizeof(T)), m_size(size), m_ptr(m_alloc.allocate(size, NULLPTR)) { }
+ : 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
@@ -512,7 +544,7 @@ public:
//! Otherwise, the block is empty and not initialized.
//! \note size is the count of elements, and not the number of bytes
SecBlock(const T *ptr, size_type len)
- : m_mark(SIZE_MAX/sizeof(T)), m_size(len), m_ptr(m_alloc.allocate(len, NULLPTR)) {
+ : 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 (ptr && m_ptr)
memcpy_s(m_ptr, m_size*sizeof(T), ptr, len*sizeof(T));
@@ -587,17 +619,21 @@ public:
//! preserving the streaming interface. The count 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. 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.
+ //! 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 unintialized memory.
- //! \details If Assign(), New(), Grow(), CleanNew(), CleanGrow() are called, then the
- //! count is reset to its default state, which is the maxmimum number of elements.
+ //! \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 Set contents and size from an array
@@ -608,8 +644,9 @@ public:
void Assign(const T *ptr, size_type len)
{
New(len);
- if (m_ptr && ptr && len)
+ if (m_ptr && ptr)
{memcpy_s(m_ptr, m_size*sizeof(T), ptr, len*sizeof(T));}
+ m_mark = ELEMS_MAX;
}
//! \brief Copy contents from another SecBlock
@@ -623,9 +660,10 @@ public:
if (this != &t)
{
New(t.m_size);
- if (m_ptr && t.m_ptr && t.m_size)
+ if (m_ptr && t.m_ptr)
{memcpy_s(m_ptr, m_size*sizeof(T), t, t.m_size*sizeof(T));}
}
+ m_mark = ELEMS_MAX;
}
//! \brief Assign contents from another SecBlock
@@ -661,6 +699,7 @@ public:
memcpy_s(m_ptr+oldSize, (m_size-oldSize)*sizeof(T), m_ptr, oldSize*sizeof(T));
}
}
+ m_mark = ELEMS_MAX;
return *this;
}
@@ -716,7 +755,7 @@ public:
{
m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, false);
m_size = newSize;
- m_mark = SIZE_MAX/sizeof(T);
+ m_mark = ELEMS_MAX;
}
//! \brief Change size without preserving contents
@@ -731,6 +770,7 @@ public:
{
New(newSize);
if (m_ptr) {memset_z(m_ptr, 0, m_size*sizeof(T));}
+ m_mark = ELEMS_MAX;
}
//! \brief Change size and preserve contents
@@ -747,8 +787,8 @@ public:
{
m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true);
m_size = newSize;
- m_mark = SIZE_MAX/sizeof(T);
}
+ m_mark = ELEMS_MAX;
}
//! \brief Change size and preserve contents
@@ -766,8 +806,8 @@ public:
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 = SIZE_MAX/sizeof(T);
}
+ m_mark = ELEMS_MAX;
}
//! \brief Change size and preserve contents
@@ -781,7 +821,7 @@ public:
{
m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true);
m_size = newSize;
- m_mark = SIZE_MAX/sizeof(T);
+ m_mark = ELEMS_MAX;
}
//! \brief Swap contents with another SecBlock