Loop with Unreachable Exit Condition ('Infinite Loop')

Description

Loop with Unreachable Exit Condition, commonly known as an Infinite Loop, is a control flow vulnerability where software contains an iteration or loop with an exit condition that can never be satisfied, causing the loop to run indefinitely. This differs from CWE-834 (Excessive Iteration) in that the loop has no valid path to termination—the exit condition is logically impossible to reach. Common causes include loop variables that are never modified, exit conditions that can never be true due to input values, and logic errors in complex termination conditions.

Risk

Infinite loops cause denial of service by consuming CPU resources indefinitely. The affected thread or process becomes completely unresponsive, unable to handle other tasks or requests. In single-threaded applications, the entire program freezes. In servers, infinite loops can exhaust thread pools as threads become stuck. Memory may also be consumed if the loop allocates resources. Unlike excessive iteration where the loop eventually terminates, infinite loops require external intervention (killing the process) to stop. The vulnerability is particularly dangerous when triggered by specific input values that attackers can provide.

Solution

Implement maximum iteration limits as a safety net, even for loops that should terminate normally. Validate input parameters that influence loop conditions—reject values like zero that could cause infinite loops. Ensure loop variables are properly modified on each iteration to make progress toward the exit condition. Use timeout mechanisms for loops that could run long. Add watchdog mechanisms in critical code paths. Implement health checks that detect stuck threads. During code review, verify that every loop has a reachable exit condition. Use static analysis tools that can detect potential infinite loops.

Common Consequences

ImpactDetails
AvailabilityScope: Availability

DoS: Resource Consumption - Infinite loops consume CPU cycles indefinitely, preventing the thread from doing useful work.
AvailabilityScope: Availability

DoS: Slow Response - Other operations are starved of CPU time, causing slow response or complete unresponsiveness.
AvailabilityScope: Availability

DoS: System Hang - In severe cases, the entire application or system may become unresponsive.

Example Code

Vulnerable Code

// Vulnerable: Server connection loop with no attempt limit
int connected;
struct sockaddr_in servaddr;
int servsock;

// Vulnerable: No maximum attempts, loops forever if server unresponsive
do {
    connected = connect(servsock, (struct sockaddr *)&servaddr, sizeof(servaddr));
} while (connected < 0);

// Will loop infinitely if server never accepts connection
// Vulnerable: Inventory loop with division by zero potential
public class VulnerableInventoryCheck {

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

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

        return isReorder;
    }
}
# Vulnerable: Loop variable never updated
def vulnerable_search(items, target):
    i = 0
    found = False

    while not found and i < len(items):
        if items[i] == target:
            found = True
        # Vulnerable: Forgot to increment i!
        # Loop runs forever if target not at index 0

    return found
// Vulnerable: Off-by-one causes infinite loop
void vulnerable_array_process(int *arr, int size) {
    int i = 0;

    // Vulnerable: Should be i < size, not i <= size
    // When i == size, condition is true but no progress is made
    while (i <= size) {
        process(arr[i]);
        if (arr[i] == SENTINEL) {
            break;
        }
        // If sentinel not found by index size-1, infinite loop
        i++;
    }
}
// Vulnerable: Floating point comparison never matches
function vulnerableFloatLoop() {
    let value = 0.1;
    let sum = 0;

    // Vulnerable: Due to floating point errors, value may never equal 1.0
    while (value !== 1.0) {
        sum += value;
        value += 0.1;  // 0.1 + 0.1 + ... doesn't exactly equal 1.0
    }

    return sum;
}
// Vulnerable: Signed/unsigned comparison issue
void vulnerable_countdown(unsigned int count) {
    int i = count;

    // If count is very large (> INT_MAX), i wraps to negative
    // Vulnerable: i-- will wrap around, never reaching 0
    while (i >= 0) {
        process(i);
        i--;  // When i is INT_MIN, i-- wraps to INT_MAX
    }
}
// Vulnerable: State machine with unreachable exit
<?php
function vulnerable_state_machine($input) {
    $state = 'START';

    while ($state !== 'END') {
        switch ($state) {
            case 'START':
                $state = 'PROCESSING';
                break;
            case 'PROCESSING':
                process($input);
                // Vulnerable: 'END' state never set!
                // Stays in PROCESSING forever
                break;
        }
    }
}
?>

Fixed Code

// Fixed: Connection loop with maximum attempts
#define MAX_ATTEMPTS 10

int connected;
struct sockaddr_in servaddr;
int servsock;
int count = 0;

