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 orbisai0security
May 14, 2026
#c-programming#buffer-overflow#cwe-120#memory-safety#secure-coding#game-security#stack-overflow

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.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #54

Related Articles

critical

Stack Buffer Overflow in tpl.c: How strcpy Without Bounds Checking Enables Full Control Flow Hijacking

A critical stack buffer overflow vulnerability was discovered and patched in tpl.c, where command-line arguments were copied into fixed-size stack buffers using strcpy without any length validation. An attacker supplying an oversized argument could overwrite the saved return address on the stack, achieving complete control flow hijacking. The fix eliminates this classic but devastatingly effective vulnerability class that has plagued C programs for decades.

critical

Heap Buffer Overflow in tzsp_forwarder.c: When Packets Attack

A critical heap buffer overflow vulnerability (CWE-120) was discovered and patched in `contrib/tzsp_forwarder.c`, where an attacker-controlled `caplen` value from a crafted network packet could overwrite adjacent heap memory structures. This class of vulnerability can lead to remote code execution, process crashes, or sensitive data disclosure. The fix introduces proper bounds validation before the dangerous `memcpy` operation, closing the door on this attack vector.

critical

Heap Buffer Overflow in HAL Filter: How Unvalidated memcpy Sizes Can Sink Your App

A critical heap buffer overflow vulnerability was discovered and patched in the ndsrvp HAL filter routines, where multiple `memcpy` calls used computed sizes derived from image dimensions without validating they fit within destination buffers. An attacker supplying a crafted image could exploit this to corrupt heap memory, potentially achieving arbitrary code execution. This post breaks down how the vulnerability works, how it was fixed, and what developers can do to prevent similar issues.