Skip to content

Latest commit

 

History

History
703 lines (490 loc) · 24.4 KB

File metadata and controls

703 lines (490 loc) · 24.4 KB

ssfheap — Integrity-Checked Heap Interface

SSF | Data Structures

Pedantic heap with integrity checking, double-free detection, and mark-based ownership tracking.

The heap operates entirely over a caller-supplied byte array — no system malloc is ever called. Every allocation and free operation verifies block header checksums and checks the adjacent block for overruns. Block headers use +/- ASCII markers that remain visible in raw memory dumps.

Note: This is NOT a drop-in replacement for malloc/free. The API is intentionally more explicit to prevent common dynamic memory mistakes.

Dependencies | Notes | Configuration | API Summary | Function Reference

Dependencies

Notes

  • SSFHeapInit() requires *handleOut == NULL before the call.
  • SSFHeapAlloc() / SSFHeapMalloc() require *heapMemOut == NULL before the call.
  • SSFHeapDealloc() / SSFHeapFree() set *heapMemOut to NULL on return; asserting on double-free.
  • SSFHeapDeInit() asserts if any allocations remain; free all memory before de-initializing.
  • SSFHeapAllocResize() treats a NULL *heapMemOut as a fresh allocation. On failure the original allocation is left unchanged.
  • All integrity checks run on every operation; corruption is reported immediately via assertion.
  • Designed to work on 8, 16, 32, and 64-bit machine word sizes.
  • All allocations are aligned to the machine word size (sizeof(uint64_t)).
  • Pass NULL for markOutOpt in SSFHeapDealloc() / SSFHeapFree() to skip mark retrieval.

Configuration

All options are set in ssfoptions.h.

Option Default Description
SSF_HEAP_SET_BLOCK_CHECK 0 1 to verify block headers after every modification; useful when porting to a new architecture

API Summary

Definitions

Symbol Kind Description
SSFHeapHandle_t Typedef Opaque heap handle (void *); must be initialized to NULL, set by SSFHeapInit(), cleared by SSFHeapDeInit(). Do not dereference directly.
SSFHeapStatus_t Struct Heap usage snapshot populated by SSFHeapStatus(). Fields: allocatableSize (total allocatable bytes), allocatableLen (current free bytes), minAllocatableLen (low-water mark), numBlocks (current block count), numAllocatableBlocks (free block count), maxAllocatableBlockLen (largest free block), numTotalAllocRequests, numAllocRequests, numFreeRequests.

Functions

Function / Macro Description
e.g. uint32_t SSFHeapInitMinMemSize() Returns the minimum buffer size required to create a heap
e.g. void SSFHeapInit(handleOut, heapMem, heapMemSize, heapMark, isZeroed) Initialize a heap over a user-supplied buffer
e.g. void SSFHeapDeInit(handleOut, isZeroed) De-initialize a heap
e.g. void SSFHeapCheck(handle) Verify heap integrity; asserts on corruption
e.g. void SSFHeapStatus(handle, statusOut) Retrieve heap usage statistics
e.g. bool SSFHeapAlloc(handle, heapMemOut, size, mark, isZeroed) Allocate memory from the heap
e.g. bool SSFHeapAllocResize(handle, heapMemOut, newSize, newMark, newIsZeroed) Resize an existing allocation
e.g. void SSFHeapDealloc(handle, heapMemOut, markOutOpt, isZeroed) Free memory back to the heap
e.g. bool SSFHeapMalloc(handle, heapMemOut, size, mark) Macro: allocate without zeroing
e.g. bool SSFHeapMallocAndZero(handle, heapMemOut, size, mark) Macro: allocate and zero memory
e.g. bool SSFHeapRealloc(handle, heapMemOut, newSize, newMark) Macro: resize without zeroing new area
e.g. bool SSFHeapReallocAndZeroNew(handle, heapMemOut, newSize, newMark) Macro: resize and zero newly added area
e.g. void SSFHeapFree(handle, heapMemOut, markOutOpt) Macro: free without zeroing
e.g. void SSFHeapFreeAndZero(handle, heapMemOut, markOutOpt) Macro: free and zero the freed memory

Function Reference

uint32_t SSFHeapInitMinMemSize(void);

Returns the minimum number of bytes that the heap memory buffer must be to successfully initialize a heap. A buffer of exactly this size yields zero allocatable bytes; use a larger buffer to allow actual allocations.

Returns: Minimum number of bytes required for heap metadata and block headers.

Example:

uint32_t minSize;

