Excessive Iteration

Description

Excessive Iteration is a resource consumption vulnerability where software performs an iteration or loop without sufficiently limiting the number of times the loop executes. When loop bounds are influenced by external input or derive from unchecked values, attackers can cause the application to consume excessive CPU cycles, memory, or other resources. The loop does not need to be infinite to cause harm—the impact depends on the resources consumed per iteration and the total number of iterations. Even finite loops can cause denial of service if they run for millions of iterations or perform expensive operations in each cycle.

Risk

This vulnerability enables denial of service attacks by exhausting system resources. Attackers who can influence loop bounds through input parameters, file contents, network data, or other channels can make applications unresponsive. CPU-bound loops can starve other processes and threads. Memory-consuming loops can trigger out-of-memory conditions and crashes. In servers, excessive iteration can slow response times for all users or exhaust thread pools. The vulnerability is particularly dangerous when processing untrusted data like network packets, file formats, or user input where length or count fields control iteration.

Solution

Always validate and limit loop bounds, especially when derived from external input. Implement maximum iteration limits appropriate for the application context. Use timeout mechanisms to abort long-running loops. Validate input parameters before using them in loop conditions—reject zero or negative values that could cause infinite loops. Monitor resource consumption and implement circuit breakers for expensive operations. Consider asynchronous processing with cancellation support for long-running iterations. Ensure loop variables are properly updated to make progress toward termination.

Common Consequences

ImpactDetails
AvailabilityScope: Availability

Resource Consumption - Excessive loops consume unexpected amounts of CPU cycles and memory, degrading system performance.
AvailabilityScope: Availability

DoS: Slow Response - Software operation slows down, causing extended response times for users.
AvailabilityScope: Availability

DoS: Crash, Exit, or Restart - Resource exhaustion (e.g., out-of-memory) can cause the program to crash.

Example Code

Vulnerable Code

// Vulnerable: Recursive function with unbounded depth
void vulnerable_recursive(int flag) {
    if (flag == 1) {
        // ... do something ...
    }

    // Vulnerable: flag is never modified, causes infinite recursion
    vulnerable_recursive(flag);

    // Stack will overflow
}
// Vulnerable: Loop bound depends on unvalidated parameter
public class VulnerableInventory {

    public boolean isReorder(int currentCount, int rateSold) {
        boolean isReorder = false;

        // Vulnerable: If rateSold is 0, this loops forever!
        while (currentCount > 0) {
            if (currentCount < 10) {
                isReorder = true;
            }
            currentCount = currentCount - rateSold;
        }

        return isReorder;
    }
}
# Vulnerable: User-controlled iteration count
def vulnerable_process_items(count):
    # Vulnerable: No limit on count
    # Attacker can set count = 1000000000
    for i in range(count):
        expensive_operation()  # DoS via CPU exhaustion
// Vulnerable: Loop processes network data without size limits
void vulnerable_parse_records(char *data, int num_records) {
    // Vulnerable: num_records comes from untrusted data
    // Attacker sends num_records = MAX_INT
    for (int i = 0; i < num_records; i++) {
        process_record(data + (i * RECORD_SIZE));
    }
}
// Vulnerable: No limit on recursion depth
function vulnerableFlatten(arr) {
    let result = [];
    for (let item of arr) {
        if (Array.isArray(item)) {
            // Vulnerable: Deeply nested arrays cause stack overflow
            result = result.concat(vulnerableFlatten(item));
        } else {
            result.push(item);
        }
    }
    return result;
}
// Attack: Create deeply nested array [[[[...]]]]
// Vulnerable: Processing zip file with excessive entries
<?php
function vulnerable_extract($zipFile) {
    $zip = new ZipArchive;
    $zip->open($zipFile);

    // Vulnerable: No limit on number of entries
    for ($i = 0; $i < $zip->numFiles; $i++) {
        $filename = $zip->getNameIndex($i);
        extract_file($zip, $filename);
    }
    $zip->close();
}

// Attack: Zip bomb with millions of tiny files
?>
// Vulnerable: While loop with floating point comparison
void vulnerable_float_loop(double start, double end, double step) {
    double value = start;

    // Vulnerable: Floating point errors may prevent reaching 'end'
    while (value != end) {
        process(value);
        value += step;  // Accumulated error means value may never equal end
    }
}

Fixed Code

// Fixed: Limit recursion depth
#define MAX_RECURSION_DEPTH 100

void fixed_recursive(int flag, int depth) {
    // Fixed: Check recursion depth
    if (depth > MAX_RECURSION_DEPTH) {
        return;  // Prevent stack overflow
    }

    if (flag == 1) {
        // ... do something ...
        return;  // Fixed: Actual termination condition
    }

    fixed_recursive(flag - 1, depth + 1);  // Fixed: Progress toward termination
}
// Fixed: Validate loop parameters
public class FixedInventory {

