VNX-1325: Improperly Controlled Sequential Memory Allocation
Detects allocations (Go make, Java new byte[]/ByteBuffer.allocate, C/C++ malloc/calloc/realloc/new T[]) sized by a value that arrives from outside the program, such as a parsed number, a length prefix or a request header, with no upper bound checked first, and loops bounded by such a value that allocate on every pass. Sizes from len()/sizeof, constants, values compared against a maximum or clamped with min() are not reported.
| Field | Value |
|---|---|
| Rule ID | VNX-1325 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | C, C++, Go, Java |
| CWE | CWE-1325 |
| CAPEC | CAPEC-130 |
| ATT&CK | T1499 |
| 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 | memory allocation dos loop cwe-1325 |
| Source | internal/sast/rules/vnx-1325.rego |
Overview
When memory is allocated in a loop and the loop count or individual allocation size comes from user-controlled input without a total cap, an attacker can exhaust available memory. The cumulative allocation may be orders of magnitude larger than any single checked value. This leads to OOM crashes, swap thrashing, or process termination, all denial-of-service conditions. Severity: Medium | CWE: CWE-1325 – Improperly Controlled Sequential Memory Allocation
Why This Matters
A common pattern is allocating a buffer for each item in a user-supplied list: if the user can specify 10 million items each requiring a 1 KB buffer, the total allocation is 10 GB. Without a total limit, even individually innocuous allocation sizes become dangerous at scale. This class of bug has affected JSON parsers, XML processors, and archive extraction utilities.
What Gets Flagged
// FLAGGED: malloc in loop, no total size tracking
int n = atoi(argv[1]); // user-controlled count
for (int i = 0; i < n; i++) {
buffers[i] = malloc(4096); // total = n * 4096, no cap
}
// FLAGGED: make([]...) sized by a value parsed from the request
size, _ := strconv.Atoi(r.Header.Get("X-Size"))
buf := make([]byte, size) // no upper bound
process(buf)
// FLAGGED: byte[] allocation with user-supplied size
int size = Integer.parseInt(request.getParameter("bufferSize"));
byte[] buffer = new byte[size]; // can be up to 2GB with one request
// FLAGGED: a length prefix from the network sizes new[]
uint32_t len = ntohl(raw);
char *payload = new char[len];
What Is Not Flagged
An allocation is only reported when its size comes from outside the program. These are not:
// NOT FLAGGED: sized by data the program already holds, so it cannot grow past it
out := make([]string, 0, len(items))
rows := make([][]string, len(items))
// NOT FLAGGED: a constant
buf := make([]byte, 4096)
Also not reported: a size compared against a maximum in an if or assert above the allocation (a check for zero or a negative value alone does not count), a size clamped with min() or Math.min, a loop whose body checks a running total against a limit before allocating, and test files.
Remediation
// SAFE: Track total allocation and enforce cap
#define MAX_TOTAL_BYTES (64 * 1024 * 1024) // 64 MB total
size_t total = 0;
for (int i = 0; i < n; i++) {
if (total + 4096 > MAX_TOTAL_BYTES) {
error("Allocation limit exceeded");
break;
}
buffers[i] = malloc(4096);
total += 4096;
}
// SAFE: Validate size before allocation
private static final int MAX_BUFFER_SIZE = 10 * 1024 * 1024; // 10 MB
int size = Integer.parseInt(request.getParameter("bufferSize"));
if (size < 0 || size > MAX_BUFFER_SIZE) {
throw new IllegalArgumentException("Buffer size out of range");
}
byte[] buffer = new byte[size];
// SAFE: Cap individual and total allocations
const maxTotalBytes = 64 * 1024 * 1024
var totalAllocated int64
for i := 0; i < count; i++ {
if totalAllocated+int64(size) > maxTotalBytes {
return fmt.Errorf("allocation limit exceeded")
}
buf := make([]byte, size)
totalAllocated += int64(size)
process(buf)
}
References
- CWE-1325: Improperly Controlled Sequential Memory Allocation
- CWE-400: Uncontrolled Resource Consumption
- OWASP Denial of Service Cheat Sheet
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-1325
// nosec vnx-1325
Or record an ignore rule: vulnetix ignore add --rule vnx-1325 --reason "...". See Suppressing findings.