strncpy is libc's most misleading function. Linux spent 6 years killing it

4 min read 1 source explainer
├── "strncpy is fundamentally misdesigned and its removal is a long-overdue API correction"
│  └── top10.dev editorial (top10.dev) → read below

The editorial argues that strncpy has at least three conflicting semantics — bounded copy, zero-padding, and non-null-terminating — that make it a footgun designed for a 1979 UNIX v7 filesystem use case with no legitimate modern application. The widespread misconception that it's 'the safe version of strcpy' has caused decades of subtle bugs, making its removal a vindication of safer alternatives like strscpy.

└── "The six-year, 360-patch effort demonstrates the true cost of fixing bad API decisions at scale"
  ├── top10.dev editorial (top10.dev) → read below

The editorial emphasizes that the removal required manual semantic auditing of every call site because no automated rewrite was possible — each strncpy meant something different depending on context. This serves as a cautionary tale that an API shipped in 1979 can take nearly half a century and hundreds of maintainer-reviewed patches to extract, even when its dangers are well understood.

  └── @simonpure (Hacker News, 258 pts) → view

The submission framing ('after six years of work, 360 patches') foregrounds the sheer scale and duration of the cleanup effort. By highlighting these numbers in the title, the submitter signals that the story's significance is the engineering cost required to undo a single bad standard-library choice.

What happened

Linux 7.2 ships with zero remaining calls to `strncpy()` in the entire kernel tree. The merge that closed the last instance — commit `1a3746ccbb0a` — marks the end of a six-year cleanup led by Kees Cook and the Kernel Self-Protection Project. The total cost: roughly 360 patches, touching virtually every subsystem, each one reviewed and signed off by the maintainer who owned that code.

The headline number isn't 360 patches. It's that a function which has shipped in libc since 1979 was determined to be unsafe enough that the world's most-deployed kernel decided to remove every single use of it, by hand, with no automated rewrite. The kernel's own internal `strscpy()` is now the canonical replacement, alongside `memcpy()` for cases where the caller actually wanted a fixed-length blob, and direct struct-field assignments where the original `strncpy` was being used as a janky `memset`-then-copy combo.

The Phoronix write-up frames this as a milestone. The more interesting framing is that it took six years — not because the patches were hard individually, but because the semantics of every single call site had to be audited, since `strncpy` is doing at least three different things in different parts of the codebase.

Why it matters

Here is the dirty secret of `strncpy(dest, src, n)`: the `n` does not bound the copy in the way every C programmer's intuition says it does. `strncpy` is the only function in the C standard library that can return a non-null-terminated string, and it's also the only one that will silently zero-pad your destination buffer to the full length `n` even when `src` is much shorter. Both behaviors are footguns. Together they're a feature designed for the UNIX v7 directory-entry format — fixed 14-byte filename slots, no terminator — that has had no legitimate use case in mainstream code since the late 1980s.

Most programmers learn `strncpy` as "the safe version of `strcpy`." That is the most expensive misconception in C. Compare:

- `strcpy(dest, src)` — overruns if `src` is longer than `dest`. - `strncpy(dest, src, sizeof(dest))` — does not overrun, but leaves `dest` un-terminated if `src` is `>= sizeof(dest)`. Your next `strlen` call walks off into adjacent memory. - `strscpy(dest, src, sizeof(dest))` — what `strncpy` should have been: bounded, always null-terminated, returns either the copied length or `-E2BIG`.

The kernel's replacement function exists because no libc shipped one. `strlcpy` (OpenBSD's attempt) was rejected by glibc maintainers for over a decade on aesthetic grounds. `strscpy` was written for the kernel in 2015 and has since been ported into multiple userspace projects. Linux didn't just remove an unsafe function — it shipped a reference design for what the replacement should look like, then proved at scale that the migration is mechanically tractable.

The community reaction has been muted because the work was muted. There was no Hacker News thread when patch 47 of 360 landed in the SCSI driver layer. The same is true for patches 48, 49, and 50. That is what disciplined deprecation looks like: not a manifesto, but a years-long grind through subsystems most kernel hackers never touch. The 258-point HN thread on Phoronix's recap is people noticing only because the counter hit zero.

