Back to Blog
critical SEVERITY8 min read

Stack Overflow in C: How strcpy and strcat Put Games at Risk

A critical buffer overflow vulnerability was discovered and patched in a shared C header file (common.h) used across an entire suite of games, where unbounded strcpy and strcat calls could allow attackers to overwrite stack memory and hijack program execution. The fix eliminates dangerous unbounded string operations, protecting every game binary that includes this shared header. Understanding this vulnerability is essential for any developer working with C/C++ string handling.

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

Answer Summary

This is a buffer overflow vulnerability (CWE-120) in C caused by using unbounded string functions like strcpy() and strcat() that write beyond allocated buffer boundaries. The fix replaces these dangerous calls with bounded alternatives like strncpy() and strncat(), which accept a maximum length parameter to prevent writing past the buffer's end. This pattern is critical for any C/C++ code handling user-controlled strings.

Vulnerability at a Glance

cweCWE-120
fixReplace with bounded functions strncpy() and strncat() with explicit size limits
riskRemote code execution via stack memory corruption
languageC
root causeUnbounded strcpy() and strcat() calls without length checks
vulnerabilityBuffer Overflow (Stack-based)

Stack Overflow in C: How strcpy and strcat Put an Entire Game Suite at Risk

Introduction

There's a reason security professionals have been warning about strcpy for decades — and a recently patched vulnerability in a shared game engine header file is a perfect reminder of why that warning still matters today.

A critical-severity buffer overflow was identified and fixed in common.h, a shared header file used across an entire suite of games. The vulnerability stemmed from three unbounded string operations that blindly copied caller-supplied data into fixed-size buffers with zero length validation. Because this header is shared, a single vulnerable file put every game binary in the suite at risk simultaneously.

This is exactly the kind of vulnerability that appears in CVE databases, CTF challenges, and real-world exploits alike. Whether you're a seasoned systems programmer or just starting out with C, understanding how this class of bug works — and how to fix it — is foundational knowledge.


The Vulnerability Explained

What Is a Buffer Overflow?

A buffer overflow occurs when a program writes more data into a buffer (a fixed-size region of memory) than it was designed to hold. The excess data spills into adjacent memory, potentially overwriting critical values like saved return addresses, function pointers, or other variables.

In C, the standard library functions strcpy and strcat are notorious contributors to this class of bug because they perform no bounds checking whatsoever. They copy characters until they hit a null terminator (\0), regardless of whether the destination buffer has room.

The Vulnerable Code

The vulnerability was located at lines 41, 42, and 52 of common.h. Here's a simplified representation of what the dangerous code looked like:

// ❌ VULNERABLE CODE (before fix)

char tmp_path[256];  // Fixed-size buffer on the stack

// Line 41: Copies caller-supplied path with NO length check
strcpy(tmp_path, path);

// Line 42: Appends suffix to already potentially-overflowed buffer
strcat(tmp_path, ".XXXXXX");

// Line 52: Copies scanned name into fixed-size slot without length verification
strcpy(name_buff[location], scanned_name);

Let's break down each dangerous operation:

Problem 1: strcpy(tmp_path, path) — Line 41

tmp_path is a fixed-size buffer (e.g., 256 bytes). The path variable is caller-supplied, meaning it could be any length. If an attacker or even a legitimate user provides a path longer than 255 characters, strcpy will happily write past the end of tmp_path, corrupting whatever lives next to it in memory.

Problem 2: strcat(tmp_path, ".XXXXXX") — Line 42

Even if path just barely fits into tmp_path, appending .XXXXXX (7 more characters) could push it over the edge. This is a compound overflow — the buffer was already at risk, and this line makes it worse.

Problem 3: strcpy(name_buff[location], scanned_name) — Line 52

scanned_name comes from scanning/parsing input. If the scanned name is longer than the slot in name_buff, the overflow will corrupt adjacent entries in the buffer array or other stack/heap data nearby.

Why Is This Classified as CWE-120?

This vulnerability maps to CWE-120: Buffer Copy without Checking Size of Input — also nicknamed "Classic Buffer Overflow." It's one of the oldest and most well-documented vulnerability classes in software security, yet it continues to appear in production code.

The Multiplier Effect: A Shared Header File

What elevates this from a bad bug to a critical one is the architectural detail: common.h is a shared header included by every game in the suite. This means:

  • One vulnerable file = every game binary is vulnerable
  • Patching one file = every game binary is protected
  • An attacker only needs to find one game with this code path exposed

This is a classic example of how shared code amplifies both risk and impact.

How Could an Attacker Exploit This?

