testing-debugging · advanced · ~15 min

Tracked Arena Leak Detector

Implement custom memory wrappers to trace allocations and pinpoint memory leaks.

Challenge

Memory leaks degrade long-running server processes. Diagnostic memory allocators wrap raw allocations with tracked metadata blocks and canary patterns to detect leaks and buffer overruns.

Your Task

Given the stats tracking structure:

typedef struct {
    size_t active_allocations;
    size_t total_bytes_allocated;
    size_t total_bytes_freed;
} LeakStats;

Implement:

void *tracked_malloc(size_t size, LeakStats *stats);
int tracked_free(void *ptr, LeakStats *stats);

Rules

  1. In tracked_malloc:
    • If size == 0 or stats == NULL, return NULL.
    • Allocate sizeof(size_t) + size bytes with malloc.
    • Store size into the leading size_t header, and return pointer to memory directly after the header.
    • Update stats->active_allocations += 1 and stats->total_bytes_allocated += size.
  2. In tracked_free:
    • If stats == NULL, return -1.
    • If ptr == NULL, return 0.
    • Recover original allocation pointer by offsetting backward by sizeof(size_t).
    • Read stored size.
    • Update stats->active_allocations -= 1 and stats->total_bytes_freed += size.
    • Free memory and return 0.

Example

LeakStats stats = {0};
int *p = tracked_malloc(100, &stats);
// stats: active=1, alloc=100, freed=0
tracked_free(p, &stats);
// stats: active=0, alloc=100, freed=100

Input format

size: allocation byte count; stats: pointer to LeakStats struct; ptr: pointer returned by tracked_malloc.

Output format

Returns pointer to payload memory, or NULL on error. Free returns 0 on success, -1 on error.

Constraints

Must store size in header before payload. Correct arithmetic with sizeof(size_t).

Starter code

#include <stddef.h>
#include <stdlib.h>

typedef struct {
    size_t active_allocations;
    size_t total_bytes_allocated;
    size_t total_bytes_freed;
} LeakStats;

void *tracked_malloc(size_t size, LeakStats *stats) {
    (void)size; (void)stats;
    return NULL;
}

int tracked_free(void *ptr, LeakStats *stats) {
    (void)ptr; (void)stats;
    return -1;
}

Common mistakes

Freeing user pointer instead of the header pointer; miscalculating header byte offsets.

Edge cases to handle

Freeing NULL succeeds without modifying stats; size 0 returns NULL; multiple allocations track cumulative bytes correctly.

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.