Back to Blog
critical SEVERITY6 min read

How buffer overflow in strcpy() happens in C bin2coff tool and how to fix it

A critical buffer overflow vulnerability was discovered in `tools/bin2coff.c` where multiple `strcpy()` operations copied user-controlled label strings into fixed-size buffers without bounds checking. An attacker could provide maliciously long labels to overflow destination buffers and corrupt adjacent memory structures, potentially leading to arbitrary code execution. The fix replaced unsafe string operations with bounded alternatives like `strlcpy()` and `snprintf()`.

O
By Orbis AppSec
Published June 29, 2026Reviewed June 29, 2026

Answer Summary

This is a **CWE-120 buffer overflow** vulnerability in C code within the bin2coff tool. The vulnerability occurs when `strcpy()` copies user-controlled label strings into fixed-size buffers (like 8-byte ShortName fields) without length validation. An attacker providing labels longer than buffer capacity can overflow into adjacent memory, corrupting symbol table structures and potentially achieving code execution. The fix replaces `strcpy()` with bounded functions like `strlcpy()` and `snprintf()` that enforce maximum copy lengths, preventing buffer overruns regardless of input size.

Vulnerability at a Glance

cweCWE-120 (Buffer Copy without Checking Size of Input)
fixReplace strcpy() with strlcpy() and use snprintf() for bounded string operations
riskMemory corruption leading to potential arbitrary code execution when processing malicious binary labels
languageC
root causestrcpy() copies user-controlled strings into fixed 8-byte ShortName buffers without length validation
vulnerabilityBuffer overflow via unbounded strcpy() in COFF symbol table generation

Introduction

In a binary-to-COFF conversion tool at tools/bin2coff.c:393, we discovered a critical buffer overflow vulnerability that could allow attackers to corrupt memory and potentially execute arbitrary code. The vulnerability stemmed from using strcpy() to copy user-controlled label strings into fixed-size buffers within COFF symbol table structures—specifically the 8-byte ShortName field—without any bounds checking.

This isn't just a theoretical concern. The bin2coff tool processes binary files and generates COFF (Common Object File Format) object files, meaning any malicious input file with excessively long labels could trigger the overflow. Since this tool operates on user-provided input files, the attack surface is significant: an attacker simply needs to craft a binary file with labels exceeding buffer capacity to trigger memory corruption.

The vulnerability affects production code, not test files, making it a high-priority security issue that required immediate remediation.

The Vulnerability Explained

The core problem lies in how bin2coff.c handles string operations when building COFF symbol tables. Here's the vulnerable pattern:

// Vulnerable code in tools/bin2coff.c
struct syment {
    union {
        char ShortName[8];  // Fixed 8-byte buffer
        struct {
            uint32_t Zeroes;
            uint32_t Offset;
        } Name;
    } N;
    // ... other fields
};

// Somewhere in the code around line 393:
strcpy(symbol->N.ShortName, user_label);  // No bounds checking!

The ShortName field is exactly 8 bytes, but strcpy() will copy the entire source string regardless of destination capacity. When user_label contains more than 7 characters (leaving room for null terminator), the overflow begins.

Attack Scenario:

  1. An attacker creates a malicious binary file with an embedded label like "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" (40+ characters)
  2. The victim runs: bin2coff malicious.bin output.coff
  3. The tool calls strcpy(symbol->N.ShortName, "AAAA...")
  4. The 8-byte buffer overflows, overwriting adjacent memory in the symbol table structure
  5. Depending on what follows in memory (possibly function pointers, return addresses, or other critical data), the attacker could:
    - Corrupt the string table offset, causing crashes or information disclosure
    - Overwrite control flow data for code execution
    - Trigger undefined behavior leading to exploitable conditions

Real-World Impact:

For developers using bin2coff in build pipelines, this means:
- A malicious binary from an untrusted source could compromise the build system
- Automated toolchains processing user-submitted binaries become attack vectors
- Memory corruption could lead to non-deterministic build failures or worse—silent corruption of output files

The vulnerability was confirmed exploitable by the multi_agent_ai scanner using rule V-001, which specifically detects unbounded string copy operations with user-controlled input.

The Fix

The fix replaces all unsafe strcpy() calls with bounded alternatives that enforce maximum copy lengths. Here's what changed:

Before (Vulnerable):

strcpy(symbol->N.ShortName, label);

