Last week, the Linux kernel did something that took six years, 362 patches, and an exhausting amount of collective developer willpower: it finally removed strncpy() from the kernel source tree. Gone. Deleted. No more callers anywhere in the entire kernel.

If you write C, this matters to you. Not because the Linux kernel is a special snowflake — but because strncpy has been quietly causing buffer overflows and security bugs in C codebases for decades, and most developers don’t even realize what it actually does.
I learned C the way a lot of people do — in college, with a textbook that said “use strncpy, it’s the safe one.” That advice was wrong then, and it’s still wrong now. The Linux kernel just proved it in the most dramatic way possible.
The Surprising Thing strncpy Actually Does
Here’s the mental model most developers have: strncpy(dest, src, n) copies at most n characters from src to dest, and then null-terminates. Safe. Bounded. What could go wrong?
Here’s what it actually does:
char dest[8];
const char *src = "Hello, World!";
strncpy(dest, src, sizeof(dest));
// dest = "Hello, W" — WAIT, no NUL terminator!
If the source is longer than n, strncpy copies exactly n characters and does not add a NUL terminator. Your destination buffer now contains a non-terminated string. Any subsequent call to strlen(dest), printf("%s", dest), or strcpy(somewhere, dest) will read right past the end of your buffer into whatever memory happens to be next door.
That’s not a theoretical concern. The Linux kernel maintainers called strncpy a “persistent source of bugs” — and they weren’t exaggerating.
Buffer overflows from string handling aren’t just a kernel problem — they’re behind some of the most exploited CVEs in userspace too. When I wrote about SSH server security hardening, one lesson kept coming back: the simplest code mistakes — like a missing bounds check — create the widest attack surfaces. strncpy is exactly that kind of mistake, hiding in plain sight.
But wait, it gets weirder. If the source is shorter than n, strncpy null-terminates — and then pads the rest of the destination buffer with zeros. All the way to the end.
char dest[256];
strncpy(dest, "hi", sizeof(dest));
// dest = "hi\0\0\0\0\0..." (254 more zeros)
That’s 254 useless writes you just paid for. In a kernel context, where every cycle counts, this was silently burning performance across thousands of call sites.
The Three Fatal Flaws
When you step back, strncpy has three problems that compound each other:
1. No guaranteed NUL termination. This is the big one. A string function that doesn’t guarantee a valid string is an API design failure. It’s the equivalent of a lock that only locks sometimes.
2. Pointless zero-padding. If you’re treating the destination as a string (NUL-terminated), those trailing zeros do nothing except waste CPU. The only reason they exist is historical — strncpy was originally designed for fixed-width fields in ancient Unix directory entries, not for string manipulation.
3. Surprising semantics. The function name contains “str” — strongly implying string behavior — but it doesn’t behave like a string function. The mismatch between the name and the actual contract has tripped up generations of C programmers.
Linus Torvalds himself has called strncpy “a truly stupid interface” and argued it should never have been used for strings in the first place. The kernel removal is the logical endpoint of that argument.
What Replaces It: The Safe Alternatives
The Linux kernel didn’t just delete strncpy — it built a family of replacements, each with a clear, unambiguous contract. Here they are, in order of how often you’ll reach for them:
strscpy() — The Direct Replacement
For any NUL-terminated destination, this is what you want 90% of the time:
ssize_t strscpy(char *dest, const char *src, size_t count);
It copies at most count - 1 characters and always NUL-terminates. Returns the number of characters copied (not including the NUL), or -E2BIG if truncated. No padding, no surprises.
char dest[8];
ssize_t ret = strscpy(dest, "Hello, World!", sizeof(dest));
// dest = "Hello, " (with NUL)
// ret = -E2BIG (truncation occurred)
This is now the kernel’s preferred string copy function, and it’s available outside the kernel too — it was added to glibc and musl, and you can use it in userspace C programs.
strscpy_pad() — When You Need the Zero-Padding
Same as strscpy, but pads the remaining buffer with zeros. Rarely needed, but useful when you’re writing to a fixed-width on-disk structure that expects zero-filled fields.
strtomem() — For Non-String, Fixed-Width Fields
This is for the actual use case strncpy was originally designed for — copying into a fixed-width byte array that isn’t a NUL-terminated string:
struct something {
char name[32] __nonstring;
};
strtomem(s->name, input);
The __nonstring attribute tells the compiler and static analyzers “this isn’t a string, don’t warn about missing NUL terminators.” This distinction — is it a string or isn’t it? — is what strncpy blurred and these replacements clarify.
memcpy() — For Known-Length Copies
When you already know the exact length of both source and destination, just use memcpy. The kernel documentation explicitly lists this as the right tool for the job. No string semantics, no padding, just copy exactly what you asked for.
How to Audit Your Own Codebase
The Linux kernel’s migration wasn’t magic — it was methodical. Here’s a practical approach you can apply to any C project:
Step 1: Find every strncpy call.
grep -rn "strncpy(" .
Step 2: Classify each one. Ask yourself: is the destination a NUL-terminated string, or a fixed-width byte buffer? If it’s a string, strscpy is almost certainly the right replacement. If it’s a fixed-width field, think about whether strtomem with a __nonstring attribute is more appropriate.
Step 3: Check for the manual NUL-termination pattern. You’ll often see code like this:
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
This is the developer working around strncpy‘s missing NUL — which means strscpy is the direct, cleaner replacement.
Step 4: Watch for the return value gotcha. strncpy returns a pointer to dest. strscpy returns the number of bytes copied (or a negative error). If your code uses the return value, you’ll need to update it.
Step 5: Enable compiler warnings and static analysis. GCC and Clang can warn about strncpy usage with -Wdeprecated and -Wstringop-truncation. Tools like Coverity and CodeQL flag these patterns. Turn them on and treat the warnings as errors.
And when static analysis does flag a suspicious strncpy call, git bisect is still the fastest way to pinpoint the exact commit that introduced it — a technique worth keeping in your back pocket for any security-sensitive codebase.
The Bigger Lesson: API Design Matters
The strncpy story isn’t just about C string handling. It’s about what happens when an API’s name promises one thing and its behavior delivers another. strncpy has “str” in the name, so developers assumed string semantics. It doesn’t have them. Six years and 362 patches later, the kernel finally untangled that confusion.
When you’re designing an API — whether it’s a C library function or a REST endpoint — the contract needs to be obvious from the name and the signature. If developers have to read the man page to avoid writing a security bug, the API has already failed.
The replacement functions get this right. strscpy says “string copy” and behaves like one. strtomem says “string to memory” and makes no false promises about NUL termination. The names tell you exactly what you’re getting.
That’s the quiet genius of the Linux kernel’s six-year migration. They didn’t just fix a dangerous function — they built a coherent vocabulary for talking about string operations in C, where the name and the contract finally match.
If you’re maintaining C code — embedded firmware, a systems library, a legacy application — spend an afternoon running that grep command and replacing those strncpy calls. Your future self, and anyone who audits your code for security vulnerabilities, will thank you.
The Linux kernel just proved it can be done at one of the largest scales imaginable. Your codebase is almost certainly smaller. There’s no excuse.
Better yet, add a grep for strncpy to your CI/CD pipeline so no new callers sneak in. If your build fails on deprecated string functions, you’ll never have to fight this battle again.