Untrusted Pointer Dereference

Description

Untrusted Pointer Dereference occurs when software obtains a value from an untrusted source, converts or uses that value as a pointer, and then dereferences the pointer. Attackers who can control or influence the pointer value can direct it to arbitrary memory locations, enabling them to read sensitive data, corrupt memory, cause crashes, or execute arbitrary code. This is particularly dangerous when the untrusted value is used directly as a function pointer, when kernel code dereferences pointers provided from user space, or when software designed for trusted environments is exposed to network input.

Risk

This vulnerability can lead to complete system compromise. When attackers control a pointer value, they can read from arbitrary memory locations to steal sensitive data like cryptographic keys or passwords. Writing through a controlled pointer allows corruption of critical data structures, function pointers, or return addresses, enabling code execution. In kernel or privileged contexts, the impact is amplified as attackers can access or modify any memory in the system. Even if code execution is not achieved, dereferencing invalid pointers causes crashes that can be exploited for denial of service. The vulnerability is especially critical in C/C++ code handling network input or processing untrusted data.

Solution

Never convert untrusted input directly to pointers. Validate that pointer values fall within expected ranges before dereferencing. In kernel code, use proper copy_from_user()/copy_to_user() functions and validate all pointers from user space. Implement bounds checking on array indices and offsets before converting to pointer arithmetic. Use memory-safe languages where possible. When handling indices into arrays or tables, validate against the actual size. For function pointers, use indirect tables (switch statements or validated indices) rather than raw pointer values from untrusted sources. Enable runtime protections like ASLR, DEP, and CFI. Use tools like AddressSanitizer during development to detect pointer issues.

Common Consequences

ImpactDetails
ConfidentialityScope: Confidentiality

Read Memory - If the untrusted pointer is used in a read operation, attackers may read sensitive portions of memory including credentials, keys, or private data.
AvailabilityScope: Availability

DoS: Crash, Exit, or Restart - Dereferencing pointers to inaccessible or invalid memory locations causes unexpected application termination.
Integrity, Confidentiality, AvailabilityScope: Integrity, Confidentiality, Availability

Execute Unauthorized Code or Commands - If the pointer is used as a function pointer or in a write operation, attackers may achieve arbitrary code execution.

Example Code

Vulnerable Code

// Vulnerable: Using untrusted value directly as pointer
#include <stdint.h>

void vulnerable_read_memory(int socket) {
    uint64_t address;

    // Receive address from network
    recv(socket, &address, sizeof(address), 0);

    // Vulnerable: Directly converting untrusted value to pointer
    char* ptr = (char*)address;

    // Vulnerable: Dereferencing untrusted pointer
    char data = *ptr;  // Attacker controls what memory is read

    send(socket, &data, 1, 0);
}
// Vulnerable: Kernel IOCTL with user-provided pointer
#include <linux/kernel.h>

long vulnerable_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    struct user_request req;
    char *user_buffer;

    if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
        return -EFAULT;

    // Vulnerable: req.buffer_ptr came from user and is not validated
    user_buffer = (char *)req.buffer_ptr;

    // Vulnerable: Dereferencing user-controlled pointer in kernel context
    char kernel_data = *user_buffer;  // Can read any kernel memory!

    return 0;
}
// Vulnerable: Function pointer from untrusted source
typedef void (*callback_t)(void);

void vulnerable_callback(int socket) {
    uint64_t func_addr;

    recv(socket, &func_addr, sizeof(func_addr), 0);

    // Vulnerable: Using untrusted value as function pointer
    callback_t callback = (callback_t)func_addr;

    // Vulnerable: Calling attacker-controlled address
    callback();  // Arbitrary code execution!
}
// Vulnerable: Index used to compute pointer without validation
struct record {
    char name[32];
    int value;
};

struct record records[100];

int vulnerable_get_value(int socket) {
    int index;

    recv(socket, &index, sizeof(index), 0);

    // Vulnerable: No bounds check on index
    struct record *ptr = &records[index];

    // If index > 99 or < 0, accesses out-of-bounds memory
    return ptr->value;
}
// Vulnerable: Offset used as pointer arithmetic
void vulnerable_process_packet(char *packet, int length) {
    int offset;

    // Read offset from packet
    memcpy(&offset, packet, sizeof(offset));

    // Vulnerable: No validation of offset
    char *data_ptr = packet + offset;

    // Could point anywhere in memory if offset is large
    printf("Data: %s\n", data_ptr);
}
// Vulnerable: Type confusion leading to pointer dereference
union data_union {
    int integer;
    char *pointer;
};

void vulnerable_type_confusion(int socket) {
    union data_union data;
    int type;

    recv(socket, &type, sizeof(type), 0);
    recv(socket, &data, sizeof(data), 0);

    if (type == 1) {
        printf("Integer: %d\n", data.integer);
    } else {
        // Vulnerable: Attacker sends type=2 but controls data.pointer
        printf("String: %s\n", data.pointer);  // Arbitrary read
    }
}