After (Secure):

strlcpy(symbol->N.ShortName, label, sizeof(symbol->N.ShortName));

The key improvements:

  1. strlcpy() enforces bounds: The third parameter sizeof(symbol->N.ShortName) (8 bytes) ensures no more than 7 characters are copied, always leaving room for null termination
  2. Guaranteed null-termination: Unlike strncpy(), strlcpy() always null-terminates the destination, preventing string handling bugs downstream
  3. Truncation handling: If the source exceeds capacity, strlcpy() truncates safely rather than overflowing

For cases involving formatted strings, the fix uses snprintf():

Before:

sprintf(buffer, "_%s", label);

After:

snprintf(buffer, sizeof(buffer), "_%s", label);

This ensures formatted output respects buffer boundaries regardless of input length.

Why This Works:

The bounded functions create a security invariant: "Label length must not exceed destination buffer capacity." This property holds even under adversarial input. The regression test included in the PR validates this by testing:
- Short valid input: "A" (1 char)
- Boundary case: 40 characters
- Exploit payload: 256 characters

After the fix, all cases complete without memory corruption signals (SIGSEGV/SIGABRT), confirming the security boundary is maintained.

Prevention & Best Practices

1. Ban Unsafe String Functions

Add compiler warnings or static analysis rules to flag:
- strcpy() → use strlcpy() or strncpy() with manual null termination
- strcat() → use strlcat() or strncat()
- sprintf() → use snprintf()
- gets() → use fgets()

Enable -Wstringop-overflow and -Wformat-overflow in GCC/Clang to catch these at compile time.

2. Always Validate Buffer Sizes

Before any copy operation:

if (strlen(source) >= sizeof(dest)) {
    // Handle error: truncate, reject, or allocate larger buffer
}
strlcpy(dest, source, sizeof(dest));

3. Use Static Analysis

Tools like Semgrep can detect this pattern:

rules:
  - id: unsafe-strcpy
    pattern: strcpy($DEST, $SRC)
    message: "Use strlcpy() or strncpy() instead of strcpy()"
    severity: ERROR

4. Enable Memory Safety Protections

Compile with:
- Stack canaries (-fstack-protector-strong)
- ASLR (Address Space Layout Randomization)
- DEP/NX (non-executable stack)
- FORTIFY_SOURCE (-D_FORTIFY_SOURCE=2)

These won't prevent buffer overflows but make exploitation harder.

5. Adopt Secure Coding Standards

Follow CERT C guidelines:
- STR31-C: Guarantee sufficient storage for strings
- STR38-C: Use bounded string functions

6. Test with Adversarial Input

The regression test demonstrates best practices:
- Test valid input (1 char)
- Test boundary conditions (exactly at limit)
- Test exploit payloads (far exceeding limit)
- Assert no crashes or memory corruption signals

Key Takeaways

  • Never use strcpy() with user-controlled input in production code—the bin2coff tool processed user-provided binary files, making every label a potential attack vector
  • The 8-byte ShortName field in COFF symbol tables requires special handling—always use strlcpy(dest, src, 8) when populating this structure
  • Bounded functions like strlcpy() and snprintf() are not just best practices—they're essential security controls that prevent an entire class of memory corruption vulnerabilities
  • Static analysis tools can catch these issues before they reach production—the multi_agent_ai scanner flagged this with rule V-001, enabling automated detection
  • Security-focused regression tests validate invariants under adversarial input—the included test ensures the fix prevents crashes even with 256-character payloads

How Orbis AppSec Detected This

  • Source: User-controlled label strings from input binary files processed by the bin2coff tool
  • Sink: strcpy() call at tools/bin2coff.c:393 copying into fixed 8-byte ShortName buffer in COFF symbol table structure
  • Missing control: No bounds checking or length validation before string copy operation
  • CWE: CWE-120 (Buffer Copy without Checking Size of Input)
  • Fix: Replaced strcpy() with strlcpy() and sprintf() with snprintf(), enforcing buffer size limits via sizeof() parameters

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

This buffer overflow in bin2coff.c demonstrates why C's legacy string functions remain one of the most persistent sources of security vulnerabilities. By replacing unbounded strcpy() operations with bounded alternatives like strlcpy() and snprintf(), we eliminate an entire class of memory corruption bugs.

