Improper Validation of Specified Quantity in Input

Description

Improper Validation of Specified Quantity in Input occurs when a product receives input specifying a quantity (size, length, frequency, price, rate, number of operations, time, etc.) but fails to validate or incorrectly validates that the quantity has required properties. Quantities are commonly used to allocate resources, perform calculations, and control iteration. Code often depends on proper validation of these inputs for secure operation. Without proper validation, attackers can cause resource exhaustion, buffer overflows, integer overflows, or business logic violations.

Risk

Improper quantity validation has severe security implications. Buffer overflows may occur. Integer overflow attacks possible. Memory exhaustion can happen. Resource consumption attacks enabled. Business logic can be abused. Infinite loops may be triggered. Financial calculations can be manipulated. System availability can be compromised.

Solution

Implement "accept known good" validation using strict allowlists. Validate all relevant properties including length, type, acceptable ranges, and syntax. Check for business logic conformance. Avoid relying solely on malicious input detection. Validate both minimum and maximum values. Consider signed vs unsigned issues. Implement range checks before using quantities.

Common Consequences

ImpactDetails
AvailabilityScope: Availability

DoS - Excessive resource consumption or infinite loops.
IntegrityScope: Integrity

Modify Memory - Buffer overflows from invalid sizes.
OtherScope: Other

Business logic violations from invalid quantities.

Example Code

Vulnerable Code

// Vulnerable: No validation of quantity inputs

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

// VULNERABLE: No validation of size parameter
void* vulnerable_allocate(size_t size) {
    // VULNERABLE: No check for excessive size
    // Attacker can request huge allocation

    void* buffer = malloc(size);  // Could exhaust memory
    return buffer;
}

// VULNERABLE: No validation of count
void vulnerable_process_items(int count) {
    // VULNERABLE: No upper bound check
    // VULNERABLE: No negative check

    for (int i = 0; i < count; i++) {
        // If count is very large, runs forever
        // If count is negative (when compared unsigned), unexpected behavior
        process_item(i);
    }
}

// VULNERABLE: Integer overflow in size calculation
void vulnerable_copy_data(const uint8_t* src, size_t element_size,
                          size_t element_count) {
    // VULNERABLE: Multiplication can overflow
    size_t total_size = element_size * element_count;

    // If element_size=0x10000 and element_count=0x10000:
    // total_size = 0x100000000 = 0 (32-bit overflow)

    uint8_t* buffer = malloc(total_size);  // Allocates 0 or small buffer
    memcpy(buffer, src, total_size);       // But this uses the overflowed size?

    // Actually memcpy also uses total_size, but if we had:
    // memcpy(buffer, src, element_size * element_count);
    // The copy would overflow
}

// VULNERABLE: Negative quantity in e-commerce
struct order {
    int item_id;
    int quantity;
    double price;
};

double vulnerable_calculate_total(struct order* orders, int num_orders) {
    double total = 0;

    for (int i = 0; i < num_orders; i++) {
        // VULNERABLE: No validation of quantity
        total += orders[i].quantity * orders[i].price;

        // If quantity is negative, total decreases!
        // Attacker orders -100 items and gets money back
    }

    return total;
}

// VULNERABLE: Array index from quantity
void vulnerable_array_access(uint8_t* data, int offset, int length) {
    // VULNERABLE: No bounds checking

    for (int i = 0; i < length; i++) {
        // If offset or length is negative or too large, out-of-bounds access
        process_byte(data[offset + i]);
    }
}
// Vulnerable: Java with unvalidated quantities

public class VulnerableShoppingCart {

    // VULNERABLE: No quantity validation
    public double calculateTotal(List<OrderItem> items) {
        double total = 0;

        for (OrderItem item : items) {
            // VULNERABLE: Quantity not validated
            // Negative quantities credit the customer!
            total += item.getQuantity() * item.getPrice();
        }

        return total;
    }

    // VULNERABLE: No validation of requested array size
    public byte[] createBuffer(int requestedSize) {
        // VULNERABLE: No upper limit check
        // OutOfMemoryError possible

        return new byte[requestedSize];
    }

    // VULNERABLE: Loop count from user input
    public void processRequests(int count) {
        // VULNERABLE: No validation of count
        // Could be negative or extremely large

        for (int i = 0; i < count; i++) {
            processRequest(i);  // DoS if count is huge
        }
    }
}
# Vulnerable: Python with unvalidated quantities

def vulnerable_allocate_list(size):
    # VULNERABLE: No validation of size
    # Can exhaust memory

    return [None] * size  # Creates list with 'size' elements

def vulnerable_repeat_operation(count):
    # VULNERABLE: No validation of count

    for i in range(count):
        # If count is negative in Python 2, range() returns empty
        # If count is huge, runs for very long time
        do_operation(i)

