Undefined Behavior for Input to API

Description

Undefined Behavior for Input to API is a vulnerability where the behavior of a function is undefined unless its control parameter is set to a specific value. This weakness occurs when an API function is called without providing required parameters in their expected state, leading to unpredictable behavior. Many library functions have specific preconditions that must be met for defined behavior, and violating these preconditions results in undefined outcomes that may vary by implementation.

Risk

Calling APIs with inputs that trigger undefined behavior creates unpredictable security conditions. The program may crash, silently produce wrong results, or exhibit exploitable memory corruption. Attackers can potentially leverage undefined behavior to achieve arbitrary code execution, bypass security checks, or cause denial of service. The risk is compounded because undefined behavior is not required to be consistent - it may work during testing but fail in production, or be exploitable on some platforms but not others.

Solution

Review API documentation carefully to understand all preconditions and requirements. Validate all inputs before passing them to APIs with strict requirements. Use wrapper functions that enforce preconditions and provide defined error handling. Prefer APIs with well-defined behavior for all possible inputs. When preconditions cannot be guaranteed, implement runtime checks that detect violations before calling the underlying API. Use static analysis tools to detect potential undefined behavior invocations.

Common Consequences

ImpactDetails
OtherScope: Other

Quality Degradation - Code may behave unpredictably when APIs are called with inputs that trigger undefined behavior.
OtherScope: Other

Varies by Context - Security impact depends on how undefined behavior manifests, potentially including crashes, data corruption, or exploitable conditions.

Example Code

Vulnerable Code

// Vulnerable: Calling functions with undefined behavior for certain inputs
#include <stdlib.h>
#include <string.h>
#include <math.h>

void vulnerable_abs_usage(int value) {
    // Vulnerable: abs(INT_MIN) is undefined behavior on two's complement
    // INT_MIN cannot be negated in two's complement representation
    int positive = abs(value);  // UB if value == INT_MIN

    printf("Absolute value: %d\n", positive);
}

void vulnerable_memcpy_overlap(char *buffer, size_t offset) {
    // Vulnerable: memcpy with overlapping regions is undefined
    char *src = buffer;
    char *dest = buffer + offset;

    // If src and dest overlap, behavior is undefined
    memcpy(dest, src, 100);  // UB if offset < 100
}

void vulnerable_shift_operations(int value, int shift_amount) {
    // Vulnerable: Shifting by negative amount or >= bit width is UB
    int result = value << shift_amount;  // UB if shift_amount >= 32 or < 0

    // Vulnerable: Shifting a negative number left is UB (pre-C++20)
    int negative_shift = (-1) << 2;  // UB in C and older C++
}

void vulnerable_division(int numerator, int denominator) {
    // Vulnerable: Division by zero is undefined
    int result = numerator / denominator;  // UB if denominator == 0

    // Vulnerable: INT_MIN / -1 overflows (undefined)
    if (numerator == INT_MIN && denominator == -1) {
        // Still UB even after the check because result computed first
    }
}

// Linux Standard Base specific example
void vulnerable_lsb_call() {
    // Vulnerable: __xmknod requires version parameter == 1
    // Calling with other values has undefined behavior
    __xmknod(0, path, mode, dev);  // UB: version should be 1

    // Vulnerable: Wide character functions require specific group argument
    // Some functions require group argument == 2
    __wcscpy_chk(dest, src, 0);  // UB: should be specific value
}
# Vulnerable: Python APIs with undefined behavior for edge cases
import re
import math

def vulnerable_regex_usage(pattern):
    # Vulnerable: re.compile with invalid pattern
    # Behavior varies: may raise, return None, or other
    try:
        compiled = re.compile(pattern)
    except re.error:
        pass
    # Using compiled outside try block: may be undefined

def vulnerable_math_operations(value):
    # Vulnerable: math.sqrt with negative number
    # Defined to raise ValueError, but...
    result = math.sqrt(value)  # Raises if negative

    # Vulnerable: math.log with zero or negative
    log_result = math.log(value)  # Domain error

    return result, log_result

def vulnerable_list_operations(lst, index):
    # Vulnerable: Accessing index that may not exist
    # Python raises IndexError, but catching and continuing
    # may leave program in inconsistent state
    try:
        value = lst[index]
    except IndexError:
        value = None  # Now depends on whether index was valid

    # Later code assumes value has expected type
    return value.upper()  # Error if None
// Vulnerable: Java APIs with undefined/unspecified behavior
import java.util.concurrent.atomic.*;
import java.util.*;

public class VulnerableAPIUsage {

    public void vulnerableComparator() {
        List<Integer> list = Arrays.asList(3, 1, 2, null);

        // Vulnerable: Sort with null elements
        // Comparator.naturalOrder() doesn't handle null
        Collections.sort(list);  // NullPointerException or undefined order
    }

    public void vulnerableHashCode() {
        Map<MutableKey, String> map = new HashMap<>();
        MutableKey key = new MutableKey("original");

        map.put(key, "value");

        // Vulnerable: Mutating key after insertion
        key.setValue("modified");

        // Map is now in undefined state
        // get(), contains(), remove() may all fail
        String value = map.get(key);  // May return null or "value"
    }