Consider a scenario where a game reads a file path from a configuration file, user input, or a network source, then passes it to a function in common.h:

Normal path:    /home/user/saves/game1    (28 chars  fits fine)
Malicious path: /home/user/saves/[300 'A' characters][shellcode address]

On a system without modern mitigations, this overflow could overwrite the saved return address on the stack. When the current function returns, instead of jumping back to legitimate code, the CPU jumps to an address controlled by the attacker — enabling arbitrary code execution.

Even with modern protections like stack canaries, ASLR, and NX bits, buffer overflows can still cause:
- Program crashes / Denial of Service
- Corruption of adjacent variables (logic bugs, privilege escalation)
- Information leaks when combined with other vulnerabilities


The Fix

The fix replaces all unbounded string operations with their length-aware, bounds-checking counterparts. Here's what the corrected code looks like:

// ✅ FIXED CODE (after patch)

#define MAX_PATH_LEN 256
#define MAX_NAME_LEN 64

char tmp_path[MAX_PATH_LEN];

// Line 41: Use strncpy — limits copy to buffer capacity
strncpy(tmp_path, path, sizeof(tmp_path) - 1);
tmp_path[sizeof(tmp_path) - 1] = '\0';  // Ensure null termination

// Line 42: Use strncat — limits append to remaining space
strncat(tmp_path, ".XXXXXX", sizeof(tmp_path) - strlen(tmp_path) - 1);

// Line 52: Validate length before copy
strncpy(name_buff[location], scanned_name, MAX_NAME_LEN - 1);
name_buff[location][MAX_NAME_LEN - 1] = '\0';  // Ensure null termination

Note: Even strncpy has quirks — it doesn't guarantee null termination if the source is longer than n. The explicit null termination on the line after each strncpy call is not optional; it's a required safety measure.

Why This Fix Works

Before After Why It's Better
strcpy(dst, src) strncpy(dst, src, sizeof(dst)-1) Limits copy to buffer size
strcat(dst, suffix) strncat(dst, suffix, space_remaining) Limits append to available space
No null termination guarantee Explicit dst[n] = '\0' Prevents unterminated string bugs

The key insight is simple: every write to a fixed-size buffer must be bounded by the size of that buffer.

An Even Better Alternative: Use Safer Functions

For new code, consider using platform-specific safer alternatives:

// POSIX systems: snprintf for building strings safely
snprintf(tmp_path, sizeof(tmp_path), "%s.XXXXXX", path);

// Windows: use StringCchCopy / StringCchCat
// C11: use strcpy_s / strcat_s (Annex K)
// C++: use std::string and avoid raw buffers entirely

snprintf is particularly elegant for path construction because it handles both the copy and the append in a single, bounds-safe operation.


Prevention & Best Practices

1. Treat strcpy and strcat as Red Flags

Many organizations and style guides ban strcpy and strcat outright in new code. Configure your compiler or linter to warn on their use:

# GCC/Clang: enable fortify source
gcc -D_FORTIFY_SOURCE=2 -O2 your_code.c

# This causes runtime checks on string functions
# and compile-time warnings for detectable overflows

2. Use Static Analysis Tools

These tools can catch buffer overflow risks automatically:

Running any of these tools would have flagged the strcpy/strcat calls in common.h immediately.

3. Enable Compiler Hardening Flags

# Add to your Makefile or CMakeLists.txt
CFLAGS += -fstack-protector-strong    # Stack canaries
CFLAGS += -D_FORTIFY_SOURCE=2         # Runtime buffer checks
CFLAGS += -Wformat -Wformat-security  # Format string warnings
LDFLAGS += -Wl,-z,relro,-z,now        # RELRO protection

These don't replace fixing the bug, but they add layers of defense that make exploitation harder.

4. Apply the Principle of Input Validation

Any time your code accepts external input (file paths, usernames, network data), validate it before using it:

// Always validate input length before processing
if (strlen(path) >= MAX_PATH_LEN) {
    fprintf(stderr, "Error: path too long\n");
    return ERROR_INVALID_INPUT;
}
// Now safe to proceed
strncpy(tmp_path, path, sizeof(tmp_path) - 1);

5. Consider Moving to Memory-Safe Languages

For new projects, languages like Rust, Go, and modern C++ with smart pointers eliminate entire classes of memory safety bugs by design. The irony in this case? The project already had Rust in its dependency tree (via src-tauri/Cargo.lock) — Rust's borrow checker and string types would have made this vulnerability impossible in idiomatic code.

6. Know the Relevant Standards


Key Takeaways