What this means for your stack

If you maintain a C or C++ codebase of any meaningful size, the practical lesson is not "go remove `strncpy`." It's the migration methodology. The kernel's playbook generalizes:

1. Identify the deprecated API and its replacement before you start. Don't ask call sites to figure out what they wanted. The kernel chose `strscpy` first, then went looking for `strncpy` calls. 2. Classify by semantic intent, not by syntactic match. Roughly a third of `strncpy` calls in the kernel were actually fixed-width field copies that wanted `memcpy`. Another third wanted real string copying. The remainder were genuinely buggy. A regex sweep with auto-rewrite would have shipped silent regressions in a third of the call sites. 3. Push reviews to the subsystem maintainer. Kees Cook didn't merge his own patches en masse. Each one went through the relevant subtree, which is why it took years — and why nothing broke. 4. Treat the deprecation as a build-time wall, not a documentation note. Once a subsystem was clean, future `strncpy` calls were caught by checkpatch and compiler warnings, not by hoping reviewers noticed.

Your codebase almost certainly contains the userspace equivalent: `sprintf`, `strcat`, `gets` (still, somehow), `atoi`, `system()` with shell-quoted strings. The kernel demonstrated that the cost of removing them is real but bounded — call it one full-time engineer-year per few hundred call sites — and the alternative is continuing to pay the bug-class tax indefinitely.

There's also a fresh angle for teams using LLM-assisted refactoring. The strncpy migration is, almost by accident, the best public dataset for what a careful semantic audit looks like across a huge C codebase. The 360 patches are public, paired with the original buggy code and the maintainer's review comments. If you're building or evaluating an agent that does API migrations, this is your gold-standard eval set.

Looking ahead

The next obvious target is `strcpy`. It's already rare in the kernel but not gone. After that: `sprintf` in favor of `scnprintf` (which actually returns the bytes written, not the bytes it would have written). Each one is its own multi-year project. The strncpy removal matters because it makes those projects look finishable instead of mythic — and because it ships a template that any sufficiently disciplined open-source community can copy.

Hacker News 272 pts 288 comments

Linux eliminates the strncpy API after six years of work, 360 patches

<a href="https:&#x2F;&#x2F;git.kernel.org&#x2F;pub&#x2F;scm&#x2F;linux&#x2F;kernel&#x2F;git&#x2F;torvalds&#x2F;linux.git&#x2F;commit&#x2F;?id=1a3746ccbb0a97bed3c06ccde6b880013b1dddc1" rel="nofollow">h

→ read on Hacker News
sirwhinesalot · Hacker News

I have in the past made fun of the Linux kernel devs, supposedly some of the best C developers in the world, for not knowing how to make stringbuffer and stringview types, but to be fair to them we didn&#x27;t have the consensus we have today on the topic.You know who did have the right idea though?

WalterBright · Hacker News

&quot;The strncpy function within the Linux kernel has been a &quot;persistent source of bugs&quot; for years due to counter-intuitive semantics and behavior around NUL termination along with performance issues due to redundant zero-filling of the destination.&quot;Huh. Whenever I&#x27;ve been asked

rswail · Hacker News

Things that have bugged me for 40 years...* NUL terminated strings (and now, non UTF-8 encoded strings on input&#x2F;output)* Using LF or CR or CRLF as line terminators, and pipe&#x2F;comma-delimited fields when there were other unambiguous ASCII characters that could have been used (eg, GS, FS, RS)

thiht · Hacker News

&gt; In place of strncpy, Linux kernel code should use strscpy() for NUL terminated destinations, strscpy_pad() for NUl-terminated destinations with zero-padding, strtomem_pad() for non-NUL-terminated fixed-width fields, memcpy_and_pad() for bounded copies with explicit padding, or memcpy() for know

lambdaone · Hacker News

This sort of boring grind is where the real work of systems engineering is done. Big infrastructure projects like this work on making the Linux kernel more reliable while still keeping it workable throughout the process move on the scale of decades, not months.

// share this

// get daily digest

Top 10 dev stories every morning at 8am UTC. AI-curated. Retro terminal HTML email.