Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions doc/dox_comments/header_files/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -656,3 +656,42 @@ void wc_UnloadStaticMemory(WOLFSSL_HEAP_HINT* heap);
int wolfSSL_StaticBufferSz_ex(unsigned int listSz,
const word32 *sizeList, const word32 *distList,
byte* buffer, word32 sz, int flag);

/*!
\ingroup Memory

\brief Reallocates memory from XMALLOC(), zeroizing the old block before
it is freed. A new block is allocated with XMALLOC(), the leading
min(oldSz, size) bytes are copied, oldSz bytes of ptr are zeroized,
and ptr is freed with XFREE(). If XMALLOC() returns ptr itself, as fixed IO
pools (WOLFMEM_IO_POOL_FIXED) do for IO types, the data stays in place and
only the bytes past size are zeroized. Not called directly by wolfSSL, but
through macro XREALLOC_SCRUBBED.

\return pointer to the new block on success.
\return NULL when size is 0 (ptr is zeroized and freed), or on failure
(ptr is left intact and not zeroized).

\param ptr NULL, or memory from XMALLOC()/XREALLOC(). A NULL ptr behaves
as XMALLOC(size).
\param oldSz current size of ptr in bytes. Must not exceed the real
allocation size, or the copy and zeroization overrun it.
\param size new size in bytes
\param heap heap hint. Can be NULL
\param type dynamic type

_Example_
\code
byte* key = (byte*)XMALLOC(32, NULL, DYNAMIC_TYPE_TMP_BUFFER);
byte* tmp;
...
tmp = (byte*)wc_ReallocScrubbed(key, 32, 64, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (tmp != NULL)
key = tmp;
\endcode

\sa XREALLOC_SCRUBBED
*/
void *wc_ReallocScrubbed(void *ptr, size_t oldSz, size_t size,
void *heap, int type);
28 changes: 28 additions & 0 deletions doc/dox_comments/header_files/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,34 @@ void* XREALLOC(void *p, size_t n, void* heap, int type);
*/
void XFREE(void *p, void* heap, int type);

/*!
\ingroup Memory

\brief This is not actually a function, but rather a preprocessor macro,
which reallocates memory from XMALLOC and zeroizes the old block before
it is freed. A new block is allocated with XMALLOC, the leading
min(oldSz, n) bytes are copied, oldSz bytes of p are zeroized, and p
is freed with XFREE. If XMALLOC returns p itself, as fixed IO pools
(WOLFMEM_IO_POOL_FIXED) do for IO types, the data stays in place and only
the bytes past n are zeroized. By default it maps to wc_ReallocScrubbed().

\return pointer to the new block on success.
\return NULL when n is 0 (p is zeroized and freed), or on failure (p is
left intact and not zeroized).

\param p NULL, or memory from XMALLOC/XREALLOC. A NULL p behaves as
XMALLOC.
\param oldSz current size of p in bytes. Must not exceed the real
allocation size, or the copy and zeroization overrun it.
\param n new size in bytes
\param h pointer to the heap to use
\param t memory allocation type for user hints. See enum in types.h

\sa wc_ReallocScrubbed
*/
void* XREALLOC_SCRUBBED(void *p, size_t oldSz, size_t n, void* heap,
int type);

/*!
\ingroup Math

Expand Down
21 changes: 7 additions & 14 deletions src/pk.c
Original file line number Diff line number Diff line change
Expand Up @@ -495,20 +495,13 @@ static int der_to_enc_pem_alloc(unsigned char* der, int derSz,
int blockSz = wolfSSL_EVP_CIPHER_block_size(cipher);
byte *tmpBuf;

/* Add space for padding. */
#ifdef WOLFSSL_NO_REALLOC
tmpBuf = (byte*)XMALLOC((size_t)(derSz + blockSz), heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (tmpBuf != NULL && der != NULL)
{
XMEMCPY(tmpBuf, der, (size_t)(derSz));
XFREE(der, heap, DYNAMIC_TYPE_TMP_BUFFER);
der = NULL;
}
#else
tmpBuf = (byte*)XREALLOC(der, (size_t)(derSz + blockSz), heap,
DYNAMIC_TYPE_TMP_BUFFER);
#endif
/* Add space for padding. der holds a plaintext private key DER:
* XREALLOC_SCRUBBED() ForceZero()s the old allocation before
* releasing it, so no key bytes are abandoned on the free list.
* (It never uses native realloc(), so it also subsumes the
* WOLFSSL_NO_REALLOC case.) */
tmpBuf = (byte*)XREALLOC_SCRUBBED(der, (size_t)derSz,
(size_t)(derSz + blockSz), heap, DYNAMIC_TYPE_TMP_BUFFER);
if (tmpBuf == NULL) {
WOLFSSL_ERROR_MSG("Extending DER buffer failed");
ret = 0; /* der buffer is free'd at the end of the function */
Expand Down
136 changes: 136 additions & 0 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,108 @@ static int test_wc_LoadStaticMemory_ex(void)
}


#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY_LEAN) && \
!defined(NO_WOLFSSL_CLIENT) && (WOLFMEM_IO_SZ >= 16384) && \
(WOLFMEM_MAX_BUCKETS >= WOLFMEM_DEF_BUCKETS) && \
!defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_HAVE_FRODOKEM)
#define TEST_LSM_FIXED_IO