// Fixed: Limit number of connection attempts
do {
    connected = connect(servsock, (struct sockaddr *)&servaddr, sizeof(servaddr));
    count++;

    if (connected < 0 && count < MAX_ATTEMPTS) {
        sleep(1);  // Back off before retry
    }
} while (connected < 0 && count < MAX_ATTEMPTS);

if (connected < 0) {
    handle_connection_failure();
}
// Fixed: Validate parameters to prevent infinite loop
public class FixedInventoryCheck {

    private static final int MAX_ITERATIONS = 10000;

    public boolean isReorder(int currentCount, int rateSold) {
        // Fixed: Validate rateSold to prevent infinite loop
        if (rateSold < 1) {
            // Can't determine reorder status with invalid rate
            return currentCount < 10;  // Simple threshold check instead
        }

        boolean isReorder = false;
        int iterations = 0;

        // Fixed: Also add iteration limit as safety net
        while (currentCount > 0 && iterations < MAX_ITERATIONS) {
            if (currentCount < 10) {
                isReorder = true;
            }
            currentCount = currentCount - rateSold;
            iterations++;
        }

        return isReorder;
    }
}
# Fixed: Ensure loop variable is updated
def fixed_search(items, target):
    i = 0
    found = False

    while not found and i < len(items):
        if items[i] == target:
            found = True
        i += 1  # Fixed: Always increment i

    return found

# Better: Use built-in functions
def better_search(items, target):
    return target in items
// Fixed: Correct loop condition
void fixed_array_process(int *arr, int size) {
    int i = 0;

    // Fixed: Correct boundary condition
    while (i < size) {  // Changed <= to <
        process(arr[i]);
        if (arr[i] == SENTINEL) {
            break;
        }
        i++;
    }
}
// Fixed: Use epsilon comparison for floating point
function fixedFloatLoop() {
    let value = 0.1;
    let sum = 0;
    const epsilon = 0.0001;
    const maxIterations = 1000;
    let iterations = 0;

    // Fixed: Use approximate comparison and iteration limit
    while (Math.abs(value - 1.0) > epsilon && iterations < maxIterations) {
        sum += value;
        value += 0.1;
        iterations++;
    }

    return sum;
}
// Fixed: Use matching types for comparison
void fixed_countdown(unsigned int count) {
    // Fixed: Use same type as parameter
    unsigned int i = count;

    // Fixed: Check for underflow condition
    while (i > 0) {
        process(i);
        i--;  // Safe: stops when i reaches 0
    }
    process(0);  // Process the zero case if needed
}
// Fixed: State machine with valid transitions to exit
<?php
function fixed_state_machine($input) {
    $state = 'START';
    $iterations = 0;
    $maxIterations = 1000;

    while ($state !== 'END' && $iterations < $maxIterations) {
        switch ($state) {
            case 'START':
                $state = 'PROCESSING';
                break;
            case 'PROCESSING':
                $result = process($input);
                // Fixed: Transition to END state
                $state = ($result === true) ? 'END' : 'ERROR';
                break;
            case 'ERROR':
                handle_error();
                $state = 'END';  // Fixed: Error state leads to END
                break;
        }
        $iterations++;
    }

    if ($iterations >= $maxIterations) {
        throw new Exception('State machine timeout');
    }
}
?>
// Best practice: Watchdog pattern for long-running loops
#include <signal.h>
#include <setjmp.h>

static sigjmp_buf timeout_jump;
static volatile sig_atomic_t timeout_occurred = 0;

void timeout_handler(int sig) {
    timeout_occurred = 1;
    siglongjmp(timeout_jump, 1);
}

int safe_long_operation(void *data) {
    // Set up timeout
    signal(SIGALRM, timeout_handler);
    alarm(30);  // 30 second timeout

    if (sigsetjmp(timeout_jump, 1) != 0) {
        // Timeout occurred
        return -1;
    }

    // Perform operation that might loop excessively
    while (process_more(data)) {
        // Processing...
    }

    // Cancel alarm
    alarm(0);
    return 0;
}

  • CWE-834: Excessive Iteration (parent)
  • CWE-691: Insufficient Control Flow Management (grandparent)
  • CWE-674: Uncontrolled Recursion (sibling)
  • CWE-400: Uncontrolled Resource Consumption (related)

References

  1. MITRE Corporation. "CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop')." https://cwe.mitre.org/data/definitions/835.html
  2. CERT C Secure Coding Standard. "MSC21-C. Use robust loop termination conditions."
  3. OWASP. "Denial of Service." https://owasp.org/www-community/attacks/Denial_of_Service