> ## Documentation Index
> Fetch the complete documentation index at: https://kernel.mvhcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Kernel Heap API — kmalloc, kfree, and Heap Statistics

> Reference for the MVH Kernel heap API: heap_init, kmalloc, kfree, heap_get_stats, heap_validate, and heap_self_test with canary protection.

The kernel heap is a 1 MiB coalescing allocator that sits on top of a VMM-backed virtual address region. Every allocated block is protected by a header and footer canary so that out-of-bounds writes are detected immediately at the next allocation or free. Freed blocks are poisoned with a fixed byte pattern, causing any use-after-free to produce predictably wrong data rather than silently corrupting live state. The heap also tracks invalid-free attempts — passing an unrecognized pointer to `kfree` triggers a kernel panic rather than silently corrupting internal metadata.

## Data Types

The `heap_stats_t` struct captures a snapshot of heap state at the moment `heap_get_stats` is called. Its definition from `mvh/memory.h` is:

```c theme={null}
typedef struct {
    uint64_t total_bytes;          /* Total heap capacity in bytes                  */
    uint64_t used_bytes;           /* Bytes currently allocated by live blocks      */
    uint64_t free_bytes;           /* Bytes available for future allocations        */
    uint64_t largest_free_block;   /* Size of the largest single contiguous gap     */
    uint64_t allocations;          /* Cumulative successful kmalloc calls           */
    uint64_t allocation_failures;  /* Cumulative kmalloc calls that returned NULL   */
    uint64_t invalid_frees;        /* Cumulative kfree calls with bad pointers      */
    uint32_t blocks;               /* Current total block count (free + allocated)  */
    uint32_t free_blocks;          /* Current count of free (coalesced) blocks      */
} heap_stats_t;
```

***

## Functions

### `heap_init`

```c theme={null}
int heap_init(void);
```

Initializes the kernel heap, maps the 1 MiB heap region through the VMM, and writes the initial free-block header. You must call this once, after both `pmm_init` and `vmm_init`, and before any call to `kmalloc` or `kfree`.

<ResponseField name="return" type="int">
  `0` on success. A non-zero value indicates that the VMM could not map the heap region — this is a fatal condition and the kernel should halt.
</ResponseField>

***

### `kmalloc`

```c theme={null}
void *kmalloc(uint64_t size);
```

Allocates at least `size` bytes from the kernel heap and returns a pointer to the first usable byte of the block. The allocator searches for the first free block large enough to satisfy the request (first-fit), splits it if the remainder is large enough to hold a minimum-sized free block, and writes canary values into the block header and footer before returning. Adjacent free blocks are coalesced eagerly during every allocation pass.

<ParamField path="size" type="uint64_t" required>
  Number of bytes to allocate. Must be greater than zero. The allocator rounds this up internally to satisfy any alignment requirements. Passing `0` returns `NULL`.
</ParamField>

<ResponseField name="return" type="void *">
  Pointer to the start of the allocated region on success. `NULL` if the allocation fails — either because no single free block is large enough (check `largest_free_block` in `heap_stats_t`) or because the heap has not been initialized. The pointer is always aligned to at least 8 bytes.
</ResponseField>

<Note>
  Every block returned by `kmalloc` carries a **canary** in both its header and footer. The canary is verified on every subsequent `kmalloc` and `kfree` call. If a write overflows the block and corrupts the footer canary, the corruption is detected at the next heap operation and the kernel panics with a diagnostic message — before the corrupted metadata can cause further damage.
</Note>

***

### `kfree`

```c theme={null}
void kfree(void *address);
```

Returns the block at `address` to the heap's free pool. Before marking the block free, `kfree` verifies both the header and footer canaries; if either is corrupt the kernel panics immediately. After the canary check passes, the block's payload is overwritten with a poison byte pattern so that any subsequent read through a stale pointer produces recognizable garbage rather than valid-looking data. Adjacent free blocks are then coalesced with the newly freed block.

<ParamField path="address" type="void *" required>
  A pointer that was previously returned by `kmalloc` and has not yet been freed. Passing `NULL` is a no-op. Passing any other pointer that was not returned by `kmalloc` — including a pointer into the middle of an allocation, a stack pointer, or an already-freed pointer — triggers a kernel panic.
</ParamField>

<Warning>
  Passing an invalid pointer (one not returned by `kmalloc`, or one that has already been freed) to `kfree` **triggers a kernel panic**. There is no way to recover from this at runtime. Always pair every `kmalloc` with exactly one `kfree`, and set the pointer to `NULL` after freeing to prevent accidental double-free.
</Warning>

***

### `heap_total_bytes`

```c theme={null}
uint64_t heap_total_bytes(void);
```