minSize = SSFHeapInitMinMemSize();
/* minSize is the minimum buffer size needed to initialize a heap */
/* A buffer larger than minSize provides allocatable memory */

void SSFHeapInit(SSFHeapHandle_t *handleOut, uint8_t *heapMem, uint32_t heapMemSize,
                 uint8_t heapMark, bool isZeroed);

Initializes a heap over the caller-supplied byte array heapMem. The heap handle written to *handleOut is an opaque pointer into heapMem and must be passed to all subsequent heap operations. heapMem must remain valid for the lifetime of the heap.

Parameter Direction Type Description
handleOut out SSFHeapHandle_t * Receives the opaque heap handle. Must not be NULL. Must point to NULL before the call.
heapMem in uint8_t * Caller-supplied backing buffer. Must not be NULL. Must be at least SSFHeapInitMinMemSize() bytes.
heapMemSize in uint32_t Total size of heapMem in bytes.
heapMark in uint8_t Owner tag written into every block header during init. Use a unique value per heap to distinguish heaps in memory dumps.
isZeroed in bool true to zero all of heapMem before initializing; false to zero only the heap handle area.

Returns: Nothing.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);
/* h is a valid heap handle */
SSFHeapDeInit(&h, false);

void SSFHeapDeInit(SSFHeapHandle_t *handleOut, bool isZeroed);

De-initializes the heap. All allocations must have been freed before calling; asserting otherwise. Sets *handleOut to NULL on return.

Parameter Direction Type Description
handleOut in-out SSFHeapHandle_t * Pointer to the heap handle. Set to NULL on return. Must not be NULL. Must not point to NULL.
isZeroed in bool true to zero all of heapMem after de-init; false to zero only the heap handle area.

Returns: Nothing.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);
SSFHeapDeInit(&h, false);
/* h == NULL; heap is no longer valid */

void SSFHeapCheck(SSFHeapHandle_t handle);

Walks all heap block headers and verifies their integrity checksums. Asserts immediately if any corruption is detected. Use during development or debugging to catch memory overwrites early.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle returned by SSFHeapInit(). Must be valid (non-NULL).

Returns: Nothing.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);
SSFHeapCheck(h);
/* returns normally if heap is intact; asserts on corruption */
SSFHeapDeInit(&h, false);

void SSFHeapStatus(SSFHeapHandle_t handle, SSFHeapStatus_t *statusOut);

Fills *statusOut with a snapshot of current heap statistics. Condenses adjacent free blocks before computing free-space figures.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle. Must be valid.
statusOut out SSFHeapStatus_t * Receives the status snapshot. Must not be NULL. allocatableSize: total allocatable bytes (fixed at init). allocatableLen: current free bytes. minAllocatableLen: low-water mark. numBlocks: current total block count. numAllocatableBlocks: current free block count. maxAllocatableBlockLen: largest single free block. numTotalAllocRequests: cumulative allocation attempts. numAllocRequests: successful allocations. numFreeRequests: cumulative free calls.

Returns: Nothing.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')
#define APP_MARK   ('A')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;
SSFHeapStatus_t status;
uint8_t *ptr = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);
SSFHeapMalloc(h, &ptr, 64u, APP_MARK);

SSFHeapStatus(h, &status);
/* status.allocatableLen == (status.allocatableSize - aligned 64 bytes - block overhead) */
/* status.numAllocRequests == 1 */

SSFHeapFree(h, &ptr, NULL);
SSFHeapDeInit(&h, false);

bool SSFHeapAlloc(SSFHeapHandle_t handle, void **heapMemOut, uint32_t size, uint8_t mark,
                  bool isZeroed);

Allocates size bytes from the heap using a first-fit strategy. On success *heapMemOut is set to the allocated block. On failure *heapMemOut is set to NULL. Prefer the SSFHeapMalloc() or SSFHeapMallocAndZero() macros instead of calling this directly.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle. Must be valid.
heapMemOut out void ** Receives a pointer to the allocated block on success, NULL on failure. Must not be NULL. Must point to NULL before the call.
size in uint32_t Number of bytes to allocate. Passing 0 requests a minimum-sized allocation.
mark in uint8_t Owner tag written into the block header to identify the allocator.
isZeroed in bool true to zero the allocated memory before returning it.

Returns: true if allocation succeeded; false if the heap has insufficient contiguous free space.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')
#define APP_MARK   ('A')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;
uint8_t *ptr = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);

if (SSFHeapAlloc(h, (void **)&ptr, 64u, APP_MARK, false))
{
    /* ptr points to a 64-byte block */
    SSFHeapDealloc(h, (void **)&ptr, NULL, false);
    /* ptr == NULL */
}

