VNX-1037: Processor Optimization Removal of Security-critical Code
Reports C and C++ secrets wiped with memset, bzero or ZeroMemory just before the memory is freed or goes out of scope, a dead store the optimizer may delete.
| Field | Value |
|---|---|
| Rule ID | VNX-1037 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | C, C++ |
| CWE | CWE-1037, CWE-14 |
| CAPEC | CAPEC-188 |
| ATT&CK | T1552 |
| CWSS vector | CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | optimization memset volatile dead-store memory-zeroing cwe-1037 |
| Source | internal/sast/rules/vnx-cwe1037-auto.rego |
Overview
VNX-1037 covers CWE-1037: Processor Optimization Removal or Modification of Security-critical Code. Processor-level removal (speculative execution) has no line-level signature, so this rule reports the concrete optimizer case: a compiler’s dead-store elimination of a buffer clear (CWE-14).
What is reported, in C and C++:
- A zeroing call (
memset/std::memsetwith fill value0,'\0'or0x00,bzero,ZeroMemoryorRtlZeroMemory). - Whose buffer is named as a secret: key, password, passwd, passphrase, pwd, pin, otp, secret, seed, token, cred/credential, plaintext or master, including camelCase and prefixes such as
szPasswordorsession_key. - Whose next statements, before any read of the buffer, free it (
free,OPENSSL_free,g_free,kfree,HeapFree,LocalFree,delete…) or, for a local array, struct orstd::arraydeclared in the function, return or close its scope.
Not reported: explicit_bzero, memset_s, SecureZeroMemory, RtlSecureZeroMemory, OPENSSL_cleanse, sodium_memzero and other guaranteed wipes; a clear followed by a read of the buffer or by a compiler barrier that names it (initialisation, asm volatile); a clear of caller-owned memory (a pointer parameter or a struct member that outlives the function); buffers not named as secrets; non-zero fill patterns; test code, comments and strings.
Why This Matters
The C and C++ standards let a compiler delete a store that nothing reads afterwards. A memset(password, 0, sizeof password) right before free(password) or the end of the function is exactly such a store, and GCC, Clang and MSVC remove it at ordinary optimisation levels. The source code says the secret was wiped, but the binary leaves it in freed heap or on the stack, where a later allocation, a core dump, a swap file or a memory-disclosure bug can read it.
What Gets Flagged
char password[64];
read_password(password, sizeof password);
check(password);
memset(password, 0, sizeof(password)); /* FLAGGED: dead store before return */
return ok;
unsigned char derived_key[32];
pbkdf2(pin, derived_key, sizeof derived_key);
unlock_store(derived_key);
memset(derived_key, 0x00, sizeof derived_key); /* FLAGGED: local array leaves scope */
}
std::memset(master_key.data(), 0, master_key.size()); // FLAGGED: std::array leaves scope
}
What Is Not Flagged
explicit_bzero(password, sizeof password);
OPENSSL_cleanse(key, key_len);
SecureZeroMemory(szPassword, sizeof szPassword);
Remediation
- Use a wipe the compiler must keep:
explicit_bzero(glibc, BSD),memset_s(C11 Annex K),SecureZeroMemory/RtlSecureZeroMemory(Windows),OPENSSL_cleanseorsodium_memzero. - In C++, wrap secrets in a type whose destructor calls one of those functions.
- If none is available, write through a
volatilefunction pointer tomemset, or follow the clear with a compiler barrier that names the buffer.
References
- CWE-1037: Processor Optimization Removal or Modification of Security-critical Code
- CWE-14: Compiler Removal of Code to Clear Buffers
- SEI CERT MSC06-C: Beware of compiler optimizations
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-1037
// nosec vnx-1037
Or record an ignore rule: vulnetix ignore add --rule vnx-1037 --reason "...". See Suppressing findings.