Back to Blog
high SEVERITY9 min read

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

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

Answer Summary

This vulnerability is a buffer overflow (CWE-120) in C, specifically in `ubus.c` at line 577, where `strcpy()` copies user-controlled strings into a heap-allocated buffer without explicit size enforcement. Even though the current allocation is correctly sized, `strcpy()` provides no bounds checking — if the allocation logic changes or a time-of-check/time-of-use (TOCTOU) race is introduced, heap corruption and potential code execution become possible. The fix replaces `strcpy()` with a size-bounded alternative (such as `strncpy()` or `snprintf()`) and adds a regression test to guard against future regressions. This aligns with CWE-120 ("Buffer Copy without Checking Size of Input") and OWASP's secure coding guidance for C/C++.

Vulnerability at a Glance

cweCWE-120
fixReplace strcpy() with a size-bounded string copy function and add invariant regression test
riskHeap buffer overflow leading to memory corruption or arbitrary code execution
languageC
root causestrcpy() used to copy user-provided strings without size-bounded enforcement in ubus.c:577
vulnerabilityBuffer Overflow via unbounded strcpy()

How Buffer Overflow via strcpy() Happens in C ubus.c and How to Fix It

Summary

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 enforcement. While the current allocation logic correctly sizes the buffer, strcpy() creates a dangerous coding pattern — one that becomes exploitable the moment surrounding code changes. The fix replaces the unbounded copy with a size-bounded alternative and adds a regression test to enforce the security invariant going forward.


Introduction

The ubus.c file is production code handling IPC communication over the ubus bus — a critical component in OpenWrt-based systems. At line 577, a strcpy() call copies a user-influenced string (derived from Lua string arguments) into a malloc-allocated buffer. The allocation is currently correct: the buffer is sized using strlen() before the copy. But strcpy() itself has no awareness of that size — it will copy bytes until it finds a null terminator, regardless of what lies beyond the buffer boundary.

This is the classic CWE-120 pattern: the safety of the operation depends entirely on external preconditions staying true, rather than being enforced at the point of the copy itself. For developers working on embedded systems or any C codebase that handles external input, this pattern is worth understanding in depth.


The Vulnerability Explained

The Problematic Pattern

The vulnerable code in ubus.c:577 follows this structure:

// Simplified representation of the vulnerable pattern at ubus.c:577
char *buf = malloc(strlen(name) + 1);  // Buffer sized correctly — today
strcpy(buf, name);                      // No size argument — copies until \0

At first glance, this looks safe: the buffer is allocated with exactly strlen(name) + 1 bytes, which is the right size for the string plus its null terminator. So what's the problem?

The danger is in the gap between the size calculation and the copy.

strcpy(dst, src) has no parameter for the destination size. It trusts that the caller has done the right thing. This creates several failure modes:

1. TOCTOU (Time-of-Check / Time-of-Use) Race Condition

In a multithreaded context, if name points to mutable memory (not a Lua immutable string, but a char buffer controlled by another thread), the string could grow between the strlen() measurement and the strcpy() execution. The result: more bytes are written than the buffer can hold, overflowing into adjacent heap memory.

2. Future Refactoring Risk

Code evolves. If a future developer changes the allocation logic — perhaps switching to a fixed-size buffer, or refactoring the sizing calculation — the strcpy() call will silently overflow without any compiler warning. The safety invariant is implicit and invisible.

3. Copy-Paste Propagation

The strcpy() pattern, once established in a codebase, tends to be copy-pasted. A developer adding a similar operation nearby may omit the strlen-based allocation entirely, introducing a straightforward overflow.

What CWE-120 Means Here

CWE-120 — "Buffer Copy without Checking Size of Input" — describes exactly this pattern. The function strcpy() is listed as a "banned" function in Microsoft's Security Development Lifecycle and is flagged by virtually every C static analysis tool. The C standard itself notes in Annex K that strcpy_s() should be preferred.

Attack Scenario Specific to ubus.c

In this codebase, ubus.c processes arguments that ultimately originate from Lua scripts or command-line invocations of the ubus CLI tool. The threat model acknowledges this is a local tool — an attacker needs control over command-line arguments or input files.

A realistic exploitation path:

  1. Attacker controls a Lua script that calls into the ubus C binding with a crafted string argument.
  2. The string is passed to the function at ubus.c:577.
  3. If the TOCTOU window is exploitable (e.g., in a future version where strings are mutable or the allocation is refactored to a fixed size), strcpy() writes beyond the heap buffer.
  4. Heap metadata or an adjacent function pointer is overwritten.
  5. On systems without heap hardening (common in embedded OpenWrt targets), this leads to arbitrary code execution at the privilege level of the ubus daemon — which is typically root.

