Back to Blog
critical SEVERITY8 min read

Critical Buffer Overflow in strcpy(): How Unbounded Copies Crash Systems

A critical buffer overflow vulnerability was discovered and patched in a custom `strcpy()` implementation used system-wide across kernel and userspace code. Without bounds checking, any oversized input could overwrite stack frames, return addresses, or heap metadata — opening the door to remote code execution. This post breaks down how the vulnerability works, how it was fixed, and what every C developer should know to avoid the same mistake.

O
By Orbis AppSec
Published May 10, 2026Reviewed June 3, 2026

Answer Summary

A critical buffer overflow (CWE-120) in a custom strcpy() implementation allowed unbounded copying of user-controlled input to fixed-size buffers without length validation. The fix introduces bounds checking via `strncpy()` or manual length validation to ensure copies never exceed buffer capacity, preventing stack corruption and code execution.

Vulnerability at a Glance

cweCWE-120 (Buffer Copy without Checking Size of Input)
fixReplace strcpy() with bounds-checking alternatives and validate input length before copying
riskRemote code execution, denial of service, privilege escalation
languageC
root causeUnbounded string copy operation with no size validation on user-controlled input
vulnerabilityBuffer Overflow in strcpy()

Critical Buffer Overflow in strcpy(): How Unbounded Copies Crash Systems

Severity: 🔴 Critical | CVE Class: CWE-121 (Stack-based Buffer Overflow) | Fixed In: lib/stdc/str.c


Introduction

Few vulnerabilities in systems programming are as old, as well-understood, and yet as persistently dangerous as the buffer overflow. Despite decades of warnings, compiler flags, and static analysis tools, unbounded string copies continue to appear in production codebases — sometimes in the most critical places imaginable.

This post covers a recently patched critical severity buffer overflow in a custom strcpy() implementation located in lib/stdc/str.c. What makes this particular finding especially serious isn't just the vulnerability itself — it's where the vulnerable function lived: at the heart of a system-wide string library used by kernel code, PPP authentication handlers, network packet processors, and archive path construction utilities.

When a single vulnerable function is called everywhere, fixing it everywhere starts with fixing it once, in the right place.


What Is a Buffer Overflow?

A buffer overflow occurs when a program writes more data into a memory buffer than the buffer was allocated to hold. The excess data spills into adjacent memory regions, potentially overwriting:

  • Local variables on the stack
  • Return addresses (enabling control flow hijacking)
  • Saved frame pointers
  • Heap metadata (in heap-based variants)

In the context of string operations in C, the classic culprit is strcpy() — a function that copies bytes from a source string to a destination buffer and stops only when it encounters a null terminator (\0). If the source string is longer than the destination buffer, strcpy() will happily keep writing past the end of the buffer without complaint.


The Vulnerability Explained

Technical Details

The vulnerability resided in lib/stdc/str.c at line 248, in a custom implementation of strcpy(). The function signature looked something like this:

// VULNERABLE: No bounds checking, no size parameter
char *strcpy(char *dest, const char *src) {
    char *d = dest;
    while ((*d++ = *src++) != '\0')
        ;
    return dest;
}

This is a textbook unbounded copy. The function accepts a destination pointer and a source pointer, but no size parameter. It copies bytes from src to dest until it hits a null terminator — with absolutely no awareness of how large the destination buffer actually is.

Why This Is Especially Dangerous Here

In most codebases, a vulnerable strcpy() is bad. In this codebase, it was catastrophic in potential scope because this was the system-wide implementation used by:

Caller Risk
PPP authentication handler (auth.c) Attacker-controlled authentication strings could overflow stack buffers
SAF archive path construction Malicious archive paths could overwrite adjacent memory
Network packet string processing Remote attackers sending oversized packet payloads could trigger overflow

Any one of these call sites receiving input longer than its destination buffer would cause a stack or heap overflow.

How Could It Be Exploited?

Consider the PPP authentication handler. When a remote peer sends authentication credentials during a PPP handshake, those credentials are received over the network and passed into string processing routines. If an attacker crafts an authentication packet with an unusually long username or password field, the call to strcpy() would copy that oversized string into a fixed-size stack buffer — overflowing it.

Here's a simplified attack scenario:

// Somewhere in auth.c (simplified)
void handle_auth(const char *received_username) {
    char local_buf[64];  // Fixed-size stack buffer
    strcpy(local_buf, received_username);  // 💥 No bounds check!
    // ... process authentication
}

If received_username is 200 bytes long, strcpy() writes 200 bytes into a 64-byte buffer. The extra 136 bytes overwrite whatever sits adjacent in memory — which on the stack typically means the saved return address.

By carefully crafting the overflow payload, an attacker can:

  1. Overwrite the return address with an address of their choosing
  2. Redirect execution to attacker-controlled code (or existing code via ROP chains)
  3. Achieve remote code execution — potentially with kernel-level privileges

Real-World Impact

  • Remote Code Execution (RCE): Network-facing callers like the PPP handler mean this could be triggered remotely without authentication
  • Privilege Escalation: Kernel-mode callers mean successful exploitation could yield ring-0 access
  • Denial of Service: Even without a working exploit, a crash from stack corruption takes down the entire system
  • Authentication Bypass: Stack corruption in the auth handler could corrupt authentication state variables

