FlawOpen

flawopen.com/Memory Safety/strcpy vs strncpy

Is strncpy() safer than strcpy()?

CWE-787: Out-of-bounds Write; CWE-170: Improper Null TerminationReference page
Short answer

A fixed-width field is like a row of labeled slots: the final slot must hold an end marker if later code will treat the bytes as a C string. strncpy() writes at most n bytes, so it avoids a destination overrun only when n is no larger than the real buffer. If the source fills that limit, it adds no null terminator. A later string function can then read beyond the destination looking for the missing marker.

BOTH ARE WRONG
char dst[16];

/* 1. Unbounded — classic overflow */
strcpy(dst, src);

/* 2. Bounded, but may not terminate.
   If strlen(src) >= 16, dst has no
   '\0' and every later strlen/printf
   reads out of bounds.            */
strncpy(dst, src, sizeof(dst));

/* 3. Off-by-one: room for 16 chars
   plus a terminator = 17 bytes,
   but dst only holds 16.          */
strncpy(dst, src, sizeof(dst));
dst[sizeof(dst)] = '\0';   /* OOB! */
CORRECT
char dst[16];

/* Preferred: snprintf always
   null-terminates (when size > 0),
   and tells you about truncation. */
int n = snprintf(dst, sizeof(dst),
                 "%s", src);
if (n < 0 || (size_t)n >= sizeof(dst)) {
    /* truncated — handle it */
    return ERR_TOO_LONG;
}

/* Or strlcpy where available —
   always terminates, returns the
   length it tried to create.      */
if (strlcpy(dst, src, sizeof(dst))
        >= sizeof(dst)) {
    return ERR_TOO_LONG;
}

What strncpy was actually designed for

Its behavior is not a defect: strncpy() was designed for fixed-width, null-padded character sequences, not as a generally safe C-string copy. If the source string is shorter than n, it pads the destination with null bytes through byte n - 1. If there is no null byte in the first n source bytes, it copies exactly n bytes and does not terminate the destination. That can suit a fixed-width field format; it is hazardous when the result is passed to APIs that expect a null-terminated string.

When the source is shorter than n, the required null padding writes through the full field width. For a large destination and short source, this can do more work than a string-copy routine that stops at the terminator.

Always check for truncation

Preventing the overflow is only half the job. Silent truncation is itself a security problem: a truncated hostname, path, or authorization value can change meaning rather than simply being shorter. snprintf returns the length it wanted to write, so comparing that against the buffer size detects truncation — the same return-value semantics that, when misread as "bytes written", produced Citrix Bleed.

FAQ

What about strcpy_s and the Annex K functions?

They null-terminate and check bounds, which addresses the problem, but portability is uneven — they are well supported on Windows and patchily implemented elsewhere. If you target multiple platforms, snprintf is the dependable choice.

Is strlcpy in the C standard?

It originated in OpenBSD and is widely available on BSD and macOS, with glibc adding it comparatively recently. Check availability for your targets rather than assuming it.

In C++, should I use any of these?

Prefer std::string and std::string_view, which manage length for you and remove the entire class of bug. Reach for the C functions only at an API boundary that requires a raw buffer.

References

See also CWE vs. CVE: What's the Difference? Mass Assignment: Risks and Safe Field Allowlists Serverless State Bleed: Keep User Data Request-Scoped PHP == and Magic Hash Comparisons Is Rust's unsafe block still memory safe?