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

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 buffer overflow in memcpy happens in C x_util.c and how to fix it

A critical buffer overflow vulnerability was discovered in `hardinfo2/x_util.c` where `memcpy` operations copied data into dynamically allocated arrays without validating that the destination buffer was large enough. The vulnerable pattern used raw `malloc`/`realloc` without checking the return value before immediately using the pointer as a `memcpy` destination, meaning a failed allocation could lead to a NULL pointer dereference or out-of-bounds write. The fix replaces the unsafe manual alloca

high

How buffer overflow happens in C string copy functions and how to fix it

A high-severity buffer overflow vulnerability was discovered in `bin/nad/ftw.c` where unsafe `strncpy()` calls lacked proper NULL-termination guarantees. The fix replaces the vulnerable pattern with `strlcpy()`, a safer bounded string copy function that automatically handles NULL-termination and prevents buffer overflows in file tree walking operations.

high

How buffer overflow via strcpy() happens in C ubus.c and how to fix it

A high-severity buffer overflow vulnerability was discovered and fixed in `ubus.c` at line 577, where `strcpy()` was used to copy user-provided strings into dynamically allocated buffers without explicit size bounds checking. While current allocation logic correctly sizes the buffer, the use of `strcpy()` creates a dangerous coding pattern that could lead to exploitable memory corruption if the allocation logic ever changes or a TOCTOU race condition is introduced. The fix replaces the unbounded

high

How buffer overflow via strcpy() happens in Nordic BLE C firmware and how to fix it

A high-severity buffer overflow vulnerability was discovered in the Nordic BLE Central Demo firmware, where unsafe `strcpy()` and `sprintf()` calls in the `BleDevDiscovered()` function could allow attackers to overflow stack buffers by sending specially crafted BLE service discovery responses. The fix replaced all unbounded string operations with size-checked `snprintf()` calls, preventing potential remote code execution in embedded Bluetooth devices.