π§ β
Laser Focus Activated.
Weβre now moving precisely and decisively into:
π¦ 09_arena_allocator_scope_test
π― Goal: Build and inspect a manual arena allocator where memory lifetime and boundaries are explicit and local β unlike
malloc()
which hides everything in libc internals.
π§ Strategic Why:
We just finished [[08_malloc_tls_isolation_test/]]
, proving:
-
malloc()
β predictable -
Thread-local arenas exist but arenβt visible
-
Scope and ownership of memory = unclear
Now, in 09
, we flip the model:
β You allocate the memory.
β You track the offset.
β You control the lifetime.
β You see the fragmentation yourself.
Phase Structure:
File | Purpose |
---|---|
β
09a_arena_scope_local_mmap.c | Allocate a user arena via mmap() and write into it |
β³ 09b_arena_scope_leak_tracer.c | Simulate scope leak and print memory before/after |
β³ 09c_arena_scope_fork_test.c | Fork after arena allocation β trace outcome |
Dropping 09a_arena_scope_local_mmap.c
next β clean, 1-file focused.
Letβs begin your first user-controlled memory arena.
π§ π¦ Ready? Letβs build.
// π¦ File: 09a_arena_scope_local_mmap.c
// π¬ Goal: Allocate a manual arena with mmap() and control memory lifetime and offset manually
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <stdint.h>
#define ARENA_SIZE 4096
#define BLOCK_SIZE 64
typedef struct s_arena
{
void *base;
size_t offset;
size_t capacity;
} t_arena;
void arena_init(t_arena *a, size_t size)
{
a->base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (a->base == MAP_FAILED)
{
perror("mmap");
exit(1);
}
a->offset = 0;
a->capacity = size;
printf("π¦ Arena initialized at %p (%zu bytes)\n", a->base, size);
}
void *arena_alloc(t_arena *a, size_t size)
{
if (a->offset + size > a->capacity)
{
fprintf(stderr, "β Arena out of memory!\n");
return (NULL);
}
void *ptr = (char *)a->base + a->offset;
a->offset += size;
printf("β
arena_alloc(%zu) β %p (offset = %zu)\n", size, ptr, a->offset);
return (ptr);
}
void arena_dump(t_arena *a)
{
printf("π§ Arena dump:\n");
for (size_t i = 0; i < a->offset; i++)
{
char c = ((char *)a->base)[i];
if (c >= 32 && c < 127)
printf("%c", c);
else
printf(".");
}
printf("\n");
}
void arena_free(t_arena *a)
{
if (munmap(a->base, a->capacity) != 0)
perror("munmap");
else
printf("π§Ή Arena memory freed\n");
a->base = NULL;
a->offset = 0;
a->capacity = 0;
}
int main(void)
{
t_arena arena;
arena_init(&arena, ARENA_SIZE);
char *msg1 = arena_alloc(&arena, BLOCK_SIZE);
char *msg2 = arena_alloc(&arena, BLOCK_SIZE);
char *msg3 = arena_alloc(&arena, BLOCK_SIZE);
strcpy(msg1, "Hello arena");
strcpy(msg2, "Scoped allocator is born");
strcpy(msg3, "This is not malloc");
arena_dump(&arena);
arena_free(&arena);
return (0);
}
β
[[09a_arena_scope_local_mmap.c]]
Summary
π§ͺ What You Learned | π‘ Insight |
---|---|
mmap() allocator | You now own the heap itself |
Arena boundaries | No more hidden metadata β offset tells all |
Scope-controlled memory | You allocate β you free |
Fragmentation and reuse | Clearly visible if you modify reuse logic |
π§ Blindspots Unlocked
-
What happens when you fork after arena alloc?
-
What if two arenas overlap?
-
Can you detect use-after-unmap?
-
What about memory leaks from non-zeroed offsets?
-
Is this thread-safe? (Hint: β)
Next Up: [[09b_arena_scope_leak_tracer.c]]
Weβll simulate an arena memory leak by forgetting to track or free one allocation, then diff it before/after with
memcmp()
and visual logging.
Unless you want to skip directly to fork territory in 09c
β say the word.
Youβre now building memory engines.
No more mallocs.
π¦π§ π₯
π₯ Letβs go full destructive mode then. Time to leak, corrupt, and dump like a real allocator stress-test.
π 09b_arena_scope_leak_tracer.c
β οΈ Purpose:
Simulate memory leaks, corrupted offsets, and untracked allocations
Explore the consequences of managing your own arena without safety checks
𧬠Code: 09b_arena_scope_leak_tracer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <stdint.h>
#define ARENA_SIZE 256
#define BLOCK_SIZE 32
typedef struct s_arena {
void *base;
size_t offset;
size_t capacity;
} t_arena;
void arena_init(t_arena *a, size_t size)
{
a->base = mmap(NULL, size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (a->base == MAP_FAILED)
{
perror("mmap");
exit(1);
}
a->offset = 0;
a->capacity = size;
printf("π― Arena initialized at %p (%zu bytes)\n", a->base, size);
}
void *arena_alloc(t_arena *a, size_t size)
{
if (a->offset + size > a->capacity)
{
fprintf(stderr, "β Arena out of memory at offset %zu\n", a->offset);
return (NULL);
}
void *ptr = (char *)a->base + a->offset;
a->offset += size;
printf("β
arena_alloc(%zu) β %p (offset now %zu)\n", size, ptr, a->offset);
return (ptr);
}
void arena_dump(t_arena *a)
{
printf("\nπ¦ Arena dump (%zu used / %zu total):\n", a->offset, a->capacity);
for (size_t i = 0; i < a->offset; i++)
{
char c = ((char *)a->base)[i];
if (c >= 32 && c < 127)
printf("%c", c);
else
printf(".");
}
printf("\n");
}
void arena_free(t_arena *a)
{
if (munmap(a->base, a->capacity) != 0)
perror("munmap");
else
printf("π§Ή Arena memory freed\n");
a->base = NULL;
a->offset = 0;
a->capacity = 0;
}
int main(void)
{
t_arena arena;
arena_init(&arena, ARENA_SIZE);
// Normal alloc
char *a = arena_alloc(&arena, BLOCK_SIZE);
char *b = arena_alloc(&arena, BLOCK_SIZE);
strcpy(a, "First block ok.");
strcpy(b, "Second block too.");
// Leaked pointer: never saved return
arena_alloc(&arena, BLOCK_SIZE); // not assigned to any variable
printf("β οΈ Leaked 32 bytes β no tracking.\n");
// Intentional overflow write
char *c = arena_alloc(&arena, BLOCK_SIZE);
strcpy(c, "This will overwrite next");
strcpy(c + 20, " π€‘ Oops!");
arena_dump(&arena);
arena_free(&arena);
return (0);
}
π§ What to Look For
Symbol | Meaning |
---|---|
β | Normal alloc |
β οΈ | Leak (untracked pointer) |
π€‘ | Corrupting next block |
π¦ | Heap visualization |
π§Ή | Manual unmapping |
π§ Strategic Questions (Generated for You)
π 180 IQ Review Triggers:
-
What if you realloc or resize this arena?
-
What if
arena_alloc()
returns NULL, but caller ignores it? -
What tools in real life would detect that overflow?
-
Could you implement a double-free detector manually?
β After This File, Youβll Know:
-
How memory leaks feel when you donβt track pointers
-
How arenas can silently overflow
-
How scoped allocators demand discipline and diagnostics
Say the word and Iβll prep:
-
π
README.md
for09b
-
π§
09c_arena_scope_fork_test.c
to test fork safety of arena -
π§¬
10_overlapping_allocations_demo.c
for weird memory layouts
This allocator playground is now your malloc()
.
Youβre debugging your heap like a true memory monk. π§ββοΈπ¦π§