The fix is straightforward—change one function call—but the security impact is profound. Memory safety isn't just about preventing crashes; it's about maintaining the integrity of your program's control flow and data structures under adversarial conditions.

As developers, we must treat every string operation as a potential security boundary. Use bounded functions, validate input lengths, enable compiler warnings, and test with adversarial payloads. These practices transform security from a reactive measure into a proactive engineering discipline.

References

Frequently Asked Questions

What is buffer overflow in strcpy()?

Buffer overflow via strcpy() occurs when this C function copies a source string to a destination buffer without checking if the source exceeds the destination's capacity, allowing data to overwrite adjacent memory regions and corrupt program state.

How do you prevent buffer overflow in C?

Use bounded string functions like strncpy(), strlcpy(), or snprintf() that accept maximum length parameters. Always validate input lengths before copying, allocate sufficient buffer space, and enable compiler protections like stack canaries and ASLR.

What CWE is buffer overflow?

Buffer overflow is classified as CWE-120 (Buffer Copy without Checking Size of Input) or CWE-121 (Stack-based Buffer Overflow) depending on memory location. It's part of the broader CWE-119 (Improper Restriction of Operations within Memory Bounds) category.

Is using strncpy() enough to prevent buffer overflow?

Not always. While strncpy() limits copy length, it doesn't guarantee null-termination if the source fills the buffer completely. Use strlcpy() when available (guarantees null-termination) or manually add a null terminator after strncpy(). Also validate that destination size accounts for the null terminator.

Can static analysis detect buffer overflow?

Yes, modern static analysis tools can detect many buffer overflow patterns by tracking buffer sizes and string operation bounds. Tools like Coverity, CodeQL, Semgrep, and compiler warnings (-Wstringop-overflow) flag unsafe strcpy() usage and recommend bounded alternatives.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #7

Related Articles

critical

How buffer overflow happens in C dlldbg.c sprintf() and how to fix it

A classic buffer overflow vulnerability was discovered in `bld/pbide/dlldbg/dlldbg.c` at line 80, where an unbounded `sprintf()` call wrote a user-influenced `dllName` string into a fixed-size `fmtBuffer` without any length checking. An attacker supplying a maliciously crafted DLL name could overflow the buffer, overwrite adjacent memory, and potentially achieve arbitrary code execution. The fix replaces `sprintf()` with `snprintf()`, passing `sizeof(fmtBuffer)` as an explicit bound to ensure th

critical

How buffer overflow in handle_interlink_event() happens in C terminal event handling and how to fix it

A critical buffer overflow vulnerability was discovered in `src/terminal/event.c` at line 250, where `memcpy()` blindly copied `info->cwd` into a fixed-size `term->cwd` buffer without verifying the source string's actual length. An attacker who could supply a crafted working directory path longer than `MAX_CWD_LEN` could corrupt adjacent heap memory, potentially leading to code execution. The fix replaces the unsafe `memcpy()` call with `safe_strncpy()`, which enforces the destination buffer bou

high

How buffer overflow happens in C string operations with strcpy/strncpy and how to fix it

A critical buffer overflow vulnerability in `src/pomoc.c` was discovered where `strncpy()` was used unsafely to copy a socket path into a fixed-size buffer. The fix replaces the dangerous string copy with `snprintf()`, which provides automatic bounds checking and null-termination. This prevents attackers from exploiting the CLI tool through oversized input arguments.

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.

critical

How buffer overflow via sprintf() happens in C++ settings parsing and how to fix it

A critical buffer overflow vulnerability was discovered in `app/src/main/cpp/samp/settings.cpp` where `sprintf()` writes to a fixed 127-byte buffer (`char buff[0x7F]`) without bounds checking. If the `g_pszStorage` global variable contains a string longer than ~107 bytes, the formatted output exceeds the buffer, enabling stack corruption. The fix replaces `sprintf()` with `snprintf()` using `sizeof(buff)` to guarantee writes never exceed the declared buffer length.

critical

How integer overflow in js_realloc_array() happens in C QuickJS and how to fix it

A confirmed integer overflow vulnerability in QuickJS's `js_realloc_array()` function could allow attackers to trigger heap under-allocation by supplying crafted JavaScript input. The fix adds a pre-multiplication bounds check that prevents `new_size * elem_size` from wrapping around `SIZE_MAX`. This closes a critical code execution path that existed in the production JavaScript engine.