/* Returns 1 when len bytes at p all equal val. */
static int test_lsm_bytes_eq(const byte* p, word32 len, byte val)
{
word32 i;

for (i = 0; i < len; i++) {
if (p[i] != val)
return 0;
}
return 1;
}

/* A fixed IO buffer can't grow: a request larger than it must fail rather
* than hand back a buffer that is too small. XREALLOC_SCRUBBED() of a fixed
* IO buffer gets the same block back from XMALLOC(), so it must keep the
* data in place and zero only the bytes past the new size. */
static int test_lsm_fixed_io_malloc(WOLFSSL* ssl)
{
EXPECT_DECLS;
const int tag = DYNAMIC_TYPE_IN_BUFFER;
void* heap = ssl->heap;
byte* ioBuf = NULL;

ExpectNull(XMALLOC(WOLFMEM_IO_SZ + 1, heap, tag));
ExpectNotNull(ioBuf = (byte*)XMALLOC(WOLFMEM_IO_SZ, heap, tag));

if (ioBuf != NULL) {
/* shrink: kept bytes stay in place, the tail is zeroed */
XMEMSET(ioBuf, 0xA5, 128);
ExpectPtrEq(XREALLOC_SCRUBBED(ioBuf, 128, 64, heap, tag), ioBuf);
ExpectIntEQ(test_lsm_bytes_eq(ioBuf, 64, 0xA5), 1);
ExpectIntEQ(test_lsm_bytes_eq(ioBuf + 64, 64, 0x00), 1);

/* grow within the buffer: data kept in place */
XMEMSET(ioBuf, 0x5A, 64);
ExpectPtrEq(XREALLOC_SCRUBBED(ioBuf, 64, WOLFMEM_IO_SZ, heap, tag),
ioBuf);
ExpectIntEQ(test_lsm_bytes_eq(ioBuf, 64, 0x5A), 1);

/* grow past the buffer: fails, original intact and unscrubbed */
ExpectNull(XREALLOC_SCRUBBED(ioBuf, 64, WOLFMEM_IO_SZ + 1, heap,
tag));
ExpectIntEQ(test_lsm_bytes_eq(ioBuf, 64, 0x5A), 1);
}
XFREE(ioBuf, heap, tag);

return EXPECT_RESULT();
}

/* A request too large for a shared IO buffer must not use one up: it comes
* from general memory if a bucket is large enough, otherwise it fails. */
static int test_lsm_shared_io_malloc(WOLFSSL_HEAP_HINT* heap)
{
EXPECT_DECLS;
const int tag = DYNAMIC_TYPE_IN_BUFFER;
WOLFSSL_MEM_STATS stats;
word32 avaIO = 0;
word32 tooBig = WOLFMEM_IO_SZ;
int i;
byte* ioBuf = NULL;
byte* general = NULL;

ExpectIntEQ(wolfSSL_GetMemStats(heap->memory, &stats), 1);
avaIO = stats.avaIO;
ExpectIntGT(avaIO, 0);
/* larger than every bucket, whatever WOLFMEM_BUCKETS holds */
for (i = 0; i < WOLFMEM_MAX_BUCKETS; i++) {
if (stats.blockSz[i] > tooBig)
tooBig = stats.blockSz[i];
}
tooBig++;

ExpectNotNull(general = (byte*)XMALLOC(WOLFMEM_IO_SZ + 1, heap, tag));
ExpectIntEQ(wolfSSL_GetMemStats(heap->memory, &stats), 1);
ExpectIntEQ(stats.avaIO, avaIO);

ExpectNull(XMALLOC(tooBig, heap, tag));
ExpectIntEQ(wolfSSL_GetMemStats(heap->memory, &stats), 1);
ExpectIntEQ(stats.avaIO, avaIO);

ExpectNotNull(ioBuf = (byte*)XMALLOC(WOLFMEM_IO_SZ, heap, tag));
ExpectIntEQ(wolfSSL_GetMemStats(heap->memory, &stats), 1);
ExpectIntEQ(stats.avaIO, avaIO - 1);

/* Each block must go back to the list that it came from. */
XFREE(general, heap, tag);
ExpectIntEQ(wolfSSL_GetMemStats(heap->memory, &stats), 1);
ExpectIntEQ(stats.avaIO, avaIO - 1);
XFREE(ioBuf, heap, tag);
ExpectIntEQ(wolfSSL_GetMemStats(heap->memory, &stats), 1);
ExpectIntEQ(stats.avaIO, avaIO);

return EXPECT_RESULT();
}
#endif /* WOLFSSL_STATIC_MEMORY && !WOLFSSL_STATIC_MEMORY_LEAN && ... */

