Back to Blog
critical SEVERITY7 min read

Heap Buffer Overflow in ShadowsocksR: How a Missing Bounds Check Could Let Attackers Crash Your Server

A critical heap buffer overflow vulnerability was discovered in ShadowsocksR-libev's server.c, where network-supplied data was copied into fixed-size heap buffers without verifying that the source length fit within the destination. An attacker could craft a malicious packet with an oversized length field to overflow the heap, potentially enabling remote code execution or denial of service. The fix adds proper bounds checking, null pointer validation after memory allocation, and reallocation fail

O
By orbisai0security
May 28, 2026

Heap Buffer Overflow in ShadowsocksR: How a Missing Bounds Check Could Let Attackers Crash Your Server

Introduction

Memory safety bugs remain some of the most dangerous and exploitable vulnerabilities in systems programming. Among them, heap buffer overflows occupy a particularly notorious position — they can be leveraged by remote attackers to crash services, corrupt memory, or in the worst case, achieve arbitrary code execution on a target system.

This post examines a critical heap buffer overflow discovered in shadowsocksr-libev, a widely used proxy tool for network traffic obfuscation. The vulnerability existed in src/server/server.c and could be triggered by any remote attacker capable of sending a crafted network packet — no authentication required.

Whether you're a C developer, a security engineer, or someone who runs ShadowsocksR in production, understanding this vulnerability and its fix will help you write safer, more resilient network code.


The Vulnerability Explained

What Is a Heap Buffer Overflow?

A heap buffer overflow occurs when a program writes more data into a heap-allocated memory region than that region can hold. Unlike stack overflows, heap overflows can be subtle and harder to detect — but they're equally dangerous. Depending on heap layout and allocator behavior, an overflow can corrupt adjacent heap metadata, overwrite function pointers, or enable sophisticated exploitation techniques like heap spraying.

Where Was the Bug?

The vulnerability had two distinct manifestations in server_recv_cb, the callback function that processes incoming network data.

Problem 1: Unchecked malloc Return Value

// VULNERABLE CODE (before fix)
char *back_buf = (char*)malloc(sizeof(buffer_t));
memcpy(back_buf, buf, sizeof(buffer_t));

When malloc fails (returns NULL), dereferencing the null pointer in the subsequent memcpy causes undefined behavior — typically a segmentation fault and crash. In a network server, this means a single malformed packet could bring down the entire process.

Problem 2: Unchecked brealloc Return Value Before memcpy

// VULNERABLE CODE (before fix)
size_t header_len = server->header_buf->len;
brealloc(server->header_buf, server->buf->len + header_len, BUF_SIZE);
memcpy(server->header_buf->array + header_len,
       server->buf->array, server->buf->len);
server->header_buf->len = server->buf->len + header_len;

Here's where things get serious. The brealloc function resizes a buffer, but its return value is never checked. If reallocation fails:

  • server->header_buf may point to freed or invalid memory
  • The subsequent memcpy writes attacker-controlled data (server->buf->len bytes) into that invalid region
  • Since server->buf->len is derived directly from the attacker's packet, an attacker can supply an arbitrarily large length value

The result: a classic attacker-controlled heap overflow.

How Could It Be Exploited?

An attacker targeting this vulnerability would:

  1. Connect to the ShadowsocksR server — no credentials needed, just a TCP connection
  2. Craft a malicious packet with an oversized len field in the buffer structure
  3. Send the packet during the handshake stage (STAGE_HANDSHAKE)
  4. Trigger the overflow when the server attempts to copy the oversized data into the under-allocated buffer

Potential impact:
- 🔴 Remote Denial of Service — crash the server process reliably
- 🔴 Heap Corruption — corrupt adjacent allocations, leading to unpredictable behavior
- 🔴 Remote Code Execution (RCE) — with sufficient heap manipulation, potentially hijack control flow

The CVSS score for this class of vulnerability in a network-facing daemon is typically 9.0+, firmly in critical territory.

The Bonus Bug: strncpy vs snprintf

The diff also reveals a subtler fix:

// BEFORE
strncpy(svaddr.sun_path, manager_address, sizeof(svaddr.sun_path) - 1);

// AFTER
snprintf(svaddr.sun_path, sizeof(svaddr.sun_path), "%s", manager_address);

