Safeguard
Application Security•

CWE-822 Untrusted Pointer Dereference in C and C++

CWE-822 happens when a value from an untrusted source becomes a pointer and gets dereferenced. See where it appears and how to defend against it.

Safeguard Security Team
3 min read

CWE-822 describes a product that takes a value from an untrusted source, converts it into a pointer, and dereferences that pointer. The attacker picks the address the program touches.

MITRE lists C and C++ as the platforms. It is most serious in places with a trust boundary: parsers, message passing systems, and OS kernels or drivers where userland values cross into privileged code.

How it happens

A pointer is just a number. If code receives that number from a packet, file, or call argument and treats it as a valid address, the sender controls where reads and writes go. MITRE lists several variants:

  • The untrusted value is directly invoked as a function call.
  • In kernels or drivers, an untrusted pointer enters through an API or system call.
  • A value is accepted from an untrusted control sphere when it did not have to be.

The record's examples show the range. CVE-2007-5655 is a message passing framework that interprets values in packets as pointers, causing a crash. CVE-2009-1250 involves an error code incorrectly checked and interpreted as a pointer.

// Vulnerable: an address taken from the wire
struct msg { uintptr_t cb; };
void handle(const struct msg *m) {
    void (*fn)(void) = (void (*)(void))m->cb;
    fn();   // attacker chooses what runs
}

What an attacker gains

  • Read memory. A read through the pointer can expose sensitive memory.
  • Crash. An inaccessible or malformed location can end the process.
  • Code execution and memory changes. A write, or use of the pointer as a function, can alter critical state or run attacker chosen code.

How to find it

MITRE names static analysis, which can find some instances by modeling data flow, and dynamic analysis with compiler inserted checks such as AddressSanitizer. Crafted inputs are required to reach the faulty code, so fuzz the parsers and message handlers.

How to fix it

The record has no formal mitigations, so these are practical rules.

  1. Never convert external data into an address. Send an index or handle and look it up in a table you own.
  2. Validate that the index is in range before use.
  3. In kernel code, use the interfaces that copy from user space with checks, instead of dereferencing user pointers directly.
  4. Never let received data select a function to call. Map IDs to a fixed set of functions.
// Fixed: a small id selects from a fixed table
static void (*const handlers[])(void) = { on_open, on_close };
void handle(const struct msg *m) {
    if (m->id >= sizeof handlers / sizeof handlers[0]) return;
    handlers[m->id]();
}

Quick check

Why use an index instead of a pointer on the wire?

An index is checked against a table you own, so the sender can only pick valid entries. A raw pointer lets the sender pick any address.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.