def vulnerable_calculate_price(quantity, unit_price):
    # VULNERABLE: No validation of quantity
    # Negative quantity = negative total = refund!

    return quantity * unit_price

def vulnerable_slice_data(data, offset, length):
    # VULNERABLE: No bounds validation

    return data[offset:offset + length]
    # If offset is negative, slices from end
    # If length is negative, unexpected results

Fixed Code

// Fixed: Proper validation of quantity inputs

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <limits.h>

// FIXED: Validate size parameter
void* secure_allocate(size_t size, size_t max_allowed) {
    // FIXED: Check against maximum allowed
    if (size == 0) {
        return NULL;  // Invalid request
    }

    if (size > max_allowed) {
        log_error("Allocation size %zu exceeds maximum %zu", size, max_allowed);
        return NULL;
    }

    void* buffer = malloc(size);
    if (buffer == NULL) {
        log_error("Allocation failed for size %zu", size);
    }

    return buffer;
}

// FIXED: Validate count with bounds
bool secure_process_items(int count, int max_count) {
    // FIXED: Validate range
    if (count < 0) {
        log_error("Negative count: %d", count);
        return false;
    }

    if (count > max_count) {
        log_error("Count %d exceeds maximum %d", count, max_count);
        return false;
    }

    for (int i = 0; i < count; i++) {
        if (!process_item(i)) {
            return false;
        }
    }

    return true;
}

// FIXED: Detect integer overflow in size calculation
bool secure_copy_data(const uint8_t* src, size_t src_size,
                      size_t element_size, size_t element_count,
                      uint8_t** out_buffer, size_t* out_size) {
    // FIXED: Check for multiplication overflow
    if (element_count != 0 && element_size > SIZE_MAX / element_count) {
        log_error("Size overflow: %zu * %zu", element_size, element_count);
        return false;
    }

    size_t total_size = element_size * element_count;

    // FIXED: Validate against source size
    if (total_size > src_size) {
        log_error("Requested size %zu exceeds source %zu", total_size, src_size);
        return false;
    }

    uint8_t* buffer = secure_allocate(total_size, MAX_BUFFER_SIZE);
    if (buffer == NULL) {
        return false;
    }

    memcpy(buffer, src, total_size);

    *out_buffer = buffer;
    *out_size = total_size;
    return true;
}

// FIXED: Validate quantity in e-commerce
typedef struct {
    int item_id;
    int quantity;
    double price;
} order_item_t;

bool secure_calculate_total(const order_item_t* orders, int num_orders,
                            double* total_out) {
    if (orders == NULL || total_out == NULL) {
        return false;
    }

    // FIXED: Validate num_orders
    if (num_orders < 0 || num_orders > MAX_ORDER_ITEMS) {
        log_error("Invalid number of orders: %d", num_orders);
        return false;
    }

    double total = 0;

    for (int i = 0; i < num_orders; i++) {
        // FIXED: Validate quantity is positive
        if (orders[i].quantity <= 0) {
            log_error("Invalid quantity %d for item %d",
                     orders[i].quantity, orders[i].item_id);
            return false;
        }

        // FIXED: Validate quantity is reasonable
        if (orders[i].quantity > MAX_ITEM_QUANTITY) {
            log_error("Quantity %d exceeds maximum for item %d",
                     orders[i].quantity, orders[i].item_id);
            return false;
        }

        // FIXED: Validate price is positive
        if (orders[i].price <= 0) {
            log_error("Invalid price for item %d", orders[i].item_id);
            return false;
        }

        total += orders[i].quantity * orders[i].price;
    }

    *total_out = total;
    return true;
}

// FIXED: Safe array access with bounds checking
bool secure_process_range(const uint8_t* data, size_t data_size,
                          size_t offset, size_t length) {
    // FIXED: Validate offset
    if (offset >= data_size) {
        log_error("Offset %zu out of bounds (size=%zu)", offset, data_size);
        return false;
    }

    // FIXED: Validate length
    if (length == 0) {
        return true;  // Nothing to process
    }

    // FIXED: Check offset + length for overflow
    if (length > data_size - offset) {
        log_error("Length %zu exceeds remaining data at offset %zu",
                 length, offset);
        return false;
    }

    for (size_t i = 0; i < length; i++) {
        process_byte(data[offset + i]);
    }

    return true;
}
// Fixed: Java with validated quantities

public class SecureShoppingCart {

    private static final int MAX_QUANTITY = 10000;
    private static final int MAX_BUFFER_SIZE = 10 * 1024 * 1024;  // 10 MB
    private static final int MAX_ITERATIONS = 1000000;