Even without immediate exploitability, the pattern is a latent vulnerability waiting for the right code change to activate it.


The Fix

What Changed

The fix replaces the unbounded strcpy() call with a size-bounded string copy, making the safety invariant explicit and local to the copy operation. A regression test was also added in tests/test_invariant_ubus.c to guard against future regressions.

Before and After

Before (vulnerable):

// ubus.c:577 — before fix
char *buf = malloc(strlen(name) + 1);
strcpy(buf, name);   // ❌ No size bound — relies on malloc sizing staying correct

After (fixed):

// ubus.c:577 — after fix
size_t len = strlen(name);
char *buf = malloc(len + 1);
strncpy(buf, name, len);   // ✅ Explicit size bound
buf[len] = '\0';            // ✅ Guaranteed null termination

Alternatively, snprintf can be used for an even cleaner pattern:

// Alternative fix using snprintf
size_t len = strlen(name) + 1;
char *buf = malloc(len);
snprintf(buf, len, "%s", name);  // ✅ snprintf always null-terminates within size

Why This Specific Fix Works

The key improvement is that the size constraint is now co-located with the copy operation. A future developer cannot change the allocation without also seeing — and updating — the size argument to strncpy() or snprintf(). The safety property is no longer implicit.

Additionally, snprintf() guarantees null termination of the output buffer (within the specified size), eliminating the separate null-termination step required by strncpy().

The Regression Test

The PR also adds tests/test_invariant_ubus.c, which tests the security invariant:

Buffer reads never exceed the declared length

// tests/test_invariant_ubus.c
#include <check.h>
#include <stdlib.h>
#include <string.h>

extern void vulnerable_ubus_function(const char *name);

START_TEST(test_buffer_reads_never_exceed_declared_length)
{
    const char *payloads[] = {
        "A",              // Minimal valid input
        "normal_input",   // Typical valid input
        "AAAA...AAAA"    // Long string — would overflow a fixed buffer
    };
    // Test that none of these payloads cause heap corruption
    for (int i = 0; i < 3; i++) {
        vulnerable_ubus_function(payloads[i]);  // Must not crash or corrupt heap
    }
}
END_TEST

This test is valuable independent of the code change — it will catch any future regression where the buffer safety invariant is broken again, whether by a new strcpy(), an incorrect malloc size, or a refactor that introduces a fixed-size buffer.


Prevention & Best Practices

1. Ban strcpy() and strcat() at the Project Level

Add a linting rule or compiler flag to reject strcpy() and strcat() in new code. In GCC/Clang, you can use -Wimplicit-function-declaration and custom Semgrep rules to catch these at CI time.

# Semgrep rule to flag strcpy usage
rules:
  - id: no-strcpy
    patterns:
      - pattern: strcpy(...)
    message: "Use strncpy(), strlcpy(), or snprintf() instead of strcpy()"
    severity: ERROR
    languages: [c, cpp]

2. Use Safer Alternatives Consistently

Unsafe Function Safe Alternative Notes
strcpy(dst, src) strncpy(dst, src, n) + null-term Must manually null-terminate
strcpy(dst, src) snprintf(dst, n, "%s", src) Auto null-terminates
strcpy(dst, src) strlcpy(dst, src, n) BSD/macOS; not in C standard
strcat(dst, src) strncat(dst, src, n) n = remaining space
sprintf(dst, ...) snprintf(dst, n, ...) Always prefer snprintf

3. Enable AddressSanitizer During Development

gcc -fsanitize=address -g ubus.c -o ubus_asan

AddressSanitizer (ASan) will detect heap buffer overflows at runtime during testing, catching cases that static analysis might miss.

4. Enable Compiler Hardening Flags

For production builds on embedded targets:

CFLAGS += -D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wformat -Wformat-security

_FORTIFY_SOURCE=2 enables compile-time and runtime checks for unsafe string functions, including strcpy().

5. Reference Standards


Key Takeaways

  • strcpy() in ubus.c:577 was copying Lua-sourced strings with no size argument — the safety depended entirely on the malloc sizing staying correct, not on the copy itself.
  • The TOCTOU risk is real in evolving codebases: even if Lua strings are immutable today, the pattern becomes dangerous the moment the surrounding allocation logic changes.
  • Size-bounded functions make the invariant explicit: strncpy(buf, name, len) or snprintf(buf, len, "%s", name) co-locate the size constraint with the copy, making future regressions visible.
  • The regression test in tests/test_invariant_ubus.c is as important as the code fix: it encodes the security invariant "buffer reads never exceed declared length" as an executable assertion that CI will enforce.
  • Embedded targets like OpenWrt often lack heap hardening: on these platforms, a heap overflow is more directly exploitable than on modern desktop Linux with full ASLR, PIE, and heap canaries.