SSFHeapDeInit(&h, false);

bool SSFHeapAllocResize(SSFHeapHandle_t handle, void **heapMemOut, uint32_t newSize,
                        uint8_t newMark, bool newIsZeroed);

Resizes an existing allocation. If *heapMemOut is NULL on entry, behaves as a fresh allocation. On success *heapMemOut is updated to the new location (which may differ from the original) and the old block is freed; existing data is preserved up to min(newSize, oldSize). On failure *heapMemOut is left unchanged and the original allocation remains valid. Prefer the SSFHeapRealloc() or SSFHeapReallocAndZeroNew() macros instead of calling this directly.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle. Must be valid.
heapMemOut in-out void ** On entry, points to the existing allocation or NULL for a fresh alloc. On success, updated to the new allocation. On failure, left unchanged. Must not be NULL.
newSize in uint32_t New requested size in bytes. Passing 0 requests a minimum-sized allocation.
newMark in uint8_t Owner tag for the resized block.
newIsZeroed in bool true to zero any newly added bytes when growing.

Returns: true if resize succeeded; false if the heap has insufficient space for the new size.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')
#define APP_MARK   ('A')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;
uint8_t *ptr = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);
SSFHeapAlloc(h, (void **)&ptr, 64u, APP_MARK, false);

if (SSFHeapAllocResize(h, (void **)&ptr, 128u, APP_MARK, false))
{
    /* ptr points to a 128-byte block; original 64 bytes preserved */
}
/* if resize fails, ptr still points to the original 64-byte block */

if (ptr != NULL) { SSFHeapDealloc(h, (void **)&ptr, NULL, false); }
SSFHeapDeInit(&h, false);

void SSFHeapDealloc(SSFHeapHandle_t handle, void **heapMemOut, uint8_t *markOutOpt,
                    bool isZeroed);

Frees a previously allocated block back to the heap. Sets *heapMemOut to NULL on return. Verifies block header integrity and checks the adjacent block for overruns. Asserts on double-free or corruption. Prefer the SSFHeapFree() or SSFHeapFreeAndZero() macros instead of calling this directly.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle. Must be valid.
heapMemOut in-out void ** Pointer to the allocation to free. Set to NULL on return. Must not be NULL. Must not point to NULL (asserts on double-free).
markOutOpt out (opt) uint8_t * If not NULL, receives the mark value stored in the freed block header. Pass NULL to skip mark retrieval.
isZeroed in bool true to zero the freed block memory before returning it to the free pool.

Returns: Nothing.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')
#define APP_MARK   ('A')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;
uint8_t *ptr = NULL;
uint8_t mark;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);
SSFHeapAlloc(h, (void **)&ptr, 64u, APP_MARK, false);

SSFHeapDealloc(h, (void **)&ptr, &mark, false);
/* ptr == NULL; mark == APP_MARK */

SSFHeapDeInit(&h, false);

Macros that wrap SSFHeapAlloc(), SSFHeapAllocResize(), and SSFHeapDealloc() with the isZeroed argument fixed, eliminating the need to pass it explicitly on every call. Use these in preference to calling the underlying functions directly.


#define SSFHeapMalloc(handle, heapMemOut, size, mark) \
    SSFHeapAlloc(handle, (void **)heapMemOut, size, mark, false)

Allocates size bytes from the heap without zeroing the allocated memory.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle. Must be valid.
heapMemOut out void ** Receives the allocated block on success, NULL on failure. Must not be NULL. Must point to NULL before the call.
size in uint32_t Number of bytes to allocate.
mark in uint8_t Owner tag written into the block header.

Returns: true if allocation succeeded; false if insufficient contiguous free space.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')
#define APP_MARK   ('A')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;
uint8_t *ptr = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);

if (SSFHeapMalloc(h, &ptr, 64u, APP_MARK))
{
    /* ptr points to an unzeroed 64-byte block */
    SSFHeapFree(h, &ptr, NULL);
    /* ptr == NULL */
}

SSFHeapDeInit(&h, false);

#define SSFHeapMallocAndZero(handle, heapMemOut, size, mark) \
    SSFHeapAlloc(handle, (void **)heapMemOut, size, mark, true)

Allocates size bytes from the heap and zeroes the allocated memory before returning it.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle. Must be valid.
heapMemOut out void ** Receives the zero-initialized allocated block on success, NULL on failure. Must not be NULL. Must point to NULL before the call.
size in uint32_t Number of bytes to allocate.
mark in uint8_t Owner tag written into the block header.

