Improper Validation of Specified Index, Position, or Offset in Input

Description

Improper Validation of Specified Index, Position, or Offset in Input occurs when a product fails to properly validate indices, positions, or offsets provided in user input before using them to access indexable resources like buffers or files. When untrusted input specifies an index without validation, attackers may access unauthorized resource portions, potentially causing buffer overflows, buffer over-reads, excessive resource allocation, or trigger unexpected failures.

Risk

Unvalidated index inputs have severe security implications. Buffer overflows may occur. Out-of-bounds reads possible. Information disclosure may happen. Memory corruption can occur. Denial of service attacks enabled. Arbitrary code execution possible. Sensitive data may be accessed. System crashes may occur.

Solution

Implement "accept known good" input validation strategy, requiring strict conformance to specifications and rejection of non-compliant inputs. Validation should consider acceptable value ranges, bounds against actual data size, and both signed and unsigned considerations. Always check indices against actual array/buffer bounds before use.

Common Consequences

ImpactDetails
OtherScope: Other

Impact varies by context - from crashes to data leakage.
ConfidentialityScope: Confidentiality

Read Memory - Out-of-bounds read can leak data.
IntegrityScope: Integrity

Modify Memory - Out-of-bounds write can corrupt data.
AvailabilityScope: Availability

DoS - Invalid indices can crash application.

Example Code

Vulnerable Code

// Vulnerable: No validation of array index

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

#define MAX_MESSAGES 100

typedef struct {
    char subject[64];
    char body[1024];
    size_t size;
} message_t;

message_t messages[MAX_MESSAGES];
int message_count = 0;

// VULNERABLE: POP3 server - no bounds check on message number
size_t vulnerable_get_message_size(int msg_num) {
    // VULNERABLE: No validation of msg_num
    // User input directly used as index

    return messages[msg_num].size;

    // If msg_num < 0 or msg_num >= MAX_MESSAGES:
    // Out-of-bounds read - undefined behavior
    // Could read sensitive data from adjacent memory
}

// VULNERABLE: Buffer access with user-provided offset
void vulnerable_read_buffer(const uint8_t* buffer, size_t buffer_size,
                            int offset) {
    // VULNERABLE: No validation of offset

    uint8_t value = buffer[offset];  // Out-of-bounds if offset invalid
    process_byte(value);
}

// VULNERABLE: User-controlled array index
typedef struct {
    char name[32];
    double price;
    int quantity;
} product_t;

product_t products[1000];

product_t* vulnerable_get_product(int product_id) {
    // VULNERABLE: No bounds check
    // User-provided product_id used directly

    return &products[product_id];

    // Attacker can read/write outside products array
}

// VULNERABLE: File seek with user offset
void vulnerable_file_read(FILE* file, long offset, size_t length,
                          uint8_t* output) {
    // VULNERABLE: No validation of offset

    fseek(file, offset, SEEK_SET);  // Could seek beyond file
    fread(output, 1, length, file);  // Undefined behavior
}

// VULNERABLE: Negative index not checked
void vulnerable_negative_index(int* array, int index, int value) {
    // VULNERABLE: Negative index can access memory before array

    array[index] = value;

    // array[-1] accesses memory before array start
    // Can corrupt adjacent data structures
}
// Vulnerable: Java with unvalidated indices

public class VulnerableProductService {

    private Product[] products = new Product[1000];

    // VULNERABLE: No bounds check on index
    public Product getProduct(int index) {
        // VULNERABLE: ArrayIndexOutOfBoundsException or unexpected access

        return products[index];
    }

    // VULNERABLE: Servlet with user-provided index
    public void handleRequest(HttpServletRequest request,
                              HttpServletResponse response) {
        // VULNERABLE: User input directly used as index
        int productId = Integer.parseInt(request.getParameter("id"));

        Product product = products[productId];  // No validation!

        response.getWriter().write(product.toString());
    }

    // VULNERABLE: List access without bounds check
    private List<String> messages = new ArrayList<>();

    public String getMessage(int index) {
        // VULNERABLE: Throws exception or accesses wrong element

        return messages.get(index);
    }
}
# Vulnerable: Python with unvalidated indices

messages = ["msg1", "msg2", "msg3"]
products = [{"name": "A", "price": 10}, {"name": "B", "price": 20}]

def vulnerable_get_message(index):
    # VULNERABLE: No bounds check
    # Negative indices in Python wrap around!

    return messages[index]

    # index = -1 returns last message (might not be intended)
    # index = 100 raises IndexError (DoS)