    // FIXED: Validate quantity
    public double calculateTotal(List<OrderItem> items) throws ValidationException {
        double total = 0;

        for (OrderItem item : items) {
            int quantity = item.getQuantity();

            // FIXED: Validate quantity
            if (quantity <= 0) {
                throw new ValidationException(
                    "Invalid quantity: " + quantity + " for item " + item.getId());
            }

            if (quantity > MAX_QUANTITY) {
                throw new ValidationException(
                    "Quantity " + quantity + " exceeds maximum " + MAX_QUANTITY);
            }

            double price = item.getPrice();

            // FIXED: Validate price
            if (price <= 0) {
                throw new ValidationException(
                    "Invalid price for item " + item.getId());
            }

            total += quantity * price;
        }

        return total;
    }

    // FIXED: Validate buffer size
    public byte[] createBuffer(int requestedSize) throws ValidationException {
        // FIXED: Validate range
        if (requestedSize <= 0) {
            throw new ValidationException("Invalid buffer size: " + requestedSize);
        }

        if (requestedSize > MAX_BUFFER_SIZE) {
            throw new ValidationException(
                "Buffer size " + requestedSize + " exceeds maximum " + MAX_BUFFER_SIZE);
        }

        return new byte[requestedSize];
    }

    // FIXED: Validate loop count
    public void processRequests(int count) throws ValidationException {
        // FIXED: Validate count
        if (count < 0) {
            throw new ValidationException("Negative count: " + count);
        }

        if (count > MAX_ITERATIONS) {
            throw new ValidationException(
                "Count " + count + " exceeds maximum " + MAX_ITERATIONS);
        }

        for (int i = 0; i < count; i++) {
            processRequest(i);
        }
    }
}
# Fixed: Python with validated quantities

MAX_LIST_SIZE = 10000000
MAX_ITERATIONS = 1000000
MAX_QUANTITY = 10000

def secure_allocate_list(size):
    # FIXED: Validate size
    if not isinstance(size, int):
        raise TypeError(f"Size must be integer, got {type(size)}")

    if size < 0:
        raise ValueError(f"Size cannot be negative: {size}")

    if size > MAX_LIST_SIZE:
        raise ValueError(f"Size {size} exceeds maximum {MAX_LIST_SIZE}")

    return [None] * size

def secure_repeat_operation(count):
    # FIXED: Validate count
    if not isinstance(count, int):
        raise TypeError(f"Count must be integer, got {type(count)}")

    if count < 0:
        raise ValueError(f"Count cannot be negative: {count}")

    if count > MAX_ITERATIONS:
        raise ValueError(f"Count {count} exceeds maximum {MAX_ITERATIONS}")

    for i in range(count):
        do_operation(i)

def secure_calculate_price(quantity, unit_price):
    # FIXED: Validate quantity
    if not isinstance(quantity, (int, float)):
        raise TypeError(f"Quantity must be numeric, got {type(quantity)}")

    if quantity <= 0:
        raise ValueError(f"Quantity must be positive: {quantity}")

    if quantity > MAX_QUANTITY:
        raise ValueError(f"Quantity {quantity} exceeds maximum {MAX_QUANTITY}")

    # FIXED: Validate price
    if not isinstance(unit_price, (int, float)):
        raise TypeError(f"Price must be numeric, got {type(unit_price)}")

    if unit_price <= 0:
        raise ValueError(f"Price must be positive: {unit_price}")

    return quantity * unit_price

def secure_slice_data(data, offset, length):
    # FIXED: Validate parameters
    if not isinstance(offset, int) or not isinstance(length, int):
        raise TypeError("Offset and length must be integers")

    if offset < 0:
        raise ValueError(f"Offset cannot be negative: {offset}")

    if length < 0:
        raise ValueError(f"Length cannot be negative: {length}")

    if offset >= len(data):
        raise ValueError(f"Offset {offset} out of bounds (size={len(data)})")

    if offset + length > len(data):
        raise ValueError(f"Offset {offset} + length {length} exceeds data size")

    return data[offset:offset + length]

CVE Examples

  • CVE-2025-46687: Length check failure leading to integer overflow and heap buffer overflow.
  • CVE-2019-19911: Unvalidated image band specifications causing memory exhaustion.
  • CVE-2008-1440: Missing length field validation causing infinite loops.
  • CVE-2008-2374: String length field validation gaps enabling memory attacks.

  • CWE-20: Improper Input Validation (parent)
  • CWE-606: Unchecked Input for Loop Condition (child)
  • CWE-789: Memory Allocation with Excessive Size Value (can precede)
  • CWE-190: Integer Overflow or Wraparound (related)

References

  1. MITRE Corporation. "CWE-1284: Improper Validation of Specified Quantity in Input." https://cwe.mitre.org/data/definitions/1284.html
  2. OWASP. "Input Validation Cheat Sheet"
  3. CERT. "Secure Coding Standards"