testing-debugging · intermediate · ~15 min

Dangling Pointer Guard

Understand and implement generational handles to detect dangling pointers and use-after-free bugs.

Challenge

Dangling pointers and Use-After-Free (UAF) vulnerabilities happen when code accesses memory after it has been freed. Generational indexing (generational arena / slot maps) detects stale references by attaching a monotonic version counter to each slot.

Your Task

Given the slot and handle structures:

typedef struct {
    void *data;
    uint32_t generation;
    int active;
} Slot;

typedef struct {
    uint32_t index;
    uint32_t generation;
} SlotRef;

Implement:

SlotRef slot_insert(Slot *slots, size_t cap, void *data);
void *slot_lookup(const Slot *slots, size_t cap, SlotRef ref);
int slot_remove(Slot *slots, size_t cap, SlotRef ref);

Rules

  1. In slot_insert: find the first inactive slot (active == 0). Set data = data, active = 1, increment generation by 1. Return a SlotRef with index and the updated generation. If no slot available or slots == NULL, return { .index = 0, .generation = 0 }.
  2. In slot_lookup: if slots == NULL, ref.index >= cap, slot is not active, or slots[ref.index].generation != ref.generation, return NULL (prevents UAF!). Otherwise return slots[ref.index].data.
  3. In slot_remove: if slots == NULL, ref.index >= cap, slot is not active, or generation mismatch, return -1. Otherwise mark active = 0, set data = NULL, and return 0.

Example

Slot table[4] = {0};
SlotRef r = slot_insert(table, 4, "resource1");
slot_lookup(table, 4, r); // returns "resource1"
slot_remove(table, 4, r); // returns 0
slot_lookup(table, 4, r); // returns NULL (stale handle!)

Input format

slots: array of Slot structures; cap: table capacity; ref: handle containing index and generation.

Output format

Safe pointer retrieval or NULL if stale/dangling; return 0/-1 for removal.

Constraints

Generational counters must prevent use-after-free even if slot is reused.

Starter code

#include <stddef.h>
#include <stdint.h>

typedef struct {
    void *data;
    uint32_t generation;
    int active;
} Slot;

typedef struct {
    uint32_t index;
    uint32_t generation;
} SlotRef;

SlotRef slot_insert(Slot *slots, size_t cap, void *data) {
    (void)slots; (void)cap; (void)data;
    SlotRef r = {0, 0};
    return r;
}

void *slot_lookup(const Slot *slots, size_t cap, SlotRef ref) {
    (void)slots; (void)cap; (void)ref;
    return NULL;
}

int slot_remove(Slot *slots, size_t cap, SlotRef ref) {
    (void)slots; (void)cap; (void)ref;
    return -1;
}

Common mistakes

Forgetting to check generation equality; not incrementing generation when slot is reused.

Edge cases to handle

Reusing an existing slot increments generation, invalidating old references; out-of-bounds index returns NULL.

Background lessons

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