While strncpy with size - 1 is a common pattern, it has a well-known pitfall: it does not guarantee null-termination if the source string exactly fills the buffer. snprintf always null-terminates within the specified size, making it the safer choice for this kind of bounded string copy.


The Fix

The patch introduces three targeted changes that eliminate the vulnerability without altering program logic.

Fix 1: Check malloc Return Value

// FIXED CODE
char *back_buf = (char*)malloc(sizeof(buffer_t));
if (back_buf == NULL) {
    close_and_free_remote(EV_A_ remote);
    close_and_free_server(EV_A_ server);
    return;
}
memcpy(back_buf, buf, sizeof(buffer_t));

What changed: A null check is added immediately after malloc. If allocation fails, the connection is cleanly closed and the function returns — no crash, no undefined behavior.

Why it works: memcpy is never reached with a null destination pointer. The server gracefully handles memory pressure instead of crashing.

Fix 2: Check brealloc Return Value

// FIXED CODE
if (brealloc(server->header_buf, server->buf->len + header_len, BUF_SIZE) != 0) {
    close_and_free_remote(EV_A_ remote);
    close_and_free_server(EV_A_ server);
    return;
}
memcpy(server->header_buf->array + header_len,
       server->buf->array, server->buf->len);
server->header_buf->len = server->buf->len + header_len;

What changed: The return value of brealloc is now checked. A non-zero return indicates failure, and the code bails out cleanly before the memcpy.

Why it works: The memcpy only executes when we have a valid, correctly-sized destination buffer. An attacker can no longer trigger an overflow by sending a packet with an oversized length field — if the reallocation would fail or produce an insufficient buffer, the operation is aborted.

Fix 3: Safer String Copy

// FIXED CODE
snprintf(svaddr.sun_path, sizeof(svaddr.sun_path), "%s", manager_address);

What changed: strncpy replaced with snprintf, which guarantees null-termination.


Before vs. After: Side-by-Side

Aspect Before After
malloc failure Null pointer dereference Graceful connection close
brealloc failure Heap overflow via attacker-controlled len Graceful connection close
String copy Potentially non-null-terminated Always null-terminated
Attack surface Remote, unauthenticated Eliminated

Prevention & Best Practices

This vulnerability is a textbook example of C memory management pitfalls. Here's how to avoid similar issues in your own code:

1. Always Check Allocation Return Values

// ✅ CORRECT
void *ptr = malloc(size);
if (ptr == NULL) {
    // handle error
    return ERROR_OOM;
}

// ❌ WRONG
void *ptr = malloc(size);
memcpy(ptr, src, size); // UB if ptr is NULL

This applies to malloc, calloc, realloc, and any custom allocator wrappers.

2. Validate Lengths Before Copying

Never trust length values derived from network input:

// ✅ CORRECT
if (attacker_supplied_len > destination_buffer_size) {
    // reject or truncate
    return ERROR_INVALID_LENGTH;
}
memcpy(dest, src, attacker_supplied_len);

// ❌ WRONG
memcpy(dest, src, attacker_supplied_len); // overflow if len > dest size

3. Prefer Safer Alternatives

Unsafe Safer Alternative
strcpy snprintf, strlcpy
strncpy snprintf
sprintf snprintf
gets fgets
memcpy (unchecked) memcpy with bounds check

4. Use Modern Analysis Tools

Several tools can catch these bugs automatically:

  • AddressSanitizer (ASan) — detects heap overflows at runtime: gcc -fsanitize=address
  • Valgrind — memory error detection: valgrind --tool=memcheck ./server
  • Coverity / CodeQL — static analysis for null dereferences and buffer overflows
  • libFuzzer / AFL++ — fuzz network inputs to discover overflow conditions
  • clang-tidy — static linting with security-focused checks

5. Apply Defense in Depth

  • ASLR + PIE: Randomize memory layout to make exploitation harder
  • Stack canaries: Detect stack corruption (-fstack-protector-strong)
  • RELRO: Protect GOT/PLT from overwrites (-Wl,-z,relro,-z,now)
  • Seccomp: Restrict syscalls available to the server process

6. Relevant Security Standards

  • CWE-122: Heap-based Buffer Overflow
  • CWE-476: NULL Pointer Dereference
  • CWE-131: Incorrect Calculation of Buffer Size
  • OWASP: Memory Management Cheat Sheet
  • SEI CERT C: Rule MEM35-C (allocate sufficient memory), Rule MEM32-C (detect allocation errors)