def vulnerable_slice_buffer(data, start, end):
    # VULNERABLE: No validation of start/end

    return data[start:end]

    # Negative indices behave unexpectedly
    # Very large indices could cause issues

def vulnerable_dict_array_access(index):
    # VULNERABLE: No bounds check

    return products[index]

class VulnerableBuffer:
    def __init__(self, size):
        self.data = bytearray(size)

    def read_at(self, offset):
        # VULNERABLE: No bounds check
        return self.data[offset]

    def write_at(self, offset, value):
        # VULNERABLE: No bounds check
        self.data[offset] = value

Fixed Code

// Fixed: Proper validation of array indices

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

#define MAX_MESSAGES 100

typedef struct {
    char subject[64];
    char body[1024];
    size_t size;
} message_t;

message_t messages[MAX_MESSAGES];
int message_count = 0;

// FIXED: Bounds check on message number
bool secure_get_message_size(int msg_num, size_t* size_out) {
    // FIXED: Validate index range
    if (msg_num < 0) {
        log_error("Invalid message number: %d (negative)", msg_num);
        return false;
    }

    if (msg_num >= message_count) {
        log_error("Message number %d out of range (count=%d)",
                 msg_num, message_count);
        return false;
    }

    *size_out = messages[msg_num].size;
    return true;
}

// FIXED: Validate offset before buffer access
bool secure_read_buffer(const uint8_t* buffer, size_t buffer_size,
                        size_t offset, uint8_t* value_out) {
    // FIXED: Validate offset is within bounds
    if (offset >= buffer_size) {
        log_error("Offset %zu out of bounds (size=%zu)", offset, buffer_size);
        return false;
    }

    *value_out = buffer[offset];
    return true;
}

// FIXED: Validate array index with bounds
typedef struct {
    char name[32];
    double price;
    int quantity;
} product_t;

#define MAX_PRODUCTS 1000
product_t products[MAX_PRODUCTS];
int product_count = 0;

product_t* secure_get_product(int product_id) {
    // FIXED: Comprehensive validation
    if (product_id < 0) {
        log_error("Invalid product ID: %d (negative)", product_id);
        return NULL;
    }

    if (product_id >= product_count) {
        log_error("Product ID %d not found (count=%d)",
                 product_id, product_count);
        return NULL;
    }

    return &products[product_id];
}

// FIXED: Validate file offset
bool secure_file_read(FILE* file, long offset, size_t length,
                      uint8_t* output, size_t output_size) {
    if (output == NULL || file == NULL) {
        return false;
    }

    // FIXED: Get file size for validation
    long current_pos = ftell(file);
    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    fseek(file, current_pos, SEEK_SET);

    // FIXED: Validate offset
    if (offset < 0) {
        log_error("Negative file offset: %ld", offset);
        return false;
    }

    if (offset >= file_size) {
        log_error("Offset %ld beyond file size %ld", offset, file_size);
        return false;
    }

    // FIXED: Validate length
    if (length > output_size) {
        log_error("Length %zu exceeds output buffer", length);
        return false;
    }

    if (offset + (long)length > file_size) {
        log_error("Read would exceed file size");
        return false;
    }

    fseek(file, offset, SEEK_SET);
    size_t read = fread(output, 1, length, file);

    return (read == length);
}

// FIXED: Safe array access with explicit size
bool secure_array_write(int* array, size_t array_size,
                        int index, int value) {
    // FIXED: Check for negative index
    if (index < 0) {
        log_error("Negative index: %d", index);
        return false;
    }

    // FIXED: Check against actual size
    if ((size_t)index >= array_size) {
        log_error("Index %d out of bounds (size=%zu)", index, array_size);
        return false;
    }

    array[index] = value;
    return true;
}

// FIXED: Helper function for safe array access
#define SAFE_ARRAY_ACCESS(array, index, default_value) \
    ((index) >= 0 && (size_t)(index) < ARRAY_SIZE(array) ? \
     (array)[index] : (default_value))

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
// Fixed: Java with validated indices

public class SecureProductService {

    private Product[] products = new Product[1000];
    private int productCount = 0;

    // FIXED: Bounds check on index
    public Product getProduct(int index) throws ValidationException {
        // FIXED: Validate index
        if (index < 0) {
            throw new ValidationException("Invalid index: " + index + " (negative)");
        }

        if (index >= productCount) {
            throw new ValidationException(
                "Product index " + index + " out of range (count=" + productCount + ")");
        }

        return products[index];
    }