    public void vulnerableConcurrentModification() {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");

        // Vulnerable: Modifying list during iteration
        for (String s : list) {
            if (s.equals("b")) {
                list.remove(s);  // ConcurrentModificationException
                // Or undefined behavior in some cases
            }
        }
    }
}

Fixed Code

// Fixed: Validate inputs before calling APIs
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>

int secure_abs(int value) {
    // Fixed: Handle undefined case explicitly
    if (value == INT_MIN) {
        // INT_MIN cannot be negated
        // Option 1: Return INT_MAX (saturation)
        return INT_MAX;
        // Option 2: Return error
        // errno = ERANGE; return -1;
    }
    return abs(value);
}

void secure_memcpy(void *dest, const void *src, size_t count) {
    // Fixed: Use memmove for potentially overlapping regions
    // memmove handles overlap correctly
    memmove(dest, src, count);

    // Or detect overlap and choose appropriate function
}

int secure_shift(int value, int shift_amount) {
    // Fixed: Validate shift amount
    if (shift_amount < 0 || shift_amount >= (int)(sizeof(int) * 8)) {
        return 0;  // Or handle error
    }

    // Fixed: Only shift non-negative values left
    if (value < 0) {
        // Handle negative values explicitly
        unsigned int uval = (unsigned int)value;
        return (int)(uval << shift_amount);
    }

    return value << shift_amount;
}

int secure_division(int numerator, int denominator, int *result) {
    // Fixed: Check for all undefined cases
    if (denominator == 0) {
        return -1;  // Error: division by zero
    }

    if (numerator == INT_MIN && denominator == -1) {
        return -2;  // Error: overflow
    }

    *result = numerator / denominator;
    return 0;  // Success
}

// Fixed: Wrapper for platform-specific functions
int secure_mknod(const char *path, mode_t mode, dev_t dev) {
    // Fixed: Use correct version parameter as required by spec
    return __xmknod(1, path, mode, &dev);  // Version = 1
}
# Fixed: Validate inputs and handle edge cases
import re
import math

def secure_regex_compile(pattern):
    """Compile regex with proper error handling."""
    if not isinstance(pattern, str):
        raise TypeError("Pattern must be a string")

    if not pattern:
        raise ValueError("Pattern cannot be empty")

    try:
        return re.compile(pattern)
    except re.error as e:
        raise ValueError(f"Invalid regex pattern: {e}")

def secure_math_operations(value):
    """Perform math operations with input validation."""
    # Fixed: Validate before calling
    if not isinstance(value, (int, float)):
        raise TypeError("Value must be numeric")

    if value < 0:
        raise ValueError("Cannot compute sqrt/log of negative number")

    if value == 0:
        raise ValueError("Cannot compute log of zero")

    return math.sqrt(value), math.log(value)

def secure_list_access(lst, index, default=None):
    """Safely access list with default value."""
    # Fixed: Validate index
    if not isinstance(index, int):
        raise TypeError("Index must be integer")

    # Fixed: Use bounds checking
    if 0 <= index < len(lst):
        value = lst[index]
    elif -len(lst) <= index < 0:
        value = lst[index]
    else:
        return default

    # Fixed: Validate value before using
    if value is None:
        return default

    return value.upper() if isinstance(value, str) else str(value).upper()
// Fixed: Safe API usage patterns
import java.util.*;

public class SecureAPIUsage {

    public void secureSort(List<Integer> list) {
        // Fixed: Remove or handle nulls before sorting
        list.removeIf(Objects::isNull);

        // Or use null-safe comparator
        Collections.sort(list, Comparator.nullsLast(Comparator.naturalOrder()));
    }

    // Fixed: Use immutable keys for maps
    public void secureMapUsage() {
        Map<String, String> map = new HashMap<>();

        // Fixed: String keys are immutable
        String key = "original";
        map.put(key, "value");

        // Key cannot be mutated, map remains consistent
        String value = map.get(key);  // Always returns "value"
    }

    public void secureIteration(List<String> list) {
        // Fixed: Create copy for iteration when modification needed
        List<String> copy = new ArrayList<>(list);

        for (String s : copy) {
            if (s.equals("b")) {
                list.remove(s);  // Safe: iterating over copy
            }
        }

        // Or use Iterator.remove()
        Iterator<String> iter = list.iterator();
        while (iter.hasNext()) {
            if (iter.next().equals("b")) {
                iter.remove();  // Safe: uses iterator's remove
            }
        }

        // Or use removeIf
        list.removeIf(s -> s.equals("b"));
    }
}

CVE Examples

No specific CVEs are listed in the MITRE database for this CWE. However, undefined behavior vulnerabilities are documented in:

  • Linux Standard Base Specification examples
  • Compiler-specific undefined behavior security advisories

References

  1. MITRE Corporation. "CWE-475: Undefined Behavior for Input to API." https://cwe.mitre.org/data/definitions/475.html
  2. CERT C Secure Coding Standard. "MSC15-C. Do not depend on undefined behavior."
  3. Linux Standard Base Specification.