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
8 changes: 5 additions & 3 deletions cmake/functions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -976,9 +976,11 @@ function(generate_lib_src_list LIB_SOURCES)
wolfcrypt/src/wolfmath.c)
endif()

if(BUILD_MEMORY)
list(APPEND LIB_SOURCES wolfcrypt/src/memory.c)
endif()
# memory.c also holds the aligned/scrubbing allocators (wc_MallocAlign(),
# wc_ReallocScrubbed(), ...) that types.h binds unconditionally, so it is
# built even without memory callbacks; the callback code inside it is
# gated on USE_WOLFSSL_MEMORY.
list(APPEND LIB_SOURCES wolfcrypt/src/memory.c)

if(NOT BUILD_FIPS_RAND)
if(NOT BUILD_FIPS_V2 AND BUILD_DH)
Expand Down
182 changes: 182 additions & 0 deletions doc/dox_comments/header_files/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -636,3 +636,185 @@ 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 Aligned allocation over XMALLOC(), honoring heap and tag (static
memory pools, wolfSSL_SetAllocators() callbacks, user allocators, ports).
Over-allocates and stores a small header just below the returned pointer.
Called through macro XMALLOCALIGN.

\return pointer to aligned allocation
\return NULL if alignment is not 0 or a power of 2, the padded size
overflows, or the allocation fails.

\param alignment 0 or a power of 2; values below 2*sizeof(size_t) are
raised to it
\param size size, in bytes, of the memory to allocate
\param heap heap hint to use for memory. Can be NULL
\param tag dynamic type (see DYNAMIC_TYPE_ list in types.h)

