Skip to content

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.

Updated 02 Nov 2026Checked against cli@de9b52a
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

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.

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