The Fix

What Changed

The fix replaces the unbounded strcpy() implementation with a size-aware variant that enforces a maximum copy length. The corrected implementation follows the pattern established by strncpy() and the safer strlcpy() (from BSD):

// FIXED: Bounds-checked implementation
size_t strlcpy(char *dest, const char *src, size_t dest_size) {
    size_t src_len = strlen(src);

    if (dest_size > 0) {
        size_t copy_len = (src_len >= dest_size) ? dest_size - 1 : src_len;
        memcpy(dest, src, copy_len);
        dest[copy_len] = '\0';  // Always null-terminate
    }

    return src_len;  // Return full source length so callers can detect truncation
}

Key Security Improvements

Property Old strcpy() Fixed strlcpy()
Bounds checking ❌ None ✅ Enforced via dest_size
Null termination ⚠️ Only if src fits ✅ Always guaranteed
Truncation detection ❌ Impossible ✅ Via return value
Caller awareness ❌ No size info ✅ Explicit size required

Why strlcpy() Over strncpy()?

You might wonder: why not just use strncpy()? The answer is subtle but important:

// strncpy() has its own footgun:
strncpy(dest, src, n);
// If src is exactly n bytes, dest is NOT null-terminated!
// This can lead to information disclosure or further memory corruption.

strncpy() does not guarantee null termination when the source fills the entire buffer. strlcpy() always null-terminates the destination (as long as dest_size > 0), and returns the total length of the source string, allowing callers to detect when truncation occurred:

// Caller can now detect truncation:
if (strlcpy(dest, src, sizeof(dest)) >= sizeof(dest)) {
    // Truncation occurred — handle the error!
    log_error("String truncated, possible attack attempt");
    return -1;
}

Prevention & Best Practices

1. Never Use Unbounded String Functions

Treat these C standard library functions as banned in security-sensitive code:

// ❌ Banned: unbounded, no size parameter
strcpy(dest, src);
strcat(dest, src);
sprintf(buf, fmt, ...);
gets(buf);

// ✅ Safe alternatives
strlcpy(dest, src, sizeof(dest));     // BSD/OpenBSD
strlcat(dest, src, sizeof(dest));     // BSD/OpenBSD
snprintf(buf, sizeof(buf), fmt, ...); // C99+
fgets(buf, sizeof(buf), stdin);       // For input

2. Always Pass and Check Buffer Sizes

Any function that writes into a buffer should accept an explicit size parameter:

// ❌ Bad: caller has no way to enforce limits
void process_username(char *dest, const char *src);

// ✅ Good: size is explicit and enforced
void process_username(char *dest, size_t dest_size, const char *src);

3. Enable Compiler Protections

Modern compilers offer multiple layers of protection against buffer overflows:

# Add to your CFLAGS:
CFLAGS += -D_FORTIFY_SOURCE=2      # Runtime buffer overflow detection
CFLAGS += -fstack-protector-strong  # Stack canaries
CFLAGS += -fstack-clash-protection  # Stack clash protection
CFLAGS += -Wformat -Wformat-security # Format string warnings
LDFLAGS += -Wl,-z,relro,-z,now      # Full RELRO (Linux)

4. Use Static Analysis Tools

Several tools can catch unbounded copies before they reach production:

Tool Type Catches
Clang Static Analyzer Static Unbounded copies, use-after-free
Coverity Static Buffer overflows, memory errors
AddressSanitizer (ASan) Dynamic Heap/stack overflows at runtime
Valgrind Dynamic Memory errors, invalid reads/writes
CodeQL Semantic Data flow analysis, taint tracking

Running ASan during testing is particularly effective:

# Compile with AddressSanitizer
gcc -fsanitize=address -g -o myprogram myprogram.c

# Any buffer overflow will now cause an immediate, descriptive crash
# instead of silent memory corruption

5. Conduct Threat Modeling for System-Wide Utilities

The scope of this vulnerability was amplified because the vulnerable function was a system-wide primitive. When writing or reviewing foundational utilities (string libraries, memory allocators, logging frameworks), apply heightened scrutiny:

  • Who calls this? Map all callers and their trust boundaries
  • What input can reach this? Trace data flows from untrusted sources
  • What's the blast radius? A bug in a utility called 500 times is 500 bugs

6. Relevant Security Standards

  • CWE-121: Stack-based Buffer Overflow
  • CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
  • CERT C Coding Standard: STR31-C — Guarantee that storage for strings has sufficient space for character data and the null terminator
  • OWASP: A03:2021 — Injection (which encompasses memory corruption in native code)
  • MISRA C 2012: Rule 21.6 — The Standard Library input/output functions shall not be used

Conclusion

This vulnerability is a powerful reminder that foundational code deserves the most scrutiny. A single unsafe strcpy() implementation, tucked away in a string library, became a system-wide attack surface spanning network authentication, file path handling, and packet processing.