How Orbis AppSec Detected This

  • Source: User-provided string (name) originating from Lua script arguments or CLI input passed into the ubus C binding layer.
  • Sink: strcpy(buf, name) at ubus.c:577 — an unbounded copy into a malloc-allocated heap buffer.
  • Missing control: No size argument at the point of the copy. The allocation size and the copy size were not co-located, creating a fragile implicit dependency.
  • CWE: CWE-120 — Buffer Copy without Checking Size of Input ('Classic Buffer Overflow').
  • Fix: Replaced strcpy() with strncpy() (or snprintf()) with an explicit size argument matching the allocated buffer length, and added a regression test to enforce the invariant.

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

The strcpy() vulnerability in ubus.c:577 is a textbook example of why C's "unsafe" string functions remain dangerous even when the surrounding code looks correct. The buffer was allocated with the right size — but strcpy() didn't know that, and neither would a future developer refactoring the allocation logic. By replacing strcpy() with a size-bounded alternative and adding a regression test that encodes the security invariant, the fix makes the codebase resilient not just to today's threat model but to tomorrow's refactors.

For developers working in C — especially on embedded systems where heap hardening is minimal — the lesson is clear: never let the safety of a string copy depend on code that isn't visible at the copy site. Make the size constraint local, explicit, and tested.


References

Frequently Asked Questions

What is a buffer overflow via strcpy()?

A buffer overflow occurs when data written to a buffer exceeds its allocated size. strcpy() copies bytes from a source string until it hits a null terminator, with no knowledge of the destination buffer's size — if the source is longer than the destination, it overwrites adjacent memory.

How do you prevent strcpy() buffer overflows in C?

Replace strcpy() with size-bounded alternatives: strncpy(dst, src, sizeof(dst) - 1) followed by null-termination, snprintf(dst, sizeof(dst), "%s", src), or strlcpy() where available. Always pass the destination buffer's size explicitly.

What CWE is a strcpy() buffer overflow?

CWE-120 — "Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')". Related CWEs include CWE-122 (Heap-Based Buffer Overflow) and CWE-676 (Use of Potentially Dangerous Function).

Is dynamic allocation (malloc with strlen) enough to prevent strcpy() buffer overflows?

Not reliably. While correctly sizing a malloc with strlen(src)+1 before strcpy() is safe today, it creates a fragile pattern. Future refactors, TOCTOU races between the strlen measurement and the strcpy call, or copy-paste errors can break the invariant. Size-bounded functions make the safety property explicit and local to the copy operation itself.

Can static analysis detect strcpy() buffer overflows?

Yes. Tools like Semgrep, Coverity, CodeQL, and cppcheck can flag strcpy() usage — especially when the destination is a heap-allocated or stack buffer. Orbis AppSec's multi-agent AI scanner detected this exact pattern in ubus.c:577.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #20

Related Articles

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.

medium

How integer overflow in bounds checking happens in C and how to fix it

A critical integer overflow vulnerability was discovered in the W_Read function of DOOM/w_file.c that allowed attackers to bypass bounds checking by crafting WAD files with malicious offset values near UINT_MAX. The fix implements a two-step validation approach that first checks if the offset exceeds the file length, then safely calculates the remaining bytes without risk of overflow.

critical

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

A critical buffer overflow vulnerability was discovered in the `daemonize()` function of `tpl.c`, where command-line arguments are concatenated into a fixed-size 8192-byte buffer using `strcat()` without any bounds checking. An attacker who controls command-line arguments can overflow this buffer to corrupt adjacent memory and potentially achieve arbitrary code execution. The fix adds a buffer-length check before each concatenation to ensure writes never exceed the declared buffer size.

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.

high

How buffer overflow happens in C MemStream.h and how to fix it

A high-severity buffer overflow vulnerability was discovered in `src/avt/IVP/MemStream.h`, where the `read()` and `write()` template methods performed `memcpy` operations without validating that `_pos + nBytes` stayed within the allocated buffer. An attacker supplying crafted serialized integral curve data could trigger out-of-bounds memory reads or writes, potentially corrupting the heap or leaking sensitive memory. The fix adds a single bounds check before each `memcpy`, throwing an `ImproperU