Back to Blog
high SEVERITY7 min read

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.

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

Answer Summary

This is a buffer overflow vulnerability (CWE-120) in C caused by unsafe use of `strncpy()` in `src/pomoc.c` line 25. The function copies a socket path into a fixed-size buffer without guaranteeing null-termination, allowing potential memory corruption. The fix replaces `strncpy()` with `snprintf()`, which enforces bounds checking and automatic null-termination, eliminating the overflow risk.

Vulnerability at a Glance

cweCWE-120 (Buffer Copy without Checking Size of Input)
fixReplace strncpy() with snprintf() for automatic bounds checking and null-termination
riskLocal code execution or denial of service via crafted command-line arguments
languageC
root causestrncpy() does not guarantee null-termination; missing explicit null-byte assignment
vulnerabilityBuffer overflow via unsafe strncpy() in socket path handling

How Buffer Overflow Happens in C String Operations with strcpy/strncpy and How to Fix It

The Incident

In the pomoc.c file—a local CLI tool that communicates with a daemon via Unix sockets—a critical buffer overflow vulnerability was discovered at line 25. The code used strncpy() to copy a socket path into a fixed-size buffer without ensuring proper null-termination. This created a pathway for attackers to exploit the tool through oversized input arguments, potentially leading to code execution or denial of service.

// VULNERABLE CODE (line 25)
strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);

The vulnerability was flagged as HIGH severity by Semgrep's static analysis, marked as "likely exploitable," and required immediate remediation in production code.


The Vulnerability Explained

Understanding the Problem

The vulnerability centers on how C handles string copying. Unlike higher-level languages where strings are length-prefixed or automatically managed, C relies on null-terminated character arrays. This design creates two critical failure modes with strcpy() and strncpy():

  1. strcpy() has NO size checking — it blindly copies until it finds a null byte, regardless of buffer size
  2. strncpy() doesn't guarantee null-termination — if the source string is longer than the specified size, the destination buffer is left without a null terminator

The Specific Vulnerability in pomoc.c

In the vulnerable code:

struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);

The addr.sun_path field in struct sockaddr_un is typically 108 bytes. The code attempts to be safe by using strncpy() with sizeof(addr.sun_path) - 1 as the size limit. However, strncpy() will NOT automatically null-terminate the string if it reaches the size limit. This means:

  • If SOCKET_PATH is exactly 108 characters or longer, strncpy() copies 107 bytes and leaves addr.sun_path[107] as garbage
  • The subsequent socket operations may read past the intended string boundary, causing undefined behavior
  • An attacker controlling SOCKET_PATH (via environment variables, configuration files, or other input vectors) could craft an oversized string to corrupt adjacent memory

Attack Scenario

Consider this exploit scenario:

  1. Attacker sets an environment variable or modifies a config file to inject a malicious socket path
  2. The attacker crafts a path longer than 107 characters, e.g.:
    /tmp/socket_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
  3. strncpy() copies 107 bytes but doesn't null-terminate
  4. When the code later uses addr.sun_path (e.g., in connect() calls), the kernel or subsequent code reads beyond the intended boundary
  5. This could leak sensitive data from adjacent memory, crash the program, or potentially execute arbitrary code if the attacker controls the overflowed data

Real-World Impact

For this CLI tool:
- Denial of Service: Crafted input crashes the program, breaking automation or user workflows
- Information Disclosure: Buffer overflow can leak sensitive data from the stack or heap
- Local Privilege Escalation: If the tool runs with elevated privileges, an attacker could exploit this to gain code execution


The Fix

What Changed

The fix replaces the dangerous strncpy() call with snprintf(), which provides automatic bounds checking and guaranteed null-termination:

- strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", SOCKET_PATH);

Why This Works

snprintf() is fundamentally safer for this use case:

Aspect strncpy() snprintf()
Bounds Checking Manual (error-prone) Automatic
Null Termination NOT guaranteed Guaranteed
Truncation Handling Silent, leaves buffer dirty Silent, but buffer is valid
Return Value Number of bytes copied Number of bytes that would be written

With snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", SOCKET_PATH):
- The second argument (sizeof(addr.sun_path)) is the total buffer size, not size minus one
- snprintf() automatically writes a null terminator within that size
- If SOCKET_PATH exceeds 107 bytes, it's truncated safely to 107 bytes + null terminator
- The buffer is always valid and properly null-terminated

