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.
| 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:
- The old pointer after
realloc.q = realloc(p, n)(alsoreallocarray,g_realloc,g_try_realloc,OPENSSL_realloc) into a different variable, followed within 15 lines by a dereference ofp(p->x,p[i],*p), a call that takesporp + off, orreturn p, beforepis reassigned. Whenreallocmoves the block it frees the old one, sopdangles. Uses inside the branch that testsqforNULLare the failure path, wherepis still valid, and are not reported. - A pointer into a temporary (C++).
const char *s = ...orreturn ...ofc_str()/data()onstd::string(...),std::to_string(...),std::format/fmt::format(...), a stringstream.str(),.substr(...)or a parenthesised concatenation; QttoUtf8()/toLatin1()/toLocal8Bit()followed by.constData()/.data(); and astd::string_viewinitialised 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, andp = realloc(p, n)(a leak on failure, not a use after free).- Pointer arithmetic such as
cur - baseused 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.