    // FIXED: Servlet with validated index
    public void handleRequest(HttpServletRequest request,
                              HttpServletResponse response)
            throws IOException, ValidationException {

        String idParam = request.getParameter("id");

        // FIXED: Validate parameter exists
        if (idParam == null || idParam.isEmpty()) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing id parameter");
            return;
        }

        int productId;
        try {
            productId = Integer.parseInt(idParam);
        } catch (NumberFormatException e) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid id format");
            return;
        }

        // FIXED: Use validated getter
        try {
            Product product = getProduct(productId);
            response.getWriter().write(product.toString());
        } catch (ValidationException e) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
        }
    }

    // FIXED: Safe list access
    private List<String> messages = new ArrayList<>();

    public Optional<String> getMessage(int index) {
        // FIXED: Bounds check
        if (index < 0 || index >= messages.size()) {
            return Optional.empty();
        }

        return Optional.of(messages.get(index));
    }
}
# Fixed: Python with validated indices

from typing import Optional, List, Any

messages = ["msg1", "msg2", "msg3"]
products = [{"name": "A", "price": 10}, {"name": "B", "price": 20}]

def secure_get_message(index: int) -> Optional[str]:
    """Get message at index with bounds checking."""
    # FIXED: Validate index type
    if not isinstance(index, int):
        raise TypeError(f"Index must be integer, got {type(index)}")

    # FIXED: Validate index range
    if index < 0:
        raise ValueError(f"Index cannot be negative: {index}")

    if index >= len(messages):
        raise IndexError(f"Index {index} out of range (size={len(messages)})")

    return messages[index]

def secure_slice_buffer(data: bytes, start: int, end: int) -> bytes:
    """Slice buffer with validated indices."""
    # FIXED: Validate types
    if not isinstance(start, int) or not isinstance(end, int):
        raise TypeError("Start and end must be integers")

    # FIXED: Validate range
    if start < 0:
        raise ValueError(f"Start cannot be negative: {start}")

    if end < start:
        raise ValueError(f"End {end} cannot be less than start {start}")

    if end > len(data):
        raise ValueError(f"End {end} exceeds data length {len(data)}")

    return data[start:end]

def secure_get_product(index: int) -> Optional[dict]:
    """Get product at index with validation."""
    # FIXED: Comprehensive validation
    if not isinstance(index, int):
        raise TypeError(f"Index must be integer, got {type(index)}")

    if index < 0:
        return None  # Or raise ValueError

    if index >= len(products):
        return None  # Or raise IndexError

    return products[index]

class SecureBuffer:
    def __init__(self, size: int):
        if size <= 0:
            raise ValueError(f"Size must be positive: {size}")
        self.data = bytearray(size)

    def read_at(self, offset: int) -> Optional[int]:
        """Read byte at offset with bounds checking."""
        # FIXED: Validate offset
        if not isinstance(offset, int):
            raise TypeError(f"Offset must be integer, got {type(offset)}")

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

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

        return self.data[offset]

    def write_at(self, offset: int, value: int) -> bool:
        """Write byte at offset with bounds checking."""
        # FIXED: Validate offset
        if not isinstance(offset, int):
            raise TypeError(f"Offset must be integer")

        if offset < 0 or offset >= len(self.data):
            return False

        # FIXED: Validate value
        if not isinstance(value, int) or value < 0 or value > 255:
            raise ValueError(f"Value must be byte (0-255): {value}")

        self.data[offset] = value
        return True

CVE Examples

  • CVE-2005-0369: Large packet ID used as array index in network protocol.
  • CVE-2001-1009: Negative array index in POP LIST command caused out-of-bounds access.

  • CWE-20: Improper Input Validation (parent)
  • CWE-129: Improper Validation of Array Index (child)
  • CWE-781: Improper Address Validation in IOCTL (child)
  • CWE-787: Out-of-bounds Write (related)
  • CWE-125: Out-of-bounds Read (related)

References

  1. MITRE Corporation. "CWE-1285: Improper Validation of Specified Index, Position, or Offset in Input." https://cwe.mitre.org/data/definitions/1285.html
  2. CERT C. "ARR30-C: Do not form or use out-of-bounds pointers or array subscripts"
  3. OWASP. "Input Validation Cheat Sheet"