Fixed Code

// Fixed: Never convert untrusted input to pointer
#include <stdint.h>

// Use validated indices into a known table instead
static char* valid_buffers[16];
static size_t buffer_count = 0;

int fixed_read_memory(int socket) {
    uint32_t index;

    recv(socket, &index, sizeof(index), 0);

    // Fixed: Validate index against known bounds
    if (index >= buffer_count) {
        return -1;  // Invalid index
    }

    // Safe: Using validated index into controlled array
    char* ptr = valid_buffers[index];
    if (ptr == NULL) {
        return -1;
    }

    char data = *ptr;
    send(socket, &data, 1, 0);
    return 0;
}
// Fixed: Proper kernel pointer handling
#include <linux/kernel.h>
#include <linux/uaccess.h>

long fixed_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
    struct user_request req;
    char user_data;

    if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
        return -EFAULT;

    // Fixed: Use copy_from_user for user-provided addresses
    // This validates that the address is in user space
    if (!access_ok((void __user *)req.buffer_ptr, 1))
        return -EFAULT;

    if (copy_from_user(&user_data, (void __user *)req.buffer_ptr, 1))
        return -EFAULT;

    // Now user_data contains safely copied data
    process_data(user_data);

    return 0;
}
// Fixed: Use validated function table instead of raw pointer
typedef void (*callback_t)(void);

static callback_t valid_callbacks[] = {
    handler_type_0,
    handler_type_1,
    handler_type_2,
    handler_type_3
};
#define NUM_CALLBACKS (sizeof(valid_callbacks) / sizeof(valid_callbacks[0]))

int fixed_callback(int socket) {
    uint32_t callback_index;

    recv(socket, &callback_index, sizeof(callback_index), 0);

    // Fixed: Validate index into function table
    if (callback_index >= NUM_CALLBACKS) {
        return -1;  // Invalid callback
    }

    // Safe: Using validated index into controlled function table
    valid_callbacks[callback_index]();
    return 0;
}
// Fixed: Bounds-checked array access
struct record {
    char name[32];
    int value;
};

struct record records[100];
#define MAX_RECORDS 100

int fixed_get_value(int socket) {
    int32_t index;

    recv(socket, &index, sizeof(index), 0);

    // Fixed: Validate index bounds
    if (index < 0 || index >= MAX_RECORDS) {
        return -1;  // Out of bounds
    }

    struct record *ptr = &records[index];
    return ptr->value;
}
// Fixed: Validate offset against buffer bounds
int fixed_process_packet(char *packet, int packet_length) {
    int32_t offset;

    if (packet_length < sizeof(offset)) {
        return -1;  // Packet too small
    }

    memcpy(&offset, packet, sizeof(offset));

    // Fixed: Validate offset is within packet
    if (offset < 0 || offset >= packet_length) {
        return -1;  // Invalid offset
    }

    // Safe: offset is validated
    char *data_ptr = packet + offset;

    // Additional check: ensure null-terminated within bounds
    size_t remaining = packet_length - offset;
    size_t len = strnlen(data_ptr, remaining);
    if (len == remaining) {
        return -1;  // String not terminated within packet
    }

    printf("Data: %s\n", data_ptr);
    return 0;
}
// Fixed: Strict type handling
struct typed_data {
    int type;
    union {
        int integer;
        size_t string_index;  // Index into string table, not raw pointer
    } value;
};

static const char* string_table[] = {
    "String 0",
    "String 1",
    "String 2"
};
#define STRING_TABLE_SIZE 3

void fixed_type_handling(int socket) {
    struct typed_data data;

    recv(socket, &data, sizeof(data), 0);

    if (data.type == 1) {
        printf("Integer: %d\n", data.value.integer);
    } else if (data.type == 2) {
        // Fixed: Use validated index into string table
        if (data.value.string_index < STRING_TABLE_SIZE) {
            printf("String: %s\n", string_table[data.value.string_index]);
        }
    }
}

  • CWE-119: Improper Restriction of Operations within Bounds of Memory Buffer (parent)
  • CWE-781: Improper Address Validation in IOCTL with METHOD_NEITHER (can precede)
  • CWE-125: Out-of-bounds Read (can follow)
  • CWE-787: Out-of-bounds Write (can follow)
  • CWE-476: NULL Pointer Dereference (related)
  • CWE-823: Use of Out-of-range Pointer Offset (related)

References

  1. MITRE Corporation. "CWE-822: Untrusted Pointer Dereference." https://cwe.mitre.org/data/definitions/822.html
  2. CERT C Secure Coding Standard. "EXP34-C. Do not dereference null pointers."
  3. Microsoft. "Driver Security Checklist." https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/driver-security-checklist