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
| Impact | Details |
|---|---|
| Other | Scope: Other Quality Degradation - Code may behave unpredictably when APIs are called with inputs that trigger undefined behavior. |
| Other | Scope: 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
- MITRE Corporation. "CWE-475: Undefined Behavior for Input to API." https://cwe.mitre.org/data/definitions/475.html
- CERT C Secure Coding Standard. "MSC15-C. Do not depend on undefined behavior."
- Linux Standard Base Specification.