Skip to content

VNX-416: Use after free: stale pointer after realloc, or a pointer into a destroyed temporary

Reports the old pointer used after a successful realloc into a different variable, and C++ pointers or string_views kept into a temporary string (c_str()/data() of std::to_string, std::string(...) or std::string{...}, .str(), .substr(), a concatenation, Qt toUtf8().constData()). The realloc failure branch, p = realloc(p, n), full-expression uses and named strings are not reported.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-416
Severity High
SARIF level error
Kind sast
Languages C, C++
CWE CWE-416
CAPEC CAPEC-66
ATT&CK T1190
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 dangling-pointer memory-safety cwe-416
Source internal/sast/rules/vnx-416.rego

Overview

VNX-416 maps to CWE-416: Use After Free. It covers two shapes that VNX-C-004 (a pointer used after free()/delete) does not:

  1. The old pointer after realloc. q = realloc(p, n) (also reallocarray, g_realloc, g_try_realloc, OPENSSL_realloc) into a different variable, followed within 15 lines by a dereference of p (p->x, p[i], *p), a call that takes p or p + off, or return p, before p is reassigned. When realloc moves the block it frees the old one, so p dangles. Uses inside the branch that tests q for NULL are the failure path, where p is still valid, and are not reported.
  2. A pointer into a temporary (C++). const char *s = ... or return ... of c_str()/data() on std::string(...), std::to_string(...), std::format/fmt::format(...), a stringstream .str(), .substr(...) or a parenthesised concatenation; Qt toUtf8()/toLatin1()/toLocal8Bit() followed by .constData()/.data(); and a std::string_view initialised from such a temporary. The temporary is destroyed at the end of the statement.

What Gets Flagged

char *tmp = realloc(buf, len + n + 1);
if (!tmp) {
    free(buf);           // not flagged: realloc failed, buf is still valid
    return NULL;
}
memcpy(buf + len, s, n + 1);   // FLAGGED: buf may have been freed
const char *label = std::to_string(port).c_str();   // FLAGGED
std::string_view key = std::to_string(id);          // FLAGGED
const char *key = name.toLatin1().constData();      // FLAGGED

What Is Not Flagged

  • buf = tmp; before the next use, and p = realloc(p, n) (a leak on failure, not a use after free).
  • Pointer arithmetic such as cur - base used only to compute an offset.
  • A temporary used within the same full expression: printf("%s", std::to_string(n).c_str()).
  • c_str()/data() of a named string, comments and string literals.

Remediation

Assign the result of realloc back after checking it, and keep strings alive in a named variable for as long as a pointer into them is used:

char *tmp = realloc(buf, size);
if (tmp == NULL) { free(buf); return -1; }
buf = tmp;
std::string label = std::to_string(port);
use(label.c_str());

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-416
// nosec vnx-416

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

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