static int test_wc_LoadStaticMemory_CTX(void)
{
EXPECT_DECLS;
Expand All @@ -595,6 +697,12 @@ static int test_wc_LoadStaticMemory_CTX(void)
word32 distList[TEST_LSM_DEF_BUCKETS] = { TEST_LSM_DIST };
WOLFSSL_HEAP_HINT* heap;
WOLFSSL_CTX *ctx1 = NULL, *ctx2 = NULL;
#ifdef TEST_LSM_FIXED_IO
/* Two IO buffers, each with its management data rounded up to
* WOLFSSL_STATIC_ALIGN, plus slack for aligning the start of the array. */
byte ioMemory[2 * (WOLFMEM_IO_SZ + 64 + WOLFSSL_STATIC_ALIGN)];
WOLFSSL* ssl = NULL;
#endif


/* Set the size of the static buffer to exactly the minimum size. */
Expand All @@ -621,6 +729,34 @@ static int test_wc_LoadStaticMemory_CTX(void)
wolfSSL_CTX_free(ctx1);
wolfSSL_CTX_free(ctx2);

#ifdef TEST_LSM_FIXED_IO
/* An SSL object's fixed IO buffers. */
ctx1 = NULL;
ExpectNotNull((ctx1 = wolfSSL_CTX_new_ex(wolfSSLv23_client_method_ex(heap),
heap)));
ExpectIntEQ(wolfSSL_CTX_load_static_memory(&ctx1, NULL, ioMemory,
sizeof(ioMemory), WOLFMEM_IO_POOL_FIXED, 0), WOLFSSL_SUCCESS);
ExpectNotNull(ssl = wolfSSL_new(ctx1));
if (EXPECT_SUCCESS()) {
ExpectIntEQ(test_lsm_fixed_io_malloc(ssl), TEST_SUCCESS);
}
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx1);

/* Shared IO buffers, with a general bucket larger than an IO buffer. */
wc_UnloadStaticMemory(heap);
heap = NULL;
sizeList[WOLFMEM_DEF_BUCKETS - 1] = 2 * WOLFMEM_IO_SZ;
distList[WOLFMEM_DEF_BUCKETS - 1] = 1;
ExpectIntEQ(wc_LoadStaticMemory_ex(&heap, WOLFMEM_DEF_BUCKETS, sizeList,
distList, staticMemory, sizeof(staticMemory), 0, 0), 0);
ExpectIntEQ(wc_LoadStaticMemory_ex(&heap, WOLFMEM_DEF_BUCKETS, sizeList,
distList, ioMemory, sizeof(ioMemory), WOLFMEM_IO_POOL, 0), 0);
if (EXPECT_SUCCESS()) {
ExpectIntEQ(test_lsm_shared_io_malloc(heap), TEST_SUCCESS);
}
#endif

