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 Orbis AppSec
Published May 28, 2026Reviewed June 3, 2026

Answer Summary

This is a critical heap buffer overflow vulnerability (CWE-122) in ShadowsocksR-libev's C-language `server.c` file. The root cause is that network-supplied data was copied into fixed-size heap buffers without verifying the source length fit within the destination, allowing an attacker to send a crafted packet with an oversized length field to corrupt heap memory. The fix adds explicit bounds checking before any copy operation, null pointer validation after `malloc`/`realloc` calls, and safe handling of reallocation failures to prevent memory corruption.

Vulnerability at a Glance

cweCWE-122
fixAdd bounds check before copy, validate allocation results, handle realloc failure safely
riskRemote code execution or denial of service via crafted network packet
languageC
root causeNetwork-supplied length field used to copy data into heap buffer without bounds validation
vulnerabilityHeap Buffer Overflow

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.

Frequently Asked Questions

What is a heap buffer overflow?

A heap buffer overflow occurs when a program writes more data into a heap-allocated buffer than it was sized to hold, corrupting adjacent memory. In C, this typically happens when a length value from an untrusted source is used in a `memcpy` or similar operation without first verifying it does not exceed the buffer's allocated size.

How do you prevent heap buffer overflow in C?

Always validate that the number of bytes to be copied does not exceed the destination buffer's allocated size before performing any copy. Use safe allocation patterns that check for NULL returns, prefer bounded copy functions, and consider AddressSanitizer or Valgrind during development to catch overflows early.

What CWE is heap buffer overflow?

Heap buffer overflow is classified as CWE-122 (Heap-based Buffer Overflow), a subset of CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer).

Is input validation alone enough to prevent heap buffer overflow in C?

Input validation is necessary but not sufficient on its own. You must also validate lengths at every copy site, check allocation return values for NULL, and handle reallocation failures without freeing or overwriting the original pointer — all of which were part of this fix.

Can static analysis detect heap buffer overflow?

Yes. Tools like Semgrep, Coverity, CodeQL, and AddressSanitizer (at runtime) can detect patterns where untrusted length values flow into buffer copy operations without intervening bounds checks. Orbis AppSec detected this exact pattern automatically.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #658

Related Articles

high

How insecure string copy functions happen in C apputils.c and how to fix it

A high-severity buffer overflow vulnerability was discovered in `src/apps/common/apputils.c`, where `strncpy()` was used without guaranteed null-termination across four call sites — including the `sock_bind_to_device()` and `getdomainname()` functions. The fix replaces all unsafe `strncpy()` calls with `snprintf()`, which enforces both length bounds and automatic null-termination. Left unpatched, these flaws could allow an attacker to corrupt memory, crash the process, or potentially execute arb

critical

How integer overflow in buffer size calculation happens in C++ and how to fix it

A critical integer overflow vulnerability was discovered in OpenCV's HAL filter implementation where multiplying image dimensions without overflow protection could allocate dangerously undersized buffers. An attacker supplying crafted image dimensions (e.g., 65536×65536) could trigger heap corruption through out-of-bounds writes. The fix promotes the calculation to 64-bit arithmetic with a single cast.

critical

How buffer overflow via strcpy() happens in C zlib and how to fix it

A critical buffer overflow vulnerability was discovered in `general/libzlib/gzlib.c` where multiple `strcpy()` and `strcat()` calls operated without bounds checking. An attacker controlling file paths or error messages could overflow destination buffers, potentially achieving arbitrary code execution. The fix replaces these unsafe string operations with bounded `memcpy()` calls that respect pre-calculated buffer lengths.

critical

How buffer overflow in rcdevice.c request parser happens in C and how to fix it

A critical buffer overflow vulnerability was discovered in `src/main/io/rcdevice.c` at line 489, where the RC device request parser wrote incoming data into a fixed-size buffer without validating against the hard-coded maximum capacity `RCDEVICE_PROTOCOL_MAX_DATA_SIZE`. An attacker controlling the device's I/O data stream could overflow the buffer by sending a payload longer than `expectedDataLength`, potentially achieving arbitrary code execution. The fix adds a second bounds check against the

critical

How buffer overflow via unchecked memcpy offset happens in C++ PCL point cloud parsing and how to fix it

A critical out-of-bounds read vulnerability was discovered in `pcpatch_pcl.cpp` where the `readFloat` lambda performed a `memcpy` operation using an untrusted offset value without validating buffer boundaries. An attacker could craft malicious PCD point cloud files with large offset values to read memory outside allocated buffers, potentially leaking sensitive data or causing crashes. The fix adds a bounds check ensuring `f->offset + sizeof(float)` stays within the row buffer before any memory c

critical

How buffer overflow in stb_image.h memcpy happens in C image parsing and how to fix it

A critical buffer overflow vulnerability was discovered in stb_image.h at line 4823, where a memcpy operation copied image data without validating buffer bounds. The multiplication of width (x) and channel count (img_n) could overflow or exceed allocated memory, allowing attackers to corrupt memory through malicious PNG files. The fix adds an explicit size_t cast to prevent integer overflow during the buffer size calculation.