Skip to content

VNX-C-004: Use-after-free: pointer used after free()

Reports a C, C++ or Objective-C pointer used after free(), g_free, kfree, OPENSSL_free or delete: a dereference, a call that takes it or a return of it within 15 lines on the same path, before it is reassigned and before the path leaves, plus a for loop that advances through the pointer its body frees. Nulled or reassigned pointers, comparisons, other branches, saved next pointers, macros and comments are not reported.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-C-004
Severity High
SARIF level error
Kind sast
Languages C, C++
CWE CWE-416
CAPEC CAPEC-123
ATT&CK T1203
CWSS vector CWSS:1.0/TI:H/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H
Tags use-after-free c memory-safety heap
Source internal/sast/rules/vnx-c-004.rego

Overview

This rule finds a release (free(p), g_free, kfree, vfree, kvfree, OPENSSL_free, std::free, C++ delete/delete[]) of a variable or member expression (p, ctx->buf) and reports the first later line, within 15 lines and on the same path, that dereferences it (p->x, p[i], *p), passes it to a call (memcpy(p, ...), printf("%s", p)) or returns it. The search stops at a reassignment (p = NULL, p = malloc(n), get(&p)), at return/break/continue/goto/exit/throw, at an else or case label and at the end of the function, so a use on another branch is not reported. It also reports a for loop whose step reads through the pointer its body frees (for (p = head; p; p = p->next) free(p);). Comparisons (if (p), p != NULL), sizeof(p), #define macros, string literals and comments are not uses. Aliases (q = p; free(p); q->x) and uses in other functions are out of scope.

Use-after-free occurs when a program frees a heap-allocated object and then accesses memory through the now-dangling pointer. After free() returns the memory to the allocator, the contents of that memory are undefined, the allocator may have modified them, a subsequent allocation may have reused the memory with different contents, or the memory may have been returned to the OS. Reading through a freed pointer yields unpredictable or attacker-influenced data; writing through one corrupts the heap.

Why This Matters

Use-after-free vulnerabilities are among the most exploited memory safety bugs in modern software. Chrome, Firefox, the Linux kernel, and countless other production systems have had critical use-after-free CVEs. The reason they are so exploitable is that an attacker who can trigger a specific allocation between the free() and the subsequent access can control the contents of the freed memory, by allocating an object of their choice in the same heap slot, and influence the behaviour of the use through carefully crafted data.

In browsers and language runtimes, use-after-free bugs are routinely turned into arbitrary code execution by heap spraying: filling memory with attacker-controlled data so that when the freed pointer is dereferenced, it reads attacker data. In server applications, a use-after-free in a parsing or protocol handling code path can be triggered remotely, turning a memory safety bug into remote code execution. This is covered by ATT&CK T1203 and CAPEC-123.

The pattern is also a common source of security-relevant logic bugs even when not directly exploitable for code execution: reading freed memory can return credentials, session tokens, or other sensitive data that a previous allocation stored in that memory region.

What Gets Flagged

// FLAGGED: node freed, then its next field read
free(top);
*head = top->next;

// FLAGGED: freed string logged
free(name);
fprintf(stderr, "closing %s\n", name);

// FLAGGED: conditional free, unconditional use
if (err) {
    free(c);
}
return c->fd;

// FLAGGED: loop step reads the node the body freed
for (it = head; it != NULL; it = it->next) {
    free(it);
}
// FLAGGED: C++ delete then member access
delete entry;
return entry->len > 0 ? 1 : 0;

Not flagged: free(b->data); b->data = NULL;, free(p); p = malloc(n);, a free followed by return or goto out, and the saved-next idiom next = cur->next; free(cur); cur = next;.

Remediation

  1. Set the pointer to NULL immediately after every free() call: free(ptr); ptr = NULL;.
  2. Before dereferencing a pointer, check that it is not NULL.
  3. Adopt ownership conventions: once a pointer is freed, its variable goes out of scope, is reassigned, or is set to NULL, never left pointing at freed memory.
  4. Use static analysis tools (Valgrind, AddressSanitizer, HeapTrack) in development and testing to catch use-after-free bugs at runtime.
  5. Consider moving to C++ with RAII, smart pointers (std::unique_ptr, std::shared_ptr), or to a memory-safe language.
// SAFE: pointer nulled immediately after free
free(buffer);
buffer = NULL;
// ... later use with guard ...
if (buffer != NULL) {
    memcpy(dest, buffer, len);
}

// SAFE: save value before freeing, use the saved value
char *next_node = node->next;
free(node);
node = NULL;
// Use next_node, not node
process(next_node);

// SAFE: C++ RAII, memory freed automatically when unique_ptr goes out of scope
auto ctx = std::make_unique<CryptoContext>();
crypto_init(ctx.get());
// ctx freed automatically, no dangling pointer possible

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-c-004
// nosec vnx-c-004

Or record an ignore rule: vulnetix ignore add --rule vnx-c-004 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-c-004.rego, cli/internal/sast/docs/vnx-c-004.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.