Safeguard
Application Security•

CWE-761 Free of Pointer Not at Start of Buffer in C

Calling free on a pointer that has moved into the middle of a heap buffer corrupts memory. See the pattern, how ASan finds it, and the simple fix.

Safeguard Security Team
3 min read

CWE-761 is a heap memory bug. The product calls free() on a pointer to a heap allocation, but the pointer is not at the start of the buffer.

The record lists C on memory unsafe platforms. Parsers and string handling code that walk through buffers are the typical home for it.

How it happens

The allocator tracks the start of each block. Hand free() an address in the middle and the allocator reads bookkeeping data that is not there. MITRE explains the usual cause: memory allocated with the malloc() family, then pointer arithmetic moves the pointer into the interior or end of the buffer before free() is called.

// Vulnerable: p has moved, so free(p) gets an interior address
char *p = malloc(64);
while (*p != ':') p++;
free(p);

The record also cites CVE-2019-11930, where a function internally called calloc and returned a pointer at an index inside the allocated buffer, which led to freeing invalid memory.

What an attacker gains

The recorded consequences are memory modification, a crash, and in some cases execution of unauthorized code. A crash is the mild outcome. Corrupted allocator state can sometimes be steered.

How to find it

MITRE recommends dynamic tools that insert runtime memory checks during compilation, such as AddressSanitizer for C and C++ or valgrind. They need crafted inputs that reach the code, so feed them your test corpus and fuzzers.

How to fix it

When you traverse a buffer with pointer arithmetic, use a separate variable to track progress and keep the original address for freeing.

// Fix: keep the allocation pointer untouched
char *buf = malloc(64);
char *p = buf;
while (*p != ':') p++;
free(buf);

MITRE adds longer term options: use a vetted library or framework that guards against this, use a language with abstractions for allocation and deallocation, and in C++ consider smart pointers such as those in boost.

Where it hides in real code

Hand written parsers are the usual home. A loop advances a pointer to skip whitespace, find a delimiter or step over a header, and later the same variable is passed to free() because it was the one that pointed at the buffer last.

The CVE in the record shows a second pattern: the interior pointer is produced inside a library call, not by the caller. A function allocates a block, returns an address partway into it, and callers cannot tell that the address is not the allocation start. Documentation that says "caller must free the result" turns into a trap.

Two small habits remove most of the risk. Give the allocation pointer its own name that nothing modifies, for example base or buf, and walk with a second variable. And when designing an API, either return the start of the allocation or provide a matching release function that knows the real address. Then run the code under AddressSanitizer in CI, since it reports the bad free with a stack trace on the first occurrence.

Quick check

Why does the original pointer need to be preserved?

free() must receive exactly the address malloc returned. Any moved pointer is not that address.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.