This vulnerability is a textbook example of why C string handling demands constant vigilance:

  1. strcpy and strcat are dangerous — they have no awareness of destination buffer size and should be replaced with bounds-checking alternatives in all new and maintained code.

  2. Shared code multiplies risk — a single vulnerable header file put an entire game suite at risk. Audit your shared libraries and headers with extra scrutiny.

  3. The fix is straightforward — replacing unbounded operations with strncpy/strncat/snprintf and explicitly null-terminating buffers closes the vulnerability cleanly.

  4. Defense in depth matters — compiler hardening flags, static analysis, and input validation all work together to reduce the attack surface.

  5. Automated scanning works — this vulnerability was caught by an automated multi-agent AI scanner, demonstrating the value of integrating security tooling into your CI/CD pipeline.

Buffer overflows have been in the OWASP Top 10 and CWE Top 25 for years — not because they're exotic, but because they keep appearing in production code. The best time to fix a buffer overflow is before it ships. The second best time is right now.


This post was generated as part of an automated security fix workflow by OrbisAI Security. Vulnerability ID: V-001 | Severity: Critical | CWE-120.

Frequently Asked Questions

What is a buffer overflow vulnerability?

A buffer overflow occurs when a program writes data beyond the boundaries of allocated memory, potentially overwriting adjacent memory including return addresses, function pointers, or other critical data structures.

How do you prevent buffer overflow in C?

Use bounded string functions (strncpy, strncat, snprintf), validate input lengths before copying, use compiler protections like stack canaries, and consider safer alternatives like strlcpy where available.

What CWE is buffer overflow?

Buffer overflow is classified under CWE-120 (Buffer Copy without Checking Size of Input) and related entries like CWE-121 (Stack-based Buffer Overflow) and CWE-122 (Heap-based Buffer Overflow).

Is using strncpy enough to prevent buffer overflow?

strncpy helps but requires careful use—you must ensure the size parameter is correct, account for null termination, and handle truncation appropriately. It doesn't null-terminate if the source is longer than the size.

Can static analysis detect buffer overflow?

Yes, static analysis tools can detect many buffer overflow patterns, especially obvious cases of unbounded strcpy/strcat usage. However, complex data flows may require dynamic analysis or manual review.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #54

Related Articles

high

How integer overflow in malloc happens in C libregexp and how to fix it

A high-severity integer overflow vulnerability was discovered in QuickJS's libregexp.c where multiplication to compute allocation size could wrap around, causing a heap overflow. The fix replaces the unsafe `malloc(sizeof(capture[0]) * lre_get_alloc_count(bc))` pattern with `calloc(lre_get_alloc_count(bc), sizeof(capture[0]))`, which safely handles the multiplication internally and prevents exploitation.

high

How unbounded input size denial-of-service happens in C lexer functions and how to fix it

A high-severity denial-of-service vulnerability was discovered in the PH7 lexer where the `PH7_TokenizePHP()` function accepted arbitrarily large input sizes without validation. An attacker could submit gigabyte-scale PHP code, causing proportional CPU and memory exhaustion. The fix introduces a configurable input size cap enforced before lexer processing begins.

critical

How kernel stack buffer overflow happens in C vsprintf() and how to fix it

A critical stack buffer overflow vulnerability was discovered in `sys/kern/debug.c` where the kernel's `printf()` function called a custom `vsprintf()` implementation without any length constraint on the output buffer `db_msg`. By replacing the unbounded `vsprintf()` call with a size-aware `vsnprintf()` implementation, the fix prevents crafted format strings or oversized arguments from overwriting kernel stack memory, closing a path to arbitrary kernel code execution.

high

NULL Pointer Dereference in ESP8266 user_interface.c wifi_station_set_default_hostname()

A critical NULL pointer dereference vulnerability in the ESP8266 firmware's `user_interface.c` allowed attackers to crash devices by exhausting the limited 80KB heap memory. The `wifi_station_set_default_hostname()` function's `os_malloc` call lacked a proper NULL guard, causing `ets_sprintf` to write to address 0 when allocation failed. The fix corrected a logic inversion in the NULL check condition.

high

Thread-Safe Tokenization: Fixing strtok() Reentrancy in Game Script Parsing

A high-severity vulnerability was discovered in `lvl_script_commands.c` where the use of the non-reentrant `strtok()` function during level script parsing created conditions for memory corruption and potential arbitrary code execution. The fix replaces all `strtok()` calls with the thread-safe `strtok_r()` variant, eliminating shared global state that could be exploited through maliciously crafted level files. This change is part of a broader effort to harden the game's script parsing pipeline a

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