Improper Validation of Consistency within Input
Description
Improper Validation of Consistency within Input occurs when a product receives inputs with multiple elements or fields that should align with each other but fails to validate or incorrectly validates this consistency. Complex structured inputs often contain interdependent fields—such as a count field followed by the specified number of items, or a size field that must match actual data length. When these inputs are inconsistent, attackers can trigger unexpected errors, cause incorrect actions, or exploit dormant vulnerabilities.
Risk
Inconsistent input validation has severe security implications. Buffer overflows from size mismatches. Data corruption from count errors. Denial of service possible. Logic errors may occur. Memory corruption attacks enabled. Security bypasses possible. Parsing vulnerabilities exposed. Application crashes likely.
Solution
Implement allowlist-based validation requiring strict conformance to specifications. Validate all relevant properties: length, type, value ranges, syntax, and inter-field consistency. Reject inputs failing validation rather than attempting repairs. Verify that count fields match actual item counts. Ensure size fields match actual data sizes. Cross-validate related fields.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Impact varies depending on how inconsistent input is processed. |
| Integrity | Scope: Integrity Inconsistent data can corrupt application state. |
| Availability | Scope: Availability Mismatched counts/sizes can cause crashes. |
Example Code
Vulnerable Code
// Vulnerable: No consistency validation between fields
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
uint32_t count;
uint32_t items[100]; // Fixed max, but 'count' could be larger
} vulnerable_packet_t;
// VULNERABLE: Count not validated against actual items
void vulnerable_process_packet(const uint8_t* data, size_t data_len) {
// VULNERABLE: Read count without validation
uint32_t count;
memcpy(&count, data, sizeof(count));
// VULNERABLE: No check that count matches available data
// If count > (data_len - 4) / 4, we'll read past buffer
for (uint32_t i = 0; i < count; i++) {
uint32_t item;
memcpy(&item, data + 4 + i * 4, sizeof(item));
process_item(item); // Buffer over-read if count is too large
}
}
// VULNERABLE: Size field not validated against packet size
typedef struct {
uint32_t total_size;
uint32_t header_size;
uint32_t data_size;
uint8_t header[256];
uint8_t data[1024];
} message_t;
void vulnerable_process_message(const message_t* msg, size_t actual_size) {
// VULNERABLE: No validation of size fields
// total_size could be inconsistent with actual_size
// header_size + data_size could != total_size
// VULNERABLE: Use provided sizes without validation
process_header(msg->header, msg->header_size);
process_data(msg->data, msg->data_size);
}
// VULNERABLE: Start/end block inconsistency
typedef struct {
uint32_t start_block;
uint32_t end_block;
uint8_t block_data[4096];
} block_range_t;
void vulnerable_process_blocks(const block_range_t* range) {
// VULNERABLE: No check that start < end
// If start > end, the loop might underflow or skip incorrectly
for (uint32_t i = range->start_block; i <= range->end_block; i++) {
// If start=100, end=50, this never executes (or wraps around)
process_block(i, range->block_data);
}
}
# Vulnerable: Python with inconsistent field handling
import struct
# VULNERABLE: Length field not validated
def vulnerable_parse_tlv(data):
"""Parse Type-Length-Value structure."""
offset = 0
results = []
while offset < len(data):
# Read type and length
tlv_type = data[offset]
length = struct.unpack('>H', data[offset+1:offset+3])[0]
# VULNERABLE: No check that length is consistent with remaining data
value = data[offset+3:offset+3+length]
# If length > remaining data, value is truncated silently
results.append({'type': tlv_type, 'length': length, 'value': value})
offset += 3 + length
return results
# VULNERABLE: Array count not validated
def vulnerable_process_items(data):
"""Process items with count prefix."""
count = struct.unpack('>I', data[:4])[0]
# VULNERABLE: No validation that data contains 'count' items
items = []
offset = 4
for i in range(count):
# Will raise IndexError if not enough data
# Or may read partial/wrong data
item_size = struct.unpack('>I', data[offset:offset+4])[0]
item_data = data[offset+4:offset+4+item_size]
items.append(item_data)
offset += 4 + item_size
return items
# VULNERABLE: Checksum not validated
def vulnerable_process_packet(packet):
"""Process packet with checksum."""
data = packet['data']
checksum = packet['checksum']
# VULNERABLE: Checksum not verified
# Attacker can modify data and checksum independently
process_data(data) # Process potentially corrupted data
// Vulnerable: Java with inconsistent input handling
public class VulnerableParser {
// VULNERABLE: Declared length not validated against actual
public void processChunk(byte[] data) {
int offset = 0;
while (offset < data.length) {
// Read declared chunk size
int chunkSize = ByteBuffer.wrap(data, offset, 4).getInt();
offset += 4;
// VULNERABLE: No validation that chunkSize bytes are available
byte[] chunk = new byte[chunkSize];
// ArrayIndexOutOfBoundsException or reads wrong data
System.arraycopy(data, offset, chunk, 0, chunkSize);
offset += chunkSize;
processChunkData(chunk);
}
}
// VULNERABLE: Count field inconsistency
public List<String> parseStringList(DataInputStream input) throws IOException {
int count = input.readInt(); // Declared count
List<String> result = new ArrayList<>();
// VULNERABLE: Trust the count field
for (int i = 0; i < count; i++) {
int length = input.readInt();
byte[] bytes = new byte[length];
// VULNERABLE: No validation of length against available data
input.readFully(bytes);
result.add(new String(bytes, StandardCharsets.UTF_8));
}
return result;
}
}
Fixed Code
// Fixed: Proper consistency validation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// FIXED: Validate count against available data
bool secure_process_packet(const uint8_t* data, size_t data_len) {
// FIXED: Minimum size check
if (data_len < sizeof(uint32_t)) {
log_error("Packet too small for count field");
return false;
}
// Read count
uint32_t count;
memcpy(&count, data, sizeof(count));
// FIXED: Validate count against actual data
size_t expected_size = sizeof(uint32_t) + count * sizeof(uint32_t);
if (expected_size > data_len) {
log_error("Count %u exceeds available data (have %zu, need %zu)",
count, data_len, expected_size);
return false;
}
// FIXED: Also check for count overflow
if (count > (SIZE_MAX - sizeof(uint32_t)) / sizeof(uint32_t)) {
log_error("Count overflow");
return false;
}
// Now safe to process
for (uint32_t i = 0; i < count; i++) {
uint32_t item;
memcpy(&item, data + 4 + i * 4, sizeof(item));
process_item(item);
}
return true;
}
// FIXED: Validate all size fields for consistency
typedef struct {
uint32_t total_size;
uint32_t header_size;
uint32_t data_size;
uint8_t header[256];
uint8_t data[1024];
} message_t;
bool secure_process_message(const message_t* msg, size_t actual_size) {
// FIXED: Validate actual_size is at least minimum
if (actual_size < offsetof(message_t, header)) {
log_error("Message too small");
return false;
}
// FIXED: Validate total_size matches actual_size
if (msg->total_size != actual_size) {
log_error("Total size mismatch: declared %u, actual %zu",
msg->total_size, actual_size);
return false;
}
// FIXED: Validate header_size
if (msg->header_size > sizeof(msg->header)) {
log_error("Header size exceeds buffer");
return false;
}
// FIXED: Validate data_size
if (msg->data_size > sizeof(msg->data)) {
log_error("Data size exceeds buffer");
return false;
}
// FIXED: Validate sizes are consistent
size_t computed_size = offsetof(message_t, header) +
msg->header_size + msg->data_size;
if (computed_size != msg->total_size) {
log_error("Size fields inconsistent: header=%u, data=%u, total=%u",
msg->header_size, msg->data_size, msg->total_size);
return false;
}
// Now safe to process
process_header(msg->header, msg->header_size);
process_data(msg->data, msg->data_size);
return true;
}
// FIXED: Validate start/end consistency
typedef struct {
uint32_t start_block;
uint32_t end_block;
uint8_t block_data[4096];
} block_range_t;
bool secure_process_blocks(const block_range_t* range) {
// FIXED: Validate start <= end
if (range->start_block > range->end_block) {
log_error("Invalid block range: start %u > end %u",
range->start_block, range->end_block);
return false;
}
// FIXED: Validate range is reasonable
uint32_t count = range->end_block - range->start_block + 1;
if (count > MAX_BLOCKS) {
log_error("Block range too large: %u blocks", count);
return false;
}
for (uint32_t i = range->start_block; i <= range->end_block; i++) {
process_block(i, range->block_data);
}
return true;
}
# Fixed: Python with consistency validation
import struct
from typing import List, Dict, Any, Optional
# FIXED: TLV parsing with length validation
def secure_parse_tlv(data: bytes) -> List[Dict[str, Any]]:
"""Parse Type-Length-Value structure with validation."""
offset = 0
results = []
while offset < len(data):
# FIXED: Check minimum header size
if offset + 3 > len(data):
raise ValueError(f"Incomplete TLV header at offset {offset}")
# Read type and length
tlv_type = data[offset]
length = struct.unpack('>H', data[offset+1:offset+3])[0]
# FIXED: Validate length against remaining data
if offset + 3 + length > len(data):
raise ValueError(
f"TLV length {length} exceeds remaining data "
f"(offset={offset}, data_len={len(data)})"
)
value = data[offset+3:offset+3+length]
# FIXED: Verify we got the expected amount
if len(value) != length:
raise ValueError(f"TLV value truncated: expected {length}, got {len(value)}")
results.append({'type': tlv_type, 'length': length, 'value': value})
offset += 3 + length
return results
# FIXED: Array count validation
def secure_process_items(data: bytes, max_count: int = 1000) -> List[bytes]:
"""Process items with validated count prefix."""
if len(data) < 4:
raise ValueError("Data too short for count field")
count = struct.unpack('>I', data[:4])[0]
# FIXED: Validate count is reasonable
if count > max_count:
raise ValueError(f"Count {count} exceeds maximum {max_count}")
items = []
offset = 4
for i in range(count):
# FIXED: Check for item header
if offset + 4 > len(data):
raise ValueError(f"Incomplete item header at index {i}")
item_size = struct.unpack('>I', data[offset:offset+4])[0]
# FIXED: Validate item_size against remaining data
if offset + 4 + item_size > len(data):
raise ValueError(
f"Item {i} size {item_size} exceeds remaining data"
)
item_data = data[offset+4:offset+4+item_size]
items.append(item_data)
offset += 4 + item_size
# FIXED: Verify we consumed all data (no trailing garbage)
if offset != len(data):
raise ValueError(f"Unexpected trailing data: {len(data) - offset} bytes")
return items
# FIXED: Checksum validation
import hashlib
def secure_process_packet(packet: Dict[str, Any]) -> bool:
"""Process packet with checksum validation."""
if 'data' not in packet or 'checksum' not in packet:
raise ValueError("Missing required fields")
data = packet['data']
expected_checksum = packet['checksum']
# FIXED: Verify checksum before processing
actual_checksum = hashlib.sha256(data).hexdigest()
if actual_checksum != expected_checksum:
raise ValueError(
f"Checksum mismatch: expected {expected_checksum}, "
f"computed {actual_checksum}"
)
# Now safe to process verified data
process_data(data)
return True
// Fixed: Java with consistency validation
public class SecureParser {
private static final int MAX_CHUNK_SIZE = 1024 * 1024; // 1 MB
// FIXED: Validate declared length against actual
public void processChunk(byte[] data) throws ValidationException {
int offset = 0;
while (offset < data.length) {
// FIXED: Check header fits
if (offset + 4 > data.length) {
throw new ValidationException(
"Incomplete chunk header at offset " + offset);
}
// Read declared chunk size
int chunkSize = ByteBuffer.wrap(data, offset, 4).getInt();
offset += 4;
// FIXED: Validate chunk size
if (chunkSize < 0) {
throw new ValidationException("Negative chunk size: " + chunkSize);
}
if (chunkSize > MAX_CHUNK_SIZE) {
throw new ValidationException(
"Chunk size " + chunkSize + " exceeds maximum " + MAX_CHUNK_SIZE);
}
// FIXED: Validate chunk data available
if (offset + chunkSize > data.length) {
throw new ValidationException(
"Chunk size " + chunkSize + " exceeds remaining data " +
(data.length - offset));
}
byte[] chunk = new byte[chunkSize];
System.arraycopy(data, offset, chunk, 0, chunkSize);
offset += chunkSize;
processChunkData(chunk);
}
}
// FIXED: Count field validation
public List<String> parseStringList(DataInputStream input, int maxCount)
throws IOException, ValidationException {
int count = input.readInt();
// FIXED: Validate count
if (count < 0) {
throw new ValidationException("Negative count: " + count);
}
if (count > maxCount) {
throw new ValidationException(
"Count " + count + " exceeds maximum " + maxCount);
}
List<String> result = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
// FIXED: Validate string length
int length = input.readInt();
if (length < 0) {
throw new ValidationException(
"Negative string length at index " + i);
}
if (length > MAX_STRING_LENGTH) {
throw new ValidationException(
"String length " + length + " exceeds maximum at index " + i);
}
byte[] bytes = new byte[length];
// FIXED: Verify we read expected amount
int read = input.read(bytes);
if (read != length) {
throw new ValidationException(
"Expected " + length + " bytes, got " + read);
}
result.add(new String(bytes, StandardCharsets.UTF_8));
}
return result;
}
}
CVE Examples
- CVE-2018-16733: Product does not validate that the start block appears before the end block, causing processing errors.
- CVE-2006-3790: Size field that is inconsistent with packet size leads to buffer over-read.
- CVE-2008-4114: System crash with offset value that is inconsistent with packet size.
Related CWEs
- CWE-20: Improper Input Validation (parent)
- CWE-1215: Data Validation Issues (category)
- CWE-130: Improper Handling of Length Parameter Inconsistency (related)
- CWE-805: Buffer Access with Incorrect Length Value (related)
References
- MITRE Corporation. "CWE-1288: Improper Validation of Consistency within Input." https://cwe.mitre.org/data/definitions/1288.html
- OWASP. "Input Validation Cheat Sheet"
- CERT. "Secure Coding Guidelines"