Verification

The PR included a regression test to ensure the fix holds:

START_TEST(test_buffer_reads_never_exceed_declared_length)
{
    const char *payloads[] = {
        "normal",                    // Valid input
        "A",                         // Boundary: single char
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"  // 100 chars
    };

    for (int i = 0; i < num_payloads; i++) {
        char dest[16] = {0};
        const char *src = payloads[i];

        snprintf(dest, sizeof(dest), "%s", src);  // Now using snprintf

        // Verify no buffer overflow occurred
        ck_assert_msg(strlen(dest) < sizeof(dest),
                     "Buffer overflow detected for payload: %s", src);
        ck_assert_msg(dest[sizeof(dest) - 1] == '\0',
                     "Buffer not properly terminated for payload: %s", src);
    }
}
END_TEST

This test confirms that even with 100-character payloads (far exceeding the buffer), the buffer remains properly bounded and null-terminated.


Prevention & Best Practices

1. Never Use strcpy() in Production Code

strcpy() has NO size checking whatsoever. It should be considered deprecated:

// ❌ NEVER DO THIS
strcpy(dest, src);  // No size limit - guaranteed overflow risk

2. Use snprintf() or strlcpy() Instead of strncpy()

Both are safer alternatives:

// ✅ GOOD - snprintf (portable, POSIX)
snprintf(dest, sizeof(dest), "%s", src);

// ✅ GOOD - strlcpy (BSD extension, widely available)
strlcpy(dest, src, sizeof(dest));

Preference: snprintf() is more portable across platforms (POSIX standard), while strlcpy() is cleaner but may not be available on all systems.

3. Always Pass the Full Buffer Size

When using snprintf(), pass sizeof(dest), not sizeof(dest) - 1:

// ✅ CORRECT - snprintf handles the null terminator
snprintf(dest, sizeof(dest), "%s", src);

// ❌ WRONG - unnecessary and confusing
snprintf(dest, sizeof(dest) - 1, "%s", src);

4. Enable Compiler Warnings

Use GCC/Clang flags to catch dangerous functions:

gcc -Wall -Wextra -Wformat -Wformat-security -Wstrict-overflow src/pomoc.c

Many compilers will warn about strcpy() and unsafe string operations.

5. Use Static Analysis Tools

Semgrep, Clang Static Analyzer, and Coverity automatically detect these patterns:

semgrep --config=p/security-audit src/pomoc.c

6. Validate Input Length Before Copying

For defense-in-depth, validate the source string length:

if (strlen(SOCKET_PATH) >= sizeof(addr.sun_path)) {
    fprintf(stderr, "Socket path too long\n");
    return -1;
}
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", SOCKET_PATH);

References to Standards

  • CWE-120: Buffer Copy without Checking Size of Input
  • CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
  • OWASP: Buffer Overflow

Key Takeaways

  • strncpy() is a footgun: Even when limiting the size, it doesn't guarantee null-termination. Never use it without manually null-terminating the buffer.
  • snprintf() is the safer choice for socket paths and other fixed-size buffers: It enforces both bounds checking and null-termination in a single call.
  • This vulnerability affected production code handling user input: The pomoc.c CLI tool could be exploited through environment variables or configuration files—a realistic attack vector.
  • Static analysis caught what code review might miss: Semgrep automatically flagged this pattern before it caused a security incident.
  • Truncation is better than overflow: When input is too long, snprintf() silently truncates it safely, whereas strncpy() leaves the buffer in an undefined state.

How Orbis AppSec Detected This

Source: Socket path constant (SOCKET_PATH) and potential environment-controlled input passed to the socket connection function.

Sink: strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1) at line 25 in src/pomoc.c—a dangerous string copy into a fixed-size buffer.

Missing Control: The code lacked guaranteed null-termination after the strncpy() call. While the size was limited, the absence of an explicit null-byte assignment left the buffer in an undefined state.

CWE: CWE-120 (Buffer Copy without Checking Size of Input)

Fix: Replaced strncpy() with snprintf(), which automatically enforces bounds checking and null-termination in a single, safer function call.

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

Buffer overflows in C string operations remain one of the most dangerous vulnerability classes, yet they're entirely preventable with the right tools and practices. The fix in pomoc.c demonstrates a simple but powerful principle: use language features and library functions that enforce safety constraints automatically.