_Example_
\code
byte* buf = (byte*)wc_MallocAlign(64, 1024, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
...
wc_FreeAlign(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
\endcode

\sa wc_FreeAlign
\sa wc_ReallocAlign
\sa XMALLOCALIGN
*/
void *wc_MallocAlign(size_t alignment, size_t size, void *heap, int tag);

/*!
\ingroup Memory

\brief Frees memory returned by wc_MallocAlign(), wc_ReallocAlign() or
wc_ReallocAlignScrubbed(), by calling XFREE(). Aligned and plain
(XMALLOC()) allocations must not be mixed. Not called directly by
wolfSSL, but through macro XFREEALIGN.

\return void

\param ptr aligned pointer to free. NULL is a no-op.
\param heap heap hint used for the allocation. Can be NULL
\param tag dynamic type used for the allocation

_Example_
\code
byte* buf = (byte*)wc_MallocAlign(64, 1024, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
...
wc_FreeAlign(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
\endcode

\sa wc_MallocAlign
\sa XFREEALIGN
*/
void wc_FreeAlign(void *ptr, void *heap, int tag);

/*!
\ingroup Memory

\brief Reallocates memory from wc_MallocAlign(), using XMALLOC() and
XFREE(). Never reallocates in place: a new block aligned to alignment is
allocated, the leading min(old size, size) bytes are copied, and ptr is
freed. Not called directly by wolfSSL, but through macro XREALLOCALIGN.

\return pointer to the new aligned block on success.
\return NULL when size is 0 (ptr is freed), or on failure (ptr is left
intact, including when alignment is rejected).

\param ptr NULL, or memory from wc_MallocAlign()/wc_ReallocAlign*().
A NULL ptr behaves as wc_MallocAlign().
\param alignment as for wc_MallocAlign()
\param size new size in bytes
\param heap heap hint. Can be NULL
\param tag dynamic type

_Example_
\code
byte* buf2 = NULL;
byte* buf = (byte*)wc_MallocAlign(64, 1024, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (buf == NULL)
- fail -

buf2 = (byte*)wc_ReallocAlign(buf, 64, 4000, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (buf2 == NULL)
- fail -
...

wc_FreeAlign(buf2, NULL, DYNAMIC_TYPE_TMP_BUFFER);
\endcode

\sa wc_ReallocAlignScrubbed
\sa wc_MallocAlign
\sa XREALLOCALIGN
*/
void *wc_ReallocAlign(void *ptr, size_t alignment, size_t size, void *heap,
int tag);

/*!
\ingroup Memory

\brief As wc_ReallocAlign(), but the old block is zeroized before it is
freed, including when size is 0. On failure the old block is left intact
and not zeroized. Not called directly by wolfSSL, but through macro
XREALLOCALIGN_SCRUBBED.

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

\param ptr NULL, or memory from wc_MallocAlign()/wc_ReallocAlign*()
\param alignment as for wc_MallocAlign()
\param size new size in bytes
\param heap heap hint. Can be NULL
\param tag dynamic type

_Example_
\code
byte* buf2 = NULL;
byte* buf = (byte*)wc_MallocAlign(64, 1024, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (buf == NULL)
- fail -

buf2 = (byte*)wc_ReallocAlignScrubbed(buf, 64, 4000, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (buf2 == NULL)
- fail -

- buf freed and zeroed post ReallocAlignScrubbed-
...

wc_FreeAlign(buf2, NULL, DYNAMIC_TYPE_TMP_BUFFER);
\endcode

\sa wc_ReallocAlign
\sa XREALLOCALIGN_SCRUBBED
*/
void *wc_ReallocAlignScrubbed(void *ptr, size_t alignment, size_t size,
void *heap, int tag);

/*!
\ingroup Memory

\brief Reallocates memory from XMALLOC(), zeroizing the old block before
it is freed. Never reallocates in place: a new block is allocated with
XMALLOC(), the leading min(old_size, size) bytes are copied, old_size
bytes of ptr are zeroized, and ptr is freed with XFREE(). 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 old_size 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 tag 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 wc_ReallocAlignScrubbed
\sa XREALLOC_SCRUBBED
*/
void *wc_ReallocScrubbed(void *ptr, size_t old_size, size_t size,
void *heap, int tag);
148 changes: 148 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,154 @@ 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 allocates memory aligned to a given boundary. By default it maps to
wc_MallocAlign(), which over-allocates through XMALLOC() and stores a
small header just below the returned pointer, so heap and type reach
every allocator (static memory, wolfSSL_SetAllocators() callbacks,
XMALLOC_USER, ports). A port or user may supply its own implementation,
but XMALLOCALIGN, XFREEALIGN, XREALLOCALIGN and XREALLOCALIGN_SCRUBBED
must then all be defined together.
Memory from XMALLOCALIGN must be released with XFREEALIGN, never XFREE,
and XFREEALIGN must not be used on memory from XMALLOC.

\return pointer to n bytes aligned to a on success.
\return NULL if a is not 0 or a power of 2, or the allocation fails.

\param a alignment: 0 or a power of 2. Values below 2*sizeof(size_t)
(including 0) are raised to it.
\param n size, in bytes, of the memory to allocate
\param h pointer to the heap to use
\param t memory allocation type for user hints. See enum in types.h

_Example_
\code
byte* buf = (byte*)XMALLOCALIGN(64, 1024, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (buf == NULL) {
return MEMORY_E;
}
...
XFREEALIGN(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
\endcode

\sa XFREEALIGN
\sa XREALLOCALIGN
\sa wc_MallocAlign
*/
void* XMALLOCALIGN(size_t a, size_t n, void* heap, int type);

/*!
\ingroup Memory

\brief This is not actually a function, but rather a preprocessor macro,
which frees memory from XMALLOCALIGN, XREALLOCALIGN or
XREALLOCALIGN_SCRUBBED. By default it maps to wc_FreeAlign().

\return none No returns.

\param p aligned pointer to free. NULL is a no-op.
\param h pointer to the heap used for the allocation
\param t memory allocation type used for the allocation

\sa XMALLOCALIGN
\sa wc_FreeAlign
*/
void XFREEALIGN(void *p, void* heap, int type);

/*!
\ingroup Memory

\brief This is not actually a function, but rather a preprocessor macro,
which reallocates memory from XMALLOCALIGN. It never reallocates in
place: a new block aligned to a is allocated, the leading
min(old size, n) bytes are copied, and p is freed. By default it maps to
wc_ReallocAlign().

\return pointer to the new aligned block on success.
\return NULL when n is 0 (p is freed), or on failure (p is left intact,
including when a is rejected).

\param p NULL, or memory from XMALLOCALIGN/XREALLOCALIGN*. A NULL p
behaves as XMALLOCALIGN.
\param a alignment, as for XMALLOCALIGN
\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

_Example_
\code
byte* buf = (byte*)XMALLOCALIGN(64, 128, NULL, DYNAMIC_TYPE_TMP_BUFFER);
byte* tmp;
...
tmp = (byte*)XREALLOCALIGN(buf, 64, 256, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (tmp != NULL)
buf = tmp;
\endcode

\sa XREALLOCALIGN_SCRUBBED
\sa XMALLOCALIGN
\sa wc_ReallocAlign
*/
void* XREALLOCALIGN(void *p, size_t a, size_t n, void* heap, int type);

/*!
\ingroup Memory

\brief This is not actually a function, but rather a preprocessor macro.
As XREALLOCALIGN, but the old block is zeroized before it is freed,
including when n is 0. On failure the old block is left intact and not
zeroized. Use for buffers holding sensitive data. By default it maps to
wc_ReallocAlignScrubbed().

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

\param p NULL, or memory from XMALLOCALIGN/XREALLOCALIGN*
\param a alignment, as for XMALLOCALIGN
\param old_size current size of p in bytes. Unused by the default
implementation, which tracks the size itself, but available to
overrides that need it. Must not exceed the real allocation size.
\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 XREALLOCALIGN
\sa wc_ReallocAlignScrubbed
*/
void* XREALLOCALIGN_SCRUBBED(void *p, size_t a, size_t old_size, size_t n,
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. It never reallocates in place: a new block is allocated with
XMALLOC, the leading min(old_size, n) bytes are copied, old_size bytes of
p are zeroized, and p is freed with XFREE. 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 old_size 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 XREALLOCALIGN_SCRUBBED
\sa wc_ReallocScrubbed
*/
void* XREALLOC_SCRUBBED(void *p, size_t old_size, size_t n, void* heap,
int type);

/*!
\ingroup Math

Expand Down
13 changes: 13 additions & 0 deletions linuxkm/linuxkm_wc_port.h
Original file line number Diff line number Diff line change
Expand Up @@ -2422,6 +2422,19 @@
#endif /* !XMALLOC_USER && !XMALLOC_OVERRIDE */
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kernel 5.4 XMALLOCALIGN override drops alignment on kernels without natural kmalloc alignment · Kernel memory safety

On kernels before 5.4, XMALLOCALIGN/XREALLOCALIGN throw away a/align and call plain malloc/realloc (kmalloc/krealloc). Those kernels only guarantee ARCH_KMALLOC_MINALIGN, so callers that need 16/64-byte alignment (e.g. for AES-NI) get misaligned buffers. The gate looks inverted.

Suggested fix: Remove the pre-5.4 override so these kernels use the generic wc_MallocAlign family, which enforces the requested alignment.
Basis: Linux Documentation/core-api/memory-allocation.rst (since v5.4): kmalloc gives at least ARCH_KMALLOC_MINALIGN, plus natural alignment for power-of-two sizes; earlier kernels only promise ARCH_KMALLOC_MINALIGN.

Related known findings (similar but distinct; listed for context, not part of this finding)

#ifndef XMALLOCALIGN
#define XMALLOCALIGN(a, s, h, t) ((void)(a), (void)(h), (void)(t), malloc(s))
#define XFREEALIGN(p, h, t) do { (void)(h); (void)(t); free(p); } while (0)
/* reallocation for aligned allocations */
#define XREALLOCALIGN(p, align, size, h, t) \
((void)(align), (void)(h), (void)(t), realloc(p, size))
/* reallocation with scrubbing of lost blocks for aligned allocations */
#define XREALLOCALIGN_SCRUBBED(p, align, old_size, size, h, t) \
((void)(align), wc_ReallocScrubbed(p, old_size, size, h, t))
#endif /* XMALLOCALIGN */
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0) */
Comment on lines +2425 to +2436

#include <linux/limits.h>

#ifndef INT32_MAX
Expand Down
6 changes: 4 additions & 2 deletions src/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -1983,9 +1983,11 @@ endif !BUILD_DO178
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/wolfmath.c
endif !BUILD_FIPS_RAND

if BUILD_MEMORY
# memory.c also holds the aligned/scrubbing allocators (wc_MallocAlign(),
# wc_ReallocScrubbed(), ...) that types.h binds unconditionally, so it is
# built even without memory callbacks; the callback code inside it is
# gated on USE_WOLFSSL_MEMORY.
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/memory.c
endif

if !BUILD_FIPS_RAND
if !BUILD_FIPS_V2_PLUS
Expand Down
Loading
Loading