    private static final int MAX_ITERATIONS = 10000;

    public boolean isReorder(int currentCount, int rateSold) {
        // Fixed: Validate rateSold to prevent infinite loop
        if (rateSold < 1) {
            throw new IllegalArgumentException("rateSold must be positive");
        }

        // Fixed: Also add maximum iteration check
        int iterations = 0;
        boolean isReorder = false;

        while (currentCount > 0 && iterations < MAX_ITERATIONS) {
            if (currentCount < 10) {
                isReorder = true;
            }
            currentCount = currentCount - rateSold;
            iterations++;
        }

        return isReorder;
    }
}
# Fixed: Limit iteration count
MAX_ITEMS = 10000

def fixed_process_items(count):
    # Fixed: Enforce maximum limit
    if count < 0:
        raise ValueError("count must be non-negative")

    actual_count = min(count, MAX_ITEMS)

    for i in range(actual_count):
        expensive_operation()

    if count > MAX_ITEMS:
        logging.warning(f"Truncated iteration from {count} to {MAX_ITEMS}")
// Fixed: Validate record count against data size
#define MAX_RECORDS 10000

int fixed_parse_records(char *data, size_t data_size, int num_records) {
    // Fixed: Validate num_records
    if (num_records < 0 || num_records > MAX_RECORDS) {
        return -1;  // Invalid count
    }

    // Fixed: Verify data_size can hold num_records
    size_t required_size = (size_t)num_records * RECORD_SIZE;
    if (required_size > data_size) {
        return -1;  // Data too small
    }

    for (int i = 0; i < num_records; i++) {
        process_record(data + (i * RECORD_SIZE));
    }

    return 0;
}
// Fixed: Limit recursion depth for flatten
function fixedFlatten(arr, maxDepth = 10) {
    if (maxDepth < 0) {
        throw new Error('Maximum nesting depth exceeded');
    }

    let result = [];
    for (let item of arr) {
        if (Array.isArray(item)) {
            // Fixed: Decrement depth limit
            result = result.concat(fixedFlatten(item, maxDepth - 1));
        } else {
            result.push(item);
        }
    }
    return result;
}

// Alternative: Iterative approach with explicit stack
function fixedFlattenIterative(arr, maxIterations = 100000) {
    const result = [];
    const stack = [...arr];
    let iterations = 0;

    while (stack.length > 0) {
        if (++iterations > maxIterations) {
            throw new Error('Maximum iterations exceeded');
        }

        const item = stack.pop();
        if (Array.isArray(item)) {
            stack.push(...item);
        } else {
            result.push(item);
        }
    }

    return result.reverse();
}
// Fixed: Limit number of files processed
<?php
define('MAX_ZIP_FILES', 1000);

function fixed_extract($zipFile) {
    $zip = new ZipArchive;
    $zip->open($zipFile);

    // Fixed: Limit number of files
    $numFiles = min($zip->numFiles, MAX_ZIP_FILES);

    for ($i = 0; $i < $numFiles; $i++) {
        $filename = $zip->getNameIndex($i);
        extract_file($zip, $filename);
    }

    if ($zip->numFiles > MAX_ZIP_FILES) {
        error_log("Warning: Zip file truncated, had " . $zip->numFiles . " files");
    }

    $zip->close();
}
?>
// Fixed: Use epsilon comparison for floating point loops
#include <math.h>

#define MAX_ITERATIONS 1000000

void fixed_float_loop(double start, double end, double step) {
    double value = start;
    int iterations = 0;

    // Fixed: Use epsilon comparison and iteration limit
    double epsilon = fabs(step) * 0.001;

    while (fabs(value - end) > epsilon && iterations < MAX_ITERATIONS) {
        process(value);
        value += step;
        iterations++;
    }

    if (iterations >= MAX_ITERATIONS) {
        log_warning("Float loop reached maximum iterations");
    }
}

  • CWE-691: Insufficient Control Flow Management (parent)
  • CWE-835: Loop with Unreachable Exit Condition (child - infinite loop)
  • CWE-674: Uncontrolled Recursion (child)
  • CWE-606: Unchecked Input for Loop Condition (related)
  • CWE-400: Uncontrolled Resource Consumption (related)

References

  1. MITRE Corporation. "CWE-834: Excessive Iteration." https://cwe.mitre.org/data/definitions/834.html
  2. OWASP. "Denial of Service." https://owasp.org/www-community/attacks/Denial_of_Service
  3. CERT C Secure Coding Standard. "MSC17-C. Finish every set of statements associated with a case label with a break statement."