By replacing strncpy() with snprintf(), the development team eliminated an entire class of potential exploits in a single line change. This is a reminder that security isn't about complex solutions—it's about choosing the right primitives from the start.

For developers working with C, especially on system utilities, CLI tools, and daemon code that handles user input: audit your string operations today. Replace strcpy() with snprintf() or strlcpy(), enable compiler warnings, and integrate static analysis into your CI/CD pipeline. The pomoc.c fix is a template for how to do it right.


References

Frequently Asked Questions

What is a buffer overflow in C string operations?

A buffer overflow occurs when data written to a buffer exceeds its allocated size, overwriting adjacent memory. With `strcpy()` and `strncpy()`, this happens because they don't validate the destination buffer size or properly null-terminate strings.

How do you prevent buffer overflow in C socket operations?

Always use bounds-checked functions like `snprintf()`, `strlcpy()`, or `strncpy()` with explicit null-termination. Validate input length before copying and use sizeof() to pass the exact buffer size.

What CWE is buffer overflow in C string operations?

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

Is using strncpy() enough to prevent buffer overflow?

No. `strncpy()` prevents overflow but does NOT guarantee null-termination if the source string is longer than the size limit. You must manually null-terminate: `dest[sizeof(dest)-1] = '\0'`. `snprintf()` is safer because it handles both bounds and null-termination automatically.

Can static analysis detect this vulnerability?

Yes. Semgrep and other static analysis tools detect `strcpy()` and `strncpy()` usage as high-risk patterns. This vulnerability was caught by the Semgrep rule `c.lang.security.insecure-use-string-copy-fn.insecure-use-string-copy-fn`.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1

Related Articles

critical

How buffer overflow happens in C tar header parsing and how to fix it

A critical buffer overflow vulnerability was discovered in `microtar/microtar.c` where the `raw_to_header()` and `header_to_raw()` functions used unbounded `strcpy()` and `sprintf()` calls to copy tar header fields. Malicious tar files with non-null-terminated name fields could overflow destination buffers, potentially leading to code execution. The fix replaces all unsafe string operations with bounded alternatives: `memcpy()` with explicit null-termination and `snprintf()` instead of `sprintf(

critical

How buffer overflow happens in C ieee80211_input() and how to fix it

A critical buffer overflow vulnerability was discovered in `src/firmware/src/net/ieee80211.c` at line 1584, where the `ieee80211_input()` function processed raw 802.11 data frames without verifying that the incoming frame was large enough to contain a valid `ieee80211_frame` header. An attacker within wireless range could craft undersized or malformed frames to trigger memory corruption, potentially leading to remote code execution on the firmware. The fix adds a single, targeted bounds check th

high

How buffer overflow from unsafe string copy functions happens in C network interface code and how to fix it

A high-severity buffer overflow vulnerability was discovered in `generic/eth-impl.c`, where unsafe `strncpy()` and `sprintf()` calls could write beyond buffer boundaries when handling network interface names and device filenames. The fix replaced these dangerous functions with bounded `snprintf()` calls that guarantee null-termination and prevent memory corruption.

critical

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

A critical buffer overflow vulnerability was discovered in `fuzzer/FuzzIxml.c` where `sprintf()` wrote a PID-formatted filename into a fixed 256-byte stack buffer without any bounds checking. The fix replaces `sprintf()` with `snprintf()`, explicitly passing the buffer size to prevent any overflow. While exploitation in this specific fuzzer context requires local access, the pattern is a textbook example of CWE-120 that developers should recognize and eliminate everywhere it appears.

critical

How buffer overflow happens in C HTML parsing and how to fix it

A critical buffer overflow vulnerability in `include/html_parse.h` allowed attackers to overflow buffers by providing malicious HTML input exceeding buffer capacity. The fix adds proper bounds checking before memcpy() operations to prevent memory corruption and potential code execution.

critical

How buffer overflow in memcpy() happens in Node.js N-API bindings and how to fix it

A critical buffer overflow vulnerability was discovered in the GetBufferAsVector() function in examples_nodejs/src/zupt_napi.cpp, where memcpy() copied data from JavaScript Uint8Array buffers without proper bounds validation. This vulnerability could allow attackers to trigger memory corruption by providing maliciously crafted input arrays to the native Node.js module, potentially leading to crashes or arbitrary code execution.