Returns the total capacity of the kernel heap in bytes (always 1 MiB minus internal overhead for the heap's own bookkeeping structures).

<ResponseField name="return" type="uint64_t">
  Total heap capacity in bytes.
</ResponseField>

***

### `heap_used_bytes`

```c theme={null}
uint64_t heap_used_bytes(void);
```

Returns the number of bytes currently held by live (non-freed) allocations, excluding block header/footer overhead.

<ResponseField name="return" type="uint64_t">
  Bytes currently in use by caller-allocated blocks.
</ResponseField>

***

### `heap_allocation_count`

```c theme={null}
uint64_t heap_allocation_count(void);
```

Returns the cumulative number of successful `kmalloc` calls since `heap_init`. This counter is never decremented by `kfree`.

<ResponseField name="return" type="uint64_t">
  Total number of successful allocations since initialization.
</ResponseField>

***

### `heap_get_stats`

```c theme={null}
void heap_get_stats(heap_stats_t *stats);
```

Fills the `heap_stats_t` structure pointed to by `stats` with a consistent snapshot of all heap counters and current block inventory. Use this for diagnostic output, memory-pressure monitoring, or assertions in test code.

<ParamField path="stats" type="heap_stats_t *" required>
  Pointer to a caller-allocated `heap_stats_t` struct. Must not be `NULL`. All fields are overwritten on return.
</ParamField>

<ResponseField name="stats.total_bytes" type="uint64_t">
  Total heap capacity in bytes.
</ResponseField>

<ResponseField name="stats.used_bytes" type="uint64_t">
  Bytes currently held by live allocations (payload only, not including block metadata).
</ResponseField>

<ResponseField name="stats.free_bytes" type="uint64_t">
  Bytes available for future allocations across all free blocks combined.
</ResponseField>

<ResponseField name="stats.largest_free_block" type="uint64_t">
  The size in bytes of the single largest contiguous free block. If `kmalloc(size)` returns `NULL`, compare `size` against this value to distinguish between total-heap-exhaustion and fragmentation.
</ResponseField>

<ResponseField name="stats.allocations" type="uint64_t">
  Cumulative count of successful `kmalloc` calls since `heap_init`.
</ResponseField>

<ResponseField name="stats.allocation_failures" type="uint64_t">
  Cumulative count of `kmalloc` calls that returned `NULL`.
</ResponseField>

<ResponseField name="stats.invalid_frees" type="uint64_t">
  Cumulative count of `kfree` calls that received a pointer not recognized as a valid heap block. Each such call also panics, so in practice this counter will be at most `1` for any given boot.
</ResponseField>

<ResponseField name="stats.blocks" type="uint32_t">
  Current total number of blocks tracked by the heap (both free and allocated).
</ResponseField>

<ResponseField name="stats.free_blocks" type="uint32_t">
  Current number of free (coalesced) blocks. A large value relative to `blocks` indicates significant fragmentation.
</ResponseField>

***

### `heap_validate`

```c theme={null}
int heap_validate(void);
```

Walks every block in the heap and verifies its header and footer canaries, the consistency of block sizes, and the integrity of the free-block list. Use this in test and debug builds to assert heap health at any point in time.

<ResponseField name="return" type="int">
  `0` if the entire heap structure and all canaries are intact. Non-zero if any corruption is detected; the kernel serial log will contain a description of the first violation found.
</ResponseField>

<Tip>
  Call `heap_validate` before and after any subsystem that performs a large number of allocations to quickly isolate which call site is causing corruption during development.
</Tip>

***

### `heap_self_test`

```c theme={null}
int heap_self_test(void);
```

Executes the heap's built-in self-test suite. The tests cover single allocations, multiple simultaneous allocations, fragmentation and coalescing, boundary-size requests, and canary integrity. All blocks allocated by the test are freed before the function returns, leaving the heap in the same state as before.

<ResponseField name="return" type="int">
  `0` if all self-test cases pass. Non-zero indicates the index of the first failing sub-test; check the kernel serial log for a human-readable description.
</ResponseField>

<Tip>
  Run `heap_self_test` immediately after `heap_init` in debug builds to validate the allocator on every boot before any real allocations are made.
</Tip>

***

## Usage Example

The following example demonstrates the standard `kmalloc` / `kfree` pattern alongside heap statistics inspection.

```c theme={null}
#include <mvh/memory.h>

void example_heap_usage(void) {
    /* Allocate a 256-byte buffer */
    void *buf = kmalloc(256);
    if (buf == NULL) {
        heap_stats_t stats;
        heap_get_stats(&stats);
        klog("kmalloc failed: largest_free=%llu, failures=%llu\n",
             stats.largest_free_block, stats.allocation_failures);
        return;
    }

    /* Use the buffer */
    uint8_t *bytes = (uint8_t *)buf;
    for (int i = 0; i < 256; i++) {
        bytes[i] = (uint8_t)i;
    }

    /* Inspect heap state while the allocation is live */
    heap_stats_t stats;
    heap_get_stats(&stats);
    klog("Heap: used=%llu free=%llu allocs=%llu blocks=%u free_blocks=%u\n",
         stats.used_bytes, stats.free_bytes, stats.allocations,
         stats.blocks, stats.free_blocks);

    /* Free the buffer and immediately null the pointer to prevent reuse */
    kfree(buf);
    buf = NULL;

    /* Validate the heap structure after the free */
    if (heap_validate() != 0) {
        klog("Heap corruption detected after kfree!\n");
    }
}
```
