CWE-787: Out-of-Bounds Write — When Software Writes Beyond Its Limits

Memory corruption vulnerabilities remain among the most devastating classes of software flaws, and CWE-787: Out-of-Bounds Write consistently ranks near the top of the SANS / MITRE Top 25 for one reason:

A single out-of-bounds write can turn a simple bug into full remote code execution.

Unlike many web vulnerabilities, memory corruption flaws often allow attackers to compromise the underlying process itself—overwriting adjacent memory, corrupting control flow, and executing arbitrary code.

This article breaks down how out-of-bounds writes occur, why they remain prevalent, how modern exploitation works, and what developers can do to prevent them.

What Is an Out-of-Bounds Write?

An out-of-bounds write occurs when software writes data outside the valid boundaries of an allocated memory buffer.

In practice:

The program places data into memory it does not own.

That overwritten memory may contain:

  • Other variables
  • Application state
  • Heap metadata
  • Function pointers
  • Return addresses
  • Security-critical control structures

How Out-of-Bounds Writes Actually Work

A buffer is allocated for a specific size:

char buffer[8];

If code writes more than 8 bytes:

strcpy(buffer, userInput);

and userInput contains 32 bytes, the extra data spills into adjacent memory.


Common Causes

1. Unsafe String Handling

char name[32];
strcpy(name, input);

No bounds checking.

2. Incorrect Length Validation

if (len <= 32)
    memcpy(buf, src, len + 1);

Off-by-one errors remain common.

3. Integer Overflow Leading to Underallocation

size_t size = count * sizeof(Item);
malloc(size);

If multiplication overflows, the allocation is smaller than expected.

4. Improper Loop Bounds

for (int i = 0; i <= size; i++)

Using <= instead of <.

Why Developers Still Get It Wrong

Legacy Languages Prioritize Performance Over Safety

C and C++ provide raw memory access with minimal safety checks.

That flexibility enables performance—but places full responsibility on developers.

Unsafe APIs Persist

Dangerous functions remain widespread:

  • strcpy
  • strcat
  • sprintf
  • gets
  • Unchecked memcpy

Modern Complexity Increases Edge Cases

Even experienced developers struggle with:

  • Variable-length inputs
  • Nested allocations
  • Serialization formats
  • Protocol parsing
  • Compression/decompression logic

Testing Rarely Covers Boundary Abuse

Typical tests validate expected behavior.

Attackers test:

  • Maximum lengths
  • Negative values
  • Integer wraparound
  • Malformed protocol fields

Modern Exploitation Techniques

Out-of-bounds writes are no longer exploited with simplistic “smash the stack” techniques alone.

Modern exploitation targets complex memory layouts.

Stack Buffer Overflow

Overwrites return addresses / saved registers.

Historically:

[ Buffer ][ Saved Frame Ptr ][ Return Address ]

Overflow modifies return address.

Heap Corruption

Attackers corrupt:

  • Adjacent objects
  • Heap metadata
  • Virtual function tables
  • Allocator bookkeeping

Function Pointer Overwrite

Overwrite callback pointers:

handler->callback = attacker_controlled_address;

Object Corruption / Type Confusion Chains

Particularly common in browsers:

  • Corrupt object length fields
  • Modify object type metadata
  • Redirect execution indirectly

Visual: Exploitation Chain

Out-of-Bounds Write Stack Corruption Heap Corruption Pointer Overwrite Code Execution

Why This Vulnerability Is So Dangerous

Unlike many application flaws, out-of-bounds writes often result in:

  • Remote Code Execution (RCE)
  • Sandbox Escape
  • Privilege Escalation
  • Kernel Compromise
  • Persistent Malware Installation

This is why memory corruption flaws dominate:

  • Browser zero-days
  • OS kernel exploits
  • Hypervisor escapes
  • Embedded device compromises

Secure Coding Examples

Unsafe

void copy_name(char *input) {
    char buf[16];
    strcpy(buf, input);
}

Safer Alternative

void copy_name(char *input) {
    char buf[16];
    strncpy(buf, input, sizeof(buf)-1);
    buf[15] = '\0';
}

Note: strncpy is safer but still imperfect. Prefer modern safe abstractions where available.

Safer C++ Alternative

std::string name = input;

Use bounded abstractions instead of raw arrays.

Framework / Language Mitigations

Rust

Rust prevents most out-of-bounds writes by design:

let x = arr[10];

Triggers bounds checks / panic rather than silent corruption.

Modern C++

Prefer:

  • std::vector
  • std::array
  • std::span
  • std::string

Over raw pointers / arrays.

Compiler Protections

Stack Canaries

Detect stack corruption before return.

ASLR (Address Space Layout Randomization)

Makes memory layout unpredictable.

DEP / NX

Prevents execution of injected shellcode from data pages.

Control Flow Integrity (CFI)

Restricts valid control flow transitions.

Important Reality Check

Mitigations reduce exploitability.

They do not eliminate the vulnerability.

Attackers routinely chain techniques to bypass:

  • ASLR
  • DEP
  • Canaries
  • Sandboxing

The only reliable fix is preventing the memory corruption.


Best Practices for Prevention

Validate All Lengths and Sizes

Treat all externally influenced lengths as untrusted.

Avoid Unsafe APIs

Ban dangerous functions in code review/tooling.

Use Memory-Safe Languages Where Possible

Prefer:

  • Rust
  • Go
  • Java
  • C#

for new development when performance constraints allow.

Fuzz Boundary Conditions

Fuzzing is particularly effective against memory corruption.

Test:

  • Oversized inputs
  • Malformed structures
  • Edge-case lengths
  • Nested malformed objects

Run Sanitizers in CI

Use:

  • AddressSanitizer (ASan)
  • UndefinedBehaviorSanitizer (UBSan)
  • MemorySanitizer (MSan)

Final Thoughts

Out-of-bounds writes remain one of the most dangerous software weaknesses because they convert ordinary parsing or logic bugs into full system compromise.

They persist because:

  • Unsafe languages remain widespread
  • Legacy codebases are enormous
  • Performance concerns discourage safety abstractions
  • Developers underestimate boundary complexity

The lesson is straightforward:

If software manages memory manually, memory safety must be treated as a first-class security concern.