Conclusion

This vulnerability in ShadowsocksR-libev is a reminder that network-facing C code demands extreme care around memory operations. Two missing null checks and one unchecked reallocation were all it took to expose a critical attack surface to any remote client.

The fix is elegant in its simplicity: check return values, handle failures gracefully, and never perform a memory copy without verifying the destination is valid and large enough. These are fundamentals of safe C programming — but under deadline pressure or in complex codebases, they're easy to miss.

Key takeaways:

  • ✅ Always check the return value of memory allocation functions
  • ✅ Never use attacker-controlled length values in memcpy without validation
  • ✅ Prefer snprintf over strncpy for bounded string copies
  • ✅ Use ASan and fuzzing in your CI pipeline for network-facing code
  • ✅ Treat all network input as potentially malicious — because it is

Security is not a feature you add at the end. It's a discipline you apply at every line.


This vulnerability was identified and fixed by automated security scanning. If you maintain C network code, consider integrating similar tooling into your development workflow.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #658

Related Articles

critical

Heap Buffer Overflow in Audio Ring Buffer: How a Missing Bounds Check Could Crash Your App

A critical heap buffer overflow vulnerability was discovered in `audio_backend.c`, where the audio ring buffer's `memcpy` operations lacked bounds validation before writing PCM data. Without checking that incoming data sizes fell within the allocated buffer's capacity, a maliciously crafted audio file could corrupt adjacent heap memory, potentially enabling arbitrary code execution. The fix adds a concise pre-flight validation guard that rejects out-of-range write requests before any memory oper

critical

Critical Heap Buffer Overflow in SSDP Control Point: How Unbounded String Operations Put Networks at Risk

A critical heap buffer overflow vulnerability was discovered and patched in the SSDP control point implementation (`ssdp_ctrlpt.c`), where multiple unbounded `strcpy` and `strcat` operations constructed HTTP request buffers without any length validation. Network-received SSDP response fields — including service type strings and location URLs — could be crafted by an attacker to exceed buffer boundaries, potentially enabling arbitrary code execution or denial of service. The fix replaces the unsa

critical

Heap Buffer Overflow in OPDS Parser: How a Misplaced Variable Nearly Opened the Door to Remote Code Execution

A critical heap buffer overflow vulnerability was discovered in `lib/OpdsParser/OpdsParser.cpp`, where the buffer allocation size was calculated *after* a fixed chunk size was used to allocate memory, meaning the actual bytes read could exceed the allocated buffer. On embedded devices parsing untrusted OPDS catalog data from the network, this flaw could allow a remote attacker to corrupt heap memory and potentially achieve arbitrary code execution. The fix was elegantly simple: move the `toRead`

critical

Heap Buffer Overflow in BLE MIDI: How a Missing Bounds Check Opens the Door to Remote Exploitation

A critical heap buffer overflow vulnerability was discovered in the BLE MIDI packet assembly code of `blemidi.c`, where attacker-controlled packet length values could trigger writes beyond allocated heap memory. The fix adds an integer overflow guard before the `malloc` call, ensuring that maliciously crafted BLE MIDI packets can no longer corrupt heap memory. This vulnerability is particularly dangerous because it is remotely exploitable by any nearby Bluetooth device — no physical access requi

critical

Heap Overflow in TOML Parser: How Integer Overflow Leads to Memory Corruption

A critical heap buffer overflow vulnerability was discovered and patched in the centitoml TOML parser, where missing integer overflow validation on a `MALLOC(len+1)` call could allow an attacker to trigger memory corruption via a crafted TOML configuration file. The vulnerability (CWE-190) is reachable through community-distributed mod or map files that the game loads from its `config/` directory, making it a realistic attack vector for remote code execution. A targeted one-line guard now preven

critical

Heap Corruption via Unchecked memcpy: How Integer Overflow Bugs Corrupt Memory in Windows File Operations

A critical buffer overflow vulnerability was discovered in `phlib/nativefile.c`, where multiple `memcpy` calls copied filename and extended-attribute data into fixed-size structures without verifying that source lengths didn't exceed destination buffer boundaries. An attacker supplying an oversized filename or EA name could corrupt adjacent heap memory, potentially enabling arbitrary code execution. The fix replaces unchecked arithmetic with Windows' safe integer helpers (`RtlULongAdd`, `RtlULon