wc_UnloadStaticMemory(heap);
#endif /* WOLFSSL_STATIC_MEMORY */
return EXPECT_RESULT();
Expand Down
8 changes: 7 additions & 1 deletion wolfcrypt/src/integer.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,13 @@ int mp_grow (mp_int * a, int size)
* in case the operation failed we don't want
* to overwrite the dp member of a.
*/
tmp = (mp_digit *)XREALLOC (a->dp, sizeof (mp_digit) * size, NULL,
/* mp_ints routinely hold private keys and other secret values, so the
* old digit array must not be abandoned on the free list with live
* limbs: XREALLOC_SCRUBBED() ForceZero()s it before releasing it. This
* also scrubs public values, an acceptable cost for correctness. */
tmp = (mp_digit *)XREALLOC_SCRUBBED (a->dp,
sizeof (mp_digit) * a->alloc,
sizeof (mp_digit) * size, NULL,
DYNAMIC_TYPE_BIGINT);
if (tmp == NULL) {
/* reallocation failed but "a" is still valid [can be freed] */
Expand Down
8 changes: 7 additions & 1 deletion wolfcrypt/src/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,11 @@ void* wolfSSL_Malloc(size_t size, void* heap, int type)
if (type == DYNAMIC_TYPE_IN_BUFFER) {
pt = hint->inBuf;
}
/* fixed IO buffers can't grow: fail rather than overflow */
if ((pt != NULL) && (pt->sz < size)) {
WOLFSSL_MSG("Error IO memory was not large enough");
pt = NULL;
}
}
else
#endif
Expand All @@ -1218,7 +1223,8 @@ void* wolfSSL_Malloc(size_t size, void* heap, int type)
if (mem->flag & WOLFMEM_IO_POOL &&
(type == DYNAMIC_TYPE_OUT_BUFFER ||
type == DYNAMIC_TYPE_IN_BUFFER)) {
if (mem->io != NULL) {
/* too large for an IO buffer: try the general memory below */
if ((mem->io != NULL) && (size <= mem->io->sz)) {
pt = mem->io;
mem->io = pt->next;
}
Expand Down
69 changes: 69 additions & 0 deletions wolfcrypt/src/wc_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ Threading/Mutex options:

#include <wolfssl/wolfcrypt/libwolfssl_sources.h>

#ifndef WOLFSSL_NO_FORCE_ZERO
/* ForceZero() for the scrubbing reallocator below */
#ifdef NO_INLINE
#include <wolfssl/wolfcrypt/misc.h>
#else
#define WOLFSSL_MISC_INCLUDED
#include <wolfcrypt/src/misc.c>
#endif
#endif

#ifdef __APPLE__
#include <AvailabilityMacros.h>
#endif
Expand Down Expand Up @@ -803,6 +813,65 @@ WC_NO_INLINE void wc_BarrierDataSink(void* p)
}
#endif /* WC_BARRIER_DATA_USES_SINK */

/* Scrubbing reallocator behind XREALLOC_SCRUBBED() (types.h). It lives here
* rather than in memory.c because types.h binds it unconditionally, and
* wc_port.c is built in every configuration, including --disable-memory /
* NO_WOLFSSL_MEMORY builds that omit memory.c. */

static void wc_ReallocScrub(void *ptr, size_t len)
{
#ifdef WOLFSSL_NO_FORCE_ZERO
/* volatile stores: a plain XMEMSET() just ahead of XFREE() is a dead
* store the compiler may remove. */
volatile byte *p = (volatile byte *)ptr;
while (len-- > 0)
*p++ = 0;
#else
ForceZero(ptr, len);
#endif
}

/* Reallocation that zeroizes the old block before releasing it, composed over
* the XMALLOC()/XFREE() macro bindings so that port overrides, wolfSSL memory
* callbacks, and static pools are all honored. Never uses native realloc(),
* which may release the old block without clearing it. On failure (NULL
* return) the original allocation is intact and unscrubbed. */
void *wc_ReallocScrubbed(void *ptr, size_t oldSz, size_t size,
void *heap, int type)
{
void *newPtr;

(void)heap;
(void)type;

if (ptr == NULL && size != 0)
return XMALLOC(size, heap, type);
else if (ptr != NULL && size == 0) {
/* realloc-to-zero idiom: scrub, free, and return NULL. */
wc_ReallocScrub(ptr, oldSz);
XFREE(ptr, heap, type);
return NULL;
}
else if (ptr == NULL && size == 0) {
return NULL;
}
newPtr = XMALLOC(size, heap, type);
if (newPtr == NULL)
return NULL; /* original intact and unscrubbed */
if (newPtr == ptr) {
/* fixed IO pools hand back the block already in use */
if (oldSz > size) {
wc_ReallocScrub((byte*)ptr + size, oldSz - size);
}
}
else {
XMEMMOVE(newPtr, ptr, (oldSz < size) ? oldSz : size);
wc_ReallocScrub(ptr, oldSz);
}
XFREE(ptr, heap, type);
return newPtr;
}

/* Used to initialize state for wolfcrypt
return 0 on success
*/
Expand Down
Loading
Loading