The patch below (1) documents the interface, including when the underlying
array is uninitialized versus set to 0; (2) adds move semantics; (3) makes
its members private; (4) adds asserts for some sanity checks in Debug
builds; (5) guards calls to memset and memcpy for NULL pointers and
0-sizes; (6) prefers initialization over assignment; and (7) switches to
numeric_limits::max() for sizes.
Regarding (3), two classes, Salsa and Sosemanuk, reached in. They are now
using SecBlock::data() to access the block. That will be a separate
proposal.
Regarding (5), its undefined behavior to call memset or memcpy (and
friends) with a NULL pointer. The current behavior of unchecked calls
lights up an assert in misc.h.
Regarding (6), "initialization over assignment" provides stronger exception
safety. Here's what a typical change looks like:
SecBlock(const T *t, size_type len)
- : m_size(len) {m_ptr = m_alloc.allocate(m_size, NULL));
memcpy(m_ptr,t, m_size*sizeof(T));}
+ : m_size(len), m_ptr(m_alloc.allocate(m_size, NULL)) { if(m_ptr &&
m_size){memcpy(m_ptr,t, m_size*sizeof(T));}}
+
Regarding (7), use of max() collided with Microsoft macros of the same
name. The fix was to define NOMINMAX in a way that did not cross pollinate
into user code through the header. For that, we needed to push and pop the
relevant macros.
Any comments or objections?
**********
$ cat secblock.h.diff
diff --git a/secblock.h b/secblock.h
index 907bca8..8c2e428
--- a/secblock.h
+++ b/secblock.h
@@ -3,7 +3,17 @@
#ifndef CRYPTOPP_SECBLOCK_H
#define CRYPTOPP_SECBLOCK_H
+// https://support.microsoft.com/en-us/kb/143208
+#if (_MSC_VER)
+# pragma push_macro("NOMINMAX")
+# pragma push_macro("min")
+# pragma push_macro("max")
+# undef min
+# undef max
+#endif
+
#include "config.h"
+#include "stdcpp.h"
#include "misc.h"
#if GCC_DIAGNOSTIC_AWARE
@@ -37,12 +47,12 @@ public:
const_pointer address(const_reference r) const {return (&r); }
void construct(pointer p, const T& val) {new (p) T(val);}
void destroy(pointer p) {p->~T();}
- size_type max_size() const {return ~size_type(0)/sizeof(T);} //
switch to std::numeric_limits<T>::max later
+ size_type max_size() const {return
std::numeric_limits<size_type>::max()/sizeof(T);}
protected:
static void CheckSize(size_t n)
{
- if (n > ~size_t(0) / sizeof(T))
+ if (n > std::numeric_limits<AllocatorBase::size_type>::max() /
sizeof(T))
throw InvalidArgument("AllocatorBase: requested size would
cause integer overflow");
}
};
@@ -71,7 +81,8 @@ typename A::pointer StandardReallocate(A& a, T *p,
typename A::size_type oldSize
if (preserve)
{
typename A::pointer newPointer = a.allocate(newSize, NULL);
- memcpy_s(newPointer, sizeof(T)*newSize, p,
sizeof(T)*STDMIN(oldSize, newSize));
+ const size_t size = sizeof(T)*STDMIN(oldSize, newSize);
+ if(p && size) {memcpy(newPointer, p, size);}
a.deallocate(p, oldSize);
return newPointer;
}
@@ -107,8 +118,10 @@ public:
}
void deallocate(void *p, size_type n)
- {
+ { CRYPTOPP_ASSERT((p && n) || (!p && !n));
SecureWipeArray((pointer)p, n);
+ // CRYPTOPP_ASSERT((n == 0) || (n > 0 && ((T*)p)[0] == 0));
+ // CRYPTOPP_ASSERT((n == 0) || (n > 0 && ((T*)p)[sizeof(T)*n-1] ==
0));
#if CRYPTOPP_BOOL_ALIGN16_ENABLED
if (T_Align16 && n*sizeof(T) >= 16)
@@ -203,6 +216,8 @@ public:
CRYPTOPP_ASSERT(m_allocated);
m_allocated = false;
SecureWipeArray((pointer)p, n);
+ // CRYPTOPP_ASSERT((n == 0) || (n > 0 && ((T*)p)[0] == 0));
+ // CRYPTOPP_ASSERT((n == 0) || (n > 0 &&
((T*)p)[sizeof(T)*n-1] == 0));
}
else
m_fallbackAllocator.deallocate(p, n);
@@ -219,7 +234,7 @@ public:
}
pointer newPointer = allocate(newSize, NULL);
- if (preserve)
+ if (preserve && newSize)
memcpy(newPointer, p, sizeof(T)*STDMIN(oldSize, newSize));
deallocate(p, oldSize);
return newPointer;
@@ -249,19 +264,31 @@ public:
typedef typename A::const_pointer const_iterator;
typedef typename A::size_type size_type;
+ //! construct a SecBlock with space for 'size' elements. To initialize
the elements to 0, create a SecBlock and then call CleanNew or CleanGrow.
explicit SecBlock(size_type size=0)
- : m_size(size) {m_ptr = m_alloc.allocate(size, NULL);}
+ : m_size(size), m_ptr(m_alloc.allocate(size, NULL)) { }
+ //! copy construct a SecBlock from another SecBlock
SecBlock(const SecBlock<T, A> &t)
- : m_size(t.m_size) {m_ptr = m_alloc.allocate(m_size, NULL);
memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, m_size*sizeof(T));}
+ : m_size(t.m_size), m_ptr(m_alloc.allocate(m_size, NULL)) {
CRYPTOPP_ASSERT((!t.m_ptr && !m_size) || (t.m_ptr && m_size)); if(t.m_ptr
&& t.m_size){memcpy(m_ptr,t.m_ptr, m_size*sizeof(T));}}
+ //! construct a SecBlock from an array of elements
SecBlock(const T *t, size_type len)
- : m_size(len)
+ : m_size(len), m_ptr(m_alloc.allocate(m_size, NULL)) {
CRYPTOPP_ASSERT((!m_ptr && !m_size) || (m_ptr && m_size)); if(m_ptr &&
m_size){memcpy(m_ptr,t, m_size*sizeof(T));}}
+
+#if (CRYPTOPP_CXX11_RVALUES && CRYPTOPP_CXX11_MOVE)
+ SecBlock(SecBlock&& t)
+ : m_alloc(std::move(t.m_alloc)), m_size(t.m_size),
m_ptr(std::move(t.m_ptr))
{
- m_ptr = m_alloc.allocate(len, NULL);
- if (t == NULL)
- memset_z(m_ptr, 0, len*sizeof(T));
- else
- memcpy(m_ptr, t, len*sizeof(T));
+ // TODO: research the move on the Allocator, and remove it if not
needed.
+ t.m_alloc = A();
+ t.m_size = 0;
+ t.m_ptr = NULL;
}
+ SecBlock& operator=(SecBlock&& t)
+ {
+ swap(t);
+ return *this;
+ }
+#endif
~SecBlock()
{m_alloc.deallocate(m_ptr, m_size);}
@@ -281,42 +308,42 @@ public:
{return m_ptr;}
#endif
-// T *operator +(size_type offset)
-// {return m_ptr+offset;}
-
-// const T *operator +(size_type offset) const
-// {return m_ptr+offset;}
-
-// T& operator[](size_type index)
-// {CRYPTOPP_ASSERT(index >= 0 && index < m_size); return
m_ptr[index];}
-
-// const T& operator[](size_type index) const
-// {CRYPTOPP_ASSERT(index >= 0 && index < m_size); return
m_ptr[index];}
-
+ //! provide an iterator to the first element of the array
iterator begin()
{return m_ptr;}
+ //! provide a constant iterator to the first element of the array
const_iterator begin() const
{return m_ptr;}
+ //! provide an iterator set beyond the last element of the array
iterator end()
{return m_ptr+m_size;}
+ //! provide a constant iterator set beyond the last element of the
array
const_iterator end() const
{return m_ptr+m_size;}
+ //! return a pointer to the first element in the array
typename A::pointer data() {return m_ptr;}
+ //! return a constant pointer to the first element in the array
typename A::const_pointer data() const {return m_ptr;}
+ //! return the number of elements in the array
size_type size() const {return m_size;}
+ //! return the number of elements in the array
bool empty() const {return m_size == 0;}
+ //! return a byte pointer to the first element in the array
byte * BytePtr() {return (byte *)m_ptr;}
+ //! return a byte pointer to the first element in the array
const byte * BytePtr() const {return (const byte *)m_ptr;}
+ //! return the number of bytes in the array
size_type SizeInBytes() const {return m_size*sizeof(T);}
- //! set contents and size
+ //! set contents and size from an array
void Assign(const T *t, size_type len)
{
+ // New wipes the tail of the previous array if its reduced in size
New(len);
- memcpy_s(m_ptr, m_size*sizeof(T), t, len*sizeof(T));
+ if(t && len) {memcpy(m_ptr, t, m_size*sizeof(T));}
}
//! copy contents and size from another SecBlock
@@ -324,11 +351,13 @@ public:
{
if (this != &t)
{
+ // New wipes the tail of the previous array if its reduced in
size
New(t.m_size);
- memcpy_s(m_ptr, m_size*sizeof(T), t.m_ptr, m_size*sizeof(T));
+ if(t.m_ptr && t.m_size)
{memcpy(m_ptr,t.m_ptr,t.m_size*sizeof(T));}
}
}
+ //! assign contents and size from another SecBlock
SecBlock<T, A>& operator=(const SecBlock<T, A> &t)
{
// Assign guards for self-assignment
@@ -339,46 +368,54 @@ public:
// append to this object
SecBlock<T, A>& operator+=(const SecBlock<T, A> &t)
{
+ CRYPTOPP_ASSERT((!t.m_ptr && !t.m_size) || (t.m_ptr && t.m_size));
+
size_type oldSize = m_size;
Grow(m_size+t.m_size);
- memcpy_s(m_ptr+oldSize, m_size*sizeof(T), t.m_ptr,
t.m_size*sizeof(T));
+ if(t.m_ptr && t.m_size)
{memcpy(m_ptr+oldSize,t.m_ptr,t.m_size*sizeof(T));}
return *this;
}
// append operator
SecBlock<T, A> operator+(const SecBlock<T, A> &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));
+
SecBlock<T, A> result(m_size+t.m_size);
- memcpy_s(result.m_ptr, result.m_size*sizeof(T), m_ptr,
m_size*sizeof(T));
- memcpy_s(result.m_ptr+m_size, t.m_size*sizeof(T), t.m_ptr,
t.m_size*sizeof(T));
+ if(m_ptr && m_size) {memcpy(result.m_ptr, m_ptr,
m_size*sizeof(T));}
+ if(t.m_ptr && t.m_size) {memcpy(result.m_ptr, t.m_ptr,
t.m_size*sizeof(T));}
return result;
}
+ //! bitwise compare two SecBlocks using a constant time compare if the
arrays are equal size
bool operator==(const SecBlock<T, A> &t) const
{
return m_size == t.m_size && VerifyBufsEqual(m_ptr, t.m_ptr,
m_size*sizeof(T));
}
+ //! bitwise compare two SecBlocks using a constant time compare if the
arrays are equal size
bool operator!=(const SecBlock<T, A> &t) const
{
return !operator==(t);
}
- //! change size, without preserving contents
+ //! change size without preserving contents. new content unintialized
void New(size_type newSize)
{
+ // reallocate wipes the tail of the array if its reduced in size
m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, false);
m_size = newSize;
}
- //! change size and set contents to 0
+ //! change size without preserving contents. all content set to 0
void CleanNew(size_type newSize)
{
New(newSize);
memset_z(m_ptr, 0, m_size*sizeof(T));
}
- //! change size only if newSize > current size. contents are preserved
+ //! change size only if newSize > current size. contents are
preserved, new content unintialized
void Grow(size_type newSize)
{
if (newSize > m_size)
@@ -388,18 +425,18 @@ public:
}
}
- //! change size only if newSize > current size. contents are preserved
and additional area is set to 0
+ //! change size only if newSize > current size. contents are
preserved, new content set to 0
void CleanGrow(size_type newSize)
{
if (newSize > m_size)
{
m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true);
- memset(m_ptr+m_size, 0, (newSize-m_size)*sizeof(T));
+ memset_z(m_ptr+m_size, 0, (newSize-m_size)*sizeof(T));
m_size = newSize;
}
}
- //! change size and preserve contents
+ //! change size and preserve contents. new content is uninitialized.
void resize(size_type newSize)
{
m_ptr = m_alloc.reallocate(m_ptr, m_size, newSize, true);
@@ -409,26 +446,33 @@ public:
//! swap contents and size with another SecBlock
void swap(SecBlock<T, A> &b)
{
+ // TODO: research the swap on the Allocator, and remove it if not
needed.
std::swap(m_alloc, b.m_alloc);
std::swap(m_size, b.m_size);
std::swap(m_ptr, b.m_ptr);
}
-//private:
+protected:
A m_alloc;
size_type m_size;
T *m_ptr;
};
-typedef SecBlock<byte> SecByteBlock;
+DOCUMENTED_TYPEDEF(SecBlock<byte>, SecByteBlock);
+DOCUMENTED_TYPEDEF(SecBlock<word>, SecWordBlock);
+// typedef SecBlock<byte> SecByteBlock;
typedef SecBlock<byte, AllocatorWithCleanup<byte, true> >
AlignedSecByteBlock;
-typedef SecBlock<word> SecWordBlock;
+// typedef SecBlock<word> SecWordBlock;
+
+// 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}.
//! a SecBlock with fixed size, allocated statically
template <class T, unsigned int S, class A =
FixedSizeAllocatorWithCleanup<T, S> >
class FixedSizeSecBlock : public SecBlock<T, A>
{
public:
+ //! construct a FixedSizeSecBlock
explicit FixedSizeSecBlock() : SecBlock<T, A>(S) {}
};
@@ -442,6 +486,7 @@ template <class T, unsigned int S, class A =
FixedSizeAllocatorWithCleanup<T, S,
class SecBlockWithHint : public SecBlock<T, A>
{
public:
+ //! construct a SecBlockWithHint with a count of elements
explicit SecBlockWithHint(size_t size) : SecBlock<T, A>(size) {}
};
@@ -472,7 +517,13 @@
__stl_alloc_rebind(CryptoPP::AllocatorWithCleanup<_Tp1>& __a, const _Tp2*)
NAMESPACE_END
#if GCC_DIAGNOSTIC_AWARE
-# pragma GCC diagnostic pop
+# pragma GCC diagnostic pop
+#endif
+
+#if (_MSC_VER)
+# pragma pop_macro("min")
+# pragma pop_macro("max")
+# pragma pop_macro("NOMINMAX")
#endif
#endif
--
--
You received this message because you are subscribed to the "Crypto++ Users"
Google Group.
To unsubscribe, send an email to [email protected].
More information about Crypto++ and this group is available at
http://www.cryptopp.com.
---
You received this message because you are subscribed to the Google Groups
"Crypto++ Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.