Returns: true if allocation succeeded; false if insufficient contiguous free space.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')
#define APP_MARK   ('A')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;
uint8_t *ptr = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);

if (SSFHeapMallocAndZero(h, &ptr, 64u, APP_MARK))
{
    /* ptr points to a zero-initialized 64-byte block */
    SSFHeapFree(h, &ptr, NULL);
}

SSFHeapDeInit(&h, false);

#define SSFHeapRealloc(handle, heapMemOut, newSize, newMark) \
    SSFHeapAllocResize(handle, (void **)heapMemOut, newSize, newMark, false)

Resizes an existing allocation without zeroing any newly added bytes.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle. Must be valid.
heapMemOut in-out void ** On entry, points to the existing allocation or NULL. Updated on success. Left unchanged on failure. Must not be NULL.
newSize in uint32_t New requested size in bytes.
newMark in uint8_t Owner tag for the resized block.

Returns: true if resize succeeded; false if insufficient heap space.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')
#define APP_MARK   ('A')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;
uint8_t *ptr = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);
SSFHeapMalloc(h, &ptr, 64u, APP_MARK);

if (SSFHeapRealloc(h, &ptr, 128u, APP_MARK))
{
    /* ptr points to 128-byte block; original 64 bytes preserved; bytes 64-127 unzeroed */
}
if (ptr != NULL) { SSFHeapFree(h, &ptr, NULL); }

SSFHeapDeInit(&h, false);

#define SSFHeapReallocAndZeroNew(handle, heapMemOut, newSize, newMark) \
    SSFHeapAllocResize(handle, (void **)heapMemOut, newSize, newMark, true)

Resizes an existing allocation and zeroes any bytes added when the allocation grows.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle. Must be valid.
heapMemOut in-out void ** On entry, points to the existing allocation or NULL. Updated on success. Left unchanged on failure. Must not be NULL.
newSize in uint32_t New requested size in bytes.
newMark in uint8_t Owner tag for the resized block.

Returns: true if resize succeeded; false if insufficient heap space.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')
#define APP_MARK   ('A')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;
uint8_t *ptr = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);
SSFHeapMalloc(h, &ptr, 64u, APP_MARK);

if (SSFHeapReallocAndZeroNew(h, &ptr, 128u, APP_MARK))
{
    /* ptr points to 128-byte block; original 64 bytes preserved; bytes 64-127 zeroed */
}
if (ptr != NULL) { SSFHeapFree(h, &ptr, NULL); }

SSFHeapDeInit(&h, false);

#define SSFHeapFree(handle, heapMemOut, markOutOpt) \
    SSFHeapDealloc(handle, (void **)heapMemOut, markOutOpt, false)

Frees a previously allocated block without zeroing its memory.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle. Must be valid.
heapMemOut in-out void ** Pointer to the allocation to free. Set to NULL on return. Must not point to NULL.
markOutOpt out (opt) uint8_t * If not NULL, receives the mark value from the freed block header.

Returns: Nothing.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')
#define APP_MARK   ('A')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;
uint8_t *ptr = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);
SSFHeapMalloc(h, &ptr, 64u, APP_MARK);

SSFHeapFree(h, &ptr, NULL);
/* ptr == NULL; block memory not zeroed */

SSFHeapDeInit(&h, false);

#define SSFHeapFreeAndZero(handle, heapMemOut, markOutOpt) \
    SSFHeapDealloc(handle, (void **)heapMemOut, markOutOpt, true)

Frees a previously allocated block and zeroes its memory before returning it to the free pool.

Parameter Direction Type Description
handle in SSFHeapHandle_t Opaque heap handle. Must be valid.
heapMemOut in-out void ** Pointer to the allocation to free. Set to NULL on return. Must not point to NULL.
markOutOpt out (opt) uint8_t * If not NULL, receives the mark value from the freed block header.

Returns: Nothing.

Example:

#define HEAP_SIZE  (1024u)
#define HEAP_MARK  ('H')
#define APP_MARK   ('A')

uint8_t heapMem[HEAP_SIZE];
SSFHeapHandle_t h = NULL;
uint8_t *ptr = NULL;

SSFHeapInit(&h, heapMem, sizeof(heapMem), HEAP_MARK, false);
SSFHeapMalloc(h, &ptr, 64u, APP_MARK);

SSFHeapFreeAndZero(h, &ptr, NULL);
/* ptr == NULL; freed block memory zeroed */

SSFHeapDeInit(&h, false);