The fix is conceptually simple — add a size parameter, enforce bounds, always null-terminate — but the impact of getting it right is enormous. By replacing the unbounded copy with a safe strlcpy() pattern, every caller in the codebase inherits the protection automatically.

Key Takeaways

  • Never use strcpy(), strcat(), or gets() in new code — they have no place in modern C
  • Always pass explicit buffer sizes to functions that write strings
  • Enable compiler mitigations (stack canaries, FORTIFY_SOURCE, ASan in testing)
  • Scrutinize system-wide primitives — bugs there multiply across every caller
  • Use static analysis as a routine part of your CI/CD pipeline

Memory safety vulnerabilities like this one are preventable. The tools, techniques, and safer APIs exist — it's a matter of building the habits and processes to use them consistently.


This vulnerability was identified and fixed by automated security scanning. Continuous security scanning helps catch critical issues before they reach production.

References: CWE-121 | CERT STR31-C | OpenBSD strlcpy

Frequently Asked Questions

What is a buffer overflow in strcpy()?

A buffer overflow occurs when strcpy() copies a string longer than the destination buffer can hold, overwriting adjacent memory and potentially corrupting the stack, heap, or code pointers.

How do you prevent buffer overflow in C?

Use bounded string functions like strncpy(), strlcpy(), or memcpy() with explicit length checks; validate all input sizes before copying; use compiler protections like stack canaries; and employ static analysis tools to catch unsafe patterns.

What CWE is buffer overflow in strcpy()?

CWE-120 (Buffer Copy without Checking Size of Input) and CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer) are the primary classifications.

Is input validation alone enough to prevent strcpy() overflow?

No—input validation helps, but the root cause is the unbounded copy operation itself. You must use length-limited functions or enforce strict buffer size checks at the copy site.

Can static analysis detect strcpy() buffer overflow?

Yes—tools like Clang Static Analyzer, Coverity, and Semgrep can detect strcpy() calls and flag them as high-risk, especially when the destination buffer size is unknown or user-controlled input is involved.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #8

Related Articles

critical

How Buffer Overflow via strcpy() Happens in C++ XML Parsers and How to Fix It

A critical buffer overflow vulnerability was discovered in `buildroot-external/package/libxmlparser/xmlParser.cpp`, where the `toXMLString` function used `_tcscpy()` to write XML escape sequences into a destination buffer without any bounds checking. An attacker supplying a crafted XML document could overflow the buffer and potentially execute arbitrary code. The fix replaces all five unsafe `_tcscpy()` calls with `memcpy()` calls that copy only the exact number of bytes required for each escape

high

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

A high-severity buffer overflow vulnerability was discovered in `tools/claude-vscode-wrapper.c`, where an unbounded `strcpy()` call copied a file path into a fixed-size `MAX_PATH` buffer without any size validation. The fix replaces `strcpy()` with `snprintf()` and swaps `malloc()` for `calloc()`, ensuring both string operations and memory allocation are bounds-safe and zero-initialized.

critical

How Heap Buffer Overflows Happen in C++ ZIP Extraction and How to Fix Them

A critical heap buffer overflow vulnerability was discovered in `TKLiveSync/unzip.cpp`, where ZIP archive entry names were copied into a `PATH_MAX`-sized heap buffer using `strcpy()` without any length validation. Since the ZIP specification allows entry names up to 65,535 bytes — far exceeding typical `PATH_MAX` values of 1,024 to 4,096 bytes — a crafted archive could overflow the buffer and corrupt heap memory. The fix replaces the unsafe `strcpy`/`dirname` pattern with `std::string` operation

medium

How Integer Overflow happens in C++ image processing and how to fix it

A signed integer overflow in OpenCV's `bilateralFilter.cpp` allowed the buffer size calculation `cal_width * cal_height * cn` to wrap around to a small or negative value, causing `padding.resize()` to allocate far less memory than needed. Subsequent `memcpy` operations would then write beyond the allocated buffer, creating a heap corruption primitive. The fix is a single targeted cast to `size_t` that promotes the multiplication to unsigned 64-bit arithmetic before any overflow can occur.

critical

How Stack Buffer Overflows Happen in C with sprintf() and How to Fix Them

A critical stack buffer overflow was discovered in `libuv/Learn-libuv/docs/code/tty-gravity/main.c` where `sprintf()` wrote ANSI escape sequences and user-controlled variables into a fixed 500-byte buffer without any bounds checking. An attacker controlling the `pos`, `width`, or `message` variables could overflow the stack, overwrite return addresses, and potentially achieve arbitrary code execution. The fix replaces `sprintf()` with `snprintf()` and adds explicit length validation to ensure wr

high

How insecure string copy functions happen in C and how to fix them

A high-severity buffer overflow risk was discovered in `login/main.c` where `strcpy()` was used to copy the `HOME` environment variable into a fixed-size 512-byte buffer without any bounds checking. An attacker controlling the `HOME` environment variable could overflow `pwd_file_name`, potentially corrupting memory or hijacking execution. The fix replaces the two-step `strcpy`/`strcat` pattern with a single, bounds-safe `snprintf` call.