🔥 Roger that. 180-IQ strategic advisor fully deployed.
We’re now entering full-phase asymmetric execution of [[asymmetric_c/08_malloc_tls_isolation_test/]]
.
Every file from this point will:
-
📦 Include multiple
.c
explorations if needed -
🧠 Surface non-obvious system interactions
-
🩻 Reveal invisible boundaries (thread-local vs global allocator behavior)
-
🔬 Trigger undefined behavior on purpose
-
📜 Be logged as
.md
lab reports — until I say “✅ Exercise complete”
🧠 NEXT STEPS:
Building [[asymmetric_c/08_malloc_tls_isolation_test/]]
now…
Brace for allocation chaos across threads and forks.
Lab Objective:
Map the reality ofmalloc()
memory isolation across multiple threads and forked processes.
Discover when memory isn’t yours — even when it says it is.
🧬 Stand by for drop…
// 🔍 File: 08a_tls_malloc_layout_map.c
// 🧠 Goal: Test how malloc behaves across threads — do they get different chunks? Same arena? Isolated?
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define THREADS 4
void *worker(void *arg)
{
int index = *(int *)arg;
void *ptrs[3];
for (int i = 0; i < 3; i++)
ptrs[i] = malloc(64);
printf("🧵 Thread %d mallocs:\n", index);
for (int i = 0; i < 3; i++)
printf(" [%d] → %p\n", i, ptrs[i]);
sleep(1); // hold mallocs in case we fork next
for (int i = 0; i < 3; i++)
free(ptrs[i]);
return (NULL);
}
int main(void)
{
pthread_t threads[THREADS];
int indices[THREADS];
printf("🏁 Launching %d threads for TLS malloc test...\n", THREADS);
for (int i = 0; i < THREADS; i++)
{
indices[i] = i;
pthread_create(&threads[i], NULL, worker, &indices[i]);
}
for (int i = 0; i < THREADS; i++)
pthread_join(threads[i], NULL);
printf("✅ All threads completed.\n");
return (0);
}
🔬 [[asymmetric_c/08_malloc_tls_isolation_test/]]
— Iteration 1: 08a_tls_malloc_layout_map.c
✅ Test: Do multiple threads get isolated heap arenas?
✅ What this tests | 🧠 What it reveals |
---|---|
Each thread mallocs 3 chunks | Are pointer addresses from different threads far apart? |
Print memory addresses | Do arenas overlap? Is the allocator using thread-local caching? |
All pointers freed | Do reused chunks appear (e.g. address reuse) |
🧠 180 IQ Insight:
Most modern allocators (like glibc’s ptmalloc or jemalloc) use thread-local arenas to reduce lock contention.
But:
-
Not every platform does this (e.g.
musl
might behave differently) -
Not every allocation type triggers this (e.g.
malloc(64)
vsmalloc(1024 * 1024)
) -
And after
fork()
… it gets dangerous 😈
Run it multiple times.
Check how far apart the allocations are.
If you’re seeing address ranges like:
Thread 0 mallocs:
[0] → 0x7fa34b804c10
[1] → 0x7fa34b804c60
[2] → 0x7fa34b804cb0
Thread 1 mallocs:
[0] → 0x7fa34b905c10
[1] → 0x7fa34b905c60
[2] → 0x7fa34b905cb0
You just proved allocator arena separation.
🧪 Ready for next iterations?
Coming up next:
-
08b_tls_corruption_chain.c
→ mess with one thread’s chunk, check for bleed -
08c_tls_fork_injected.c
→ run fork during live thread allocs, see what child inherits -
08d_tls_alloc_inspector.c
→ recordgetpid()
,pthread_self()
, and layout diffs
When we hit 08e
, we’ll start analyzing the allocator’s behavior at the page and chunk level with mmap()
and sbrk()
traces.
Say:
-
"drop 08b next"
-
or
"bundle a README.md with all findings so far"
We’re now officially dissecting runtime memory architecture.
This isn’t a test anymore — this is allocator surveillance warfare.
💥🧠 Continuing now with the next level of thread-local memory dissection.
Our goal:
Intentionally corrupt memory in one thread and observe if it causes bleedover or corruption in other threads’ allocations — even if pointers are different.
🧪 08b_tls_corruption_chain.c
🚨 Can threads secretly see or overwrite each other’s mallocs?
// 🔥 File: 08b_tls_corruption_chain.c
// 👻 Purpose: Write junk data in one thread’s malloc and see if others are affected.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#define THREADS 3
#define CHUNKS 3
#define SIZE 64
char *thread_blocks[THREADS][CHUNKS];
void *worker(void *arg)
{
int index = *(int *)arg;
char *ptr;
for (int i = 0; i < CHUNKS; i++)
{
ptr = malloc(SIZE);
if (!ptr)
{
perror("malloc");
exit(1);
}
memset(ptr, 'A' + index, SIZE - 1);
ptr[SIZE - 1] = 0;
thread_blocks[index][i] = ptr;
printf("🧵 Thread %d malloc[%d] → %p : %s\n", index, i, ptr, ptr);
}
sleep(2);
return (NULL);
}
int main(void)
{
pthread_t threads[THREADS];
int indices[THREADS];
for (int i = 0; i < THREADS; i++)
{
indices[i] = i;
pthread_create(&threads[i], NULL, worker, &indices[i]);
}
sleep(1); // let some threads allocate
printf("💣 CORRUPTING BLOCK in thread 0 chunk 0...\n");
if (thread_blocks[0][0])
{
memset(thread_blocks[0][0], 'X', SIZE - 1);
thread_blocks[0][0][SIZE - 1] = 0;
}
for (int i = 0; i < THREADS; i++)
pthread_join(threads[i], NULL);
printf("\n🔍 Verifying integrity of all blocks:\n");
for (int i = 0; i < THREADS; i++)
for (int j = 0; j < CHUNKS; j++)
printf(" [T%d][%d] → %p : %.10s\n", i, j, thread_blocks[i][j], thread_blocks[i][j]);
for (int i = 0; i < THREADS; i++)
for (int j = 0; j < CHUNKS; j++)
free(thread_blocks[i][j]);
return 0;
}
🧠 What This Reveals
🔬 Test | 💡 System Insight |
---|---|
Overwrite thread 0 chunk | If chunk is shared, other threads may show corruption |
Readback after all joins | Prove thread arenas are (or aren’t) isolated |
Use ASCII markers | Makes it easy to detect memory bleeding by visual patterns |
Sample Output (Healthy Case):
🧵 Thread 0 malloc[0] → 0x123... : AAAAAAAAA
🧵 Thread 1 malloc[0] → 0x456... : BBBBBBBBB
💣 CORRUPTING BLOCK...
🔍 Verifying:
[T0][0] : XXXXXXXXXX
[T1][0] : BBBBBBBBBB ✅
But if you ever see:
[T1][1] : XBBBBBBBBB 💀
🧠 You just detected heap arena overlap or metadata corruption — on your machine.
🧨 Hidden Gotchas to Look For
-
Some allocators may give adjacent chunks within the same arena — even for different threads
-
Some platforms reuse freed chunks across threads
-
Realloc may shift chunks unexpectedly
-
Thread arena reuse is lazy — the OS decides when to garbage collect
🔭 180-IQ Strategic Followup
Next iterations coming up:
-
08c_tls_fork_injected.c
— fork while threads hold active mallocs -
08d_tls_alloc_inspector.c
— logpthread_self()
,getpid()
,sbrk()
, and pointer distances -
08e_heapmap.c
— visualize pointer spacing across threads and forks
Shall I proceed to 08c_
and trigger fork + TLS chaos?
Or would you like to:
-
🔍 Document this in Obsidian
.md
now -
🧠 Add
valgrind
instrumentation -
🧪 Compare with
musl
/jemalloc
?
You’re now seeing allocator memory illusions exposed in public.
No more “trusting malloc”.
You’ve started becoming the memory mapper.
💣🧬📦
🧠💥 Perfect. Dropping [[08d_tls_alloc_inspector.c]]
first — our diagnostic x-ray tool for allocator behavior in multithreaded contexts.
🔬 [[asymmetric_c/08_malloc_tls_isolation_test/08d_tls_alloc_inspector.c]]
Goal:
Print pointer spacing,pthread_self()
, andgetpid()
to inspect allocator thread-local behavior and memory layout patterns.
// 🧠 File: 08d_tls_alloc_inspector.c
// 🔍 Purpose: Inspect per-thread allocation patterns with pointer diffs + metadata
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#define THREADS 4
#define ALLOCS 4
#define SIZE 64
void *worker(void *arg)
{
int index = *(int *)arg;
void *ptrs[ALLOCS];
printf("\n🧵 Thread %d [TID: %lu] in PID %d\n", index, pthread_self(), getpid());
for (int i = 0; i < ALLOCS; i++)
{
ptrs[i] = malloc(SIZE);
if (!ptrs[i])
perror("malloc");
memset(ptrs[i], 'A' + index, SIZE);
printf(" ➤ ptr[%d] = %p\n", i, ptrs[i]);
if (i > 0)
{
ptrdiff_t diff = (char *)ptrs[i] - (char *)ptrs[i - 1];
printf(" ↳ diff from previous: %td bytes\n", diff);
}
}
usleep(500000); // hold memory
for (int i = 0; i < ALLOCS; i++)
free(ptrs[i]);
return (NULL);
}
int main(void)
{
pthread_t threads[THREADS];
int indices[THREADS];
printf("🚀 Launching TLS inspector across %d threads\n", THREADS);
for (int i = 0; i < THREADS; i++)
{
indices[i] = i;
pthread_create(&threads[i], NULL, worker, &indices[i]);
}
for (int i = 0; i < THREADS; i++)
pthread_join(threads[i], NULL);
printf("\n✅ All threads completed.\n");
return 0;
}
🧠 What You Learn:
Metric | Why It Matters |
---|---|
pthread_self() | Unique thread identity — even if TIDs recycled by system |
getpid() | Useful to verify we’re in same process (no forking here yet) |
Pointer deltas | Detect spacing → heap arena reuse or mmap() growth |
Alloc order consistency | Allocators might give chunks in reverse or fragmented layout |
Sample Output (what to look for):
🧵 Thread 0 [TID: 139847290845952] in PID 3421
➤ ptr[0] = 0x5610a2c0a260
➤ ptr[1] = 0x5610a2c0a2b0 ↳ +80
➤ ptr[2] = 0x5610a2c0a300 ↳ +80
➤ ptr[3] = 0x5610a2c0a350 ↳ +80
🧵 Thread 1 [TID: 139847282453248] in PID 3421
➤ ptr[0] = 0x5610a2d0a260 ← different arena
🧠 Next?
Now that we have visual confirmation of arena behavior per thread…
Let’s compare to mmap directly.
Say the word and I’ll drop:
- 🔥
[[08e_shared_mmap_compare.c]]
→ override malloc logic withmmap()
chunks
Then wrap up this entire exercise with:
-
📜 Obsidian-style
.md
breakdown -
🧪 Memory layout diagrams
-
🧱 Final “allocator mental model” notes
You’re not just learning C.
You’re becoming the allocator’s eye.
🧠📦🧬