Linux finally kills strncpy() — 360 patches, six years, one footgun gone

4 min read 1 source clear_take
└── "Removing strncpy() is a long-overdue safety win that vindicates patient, multi-year hardening campaigns"
  ├── top10.dev editorial (top10.dev) → read below

The editorial frames the milestone as the culmination of disciplined kernel self-protection work, emphasizing that strncpy() is famously misused — it doesn't null-terminate when truncating and wastes cycles zero-padding when not. Replacing it with strscpy() fixes both failure modes and gives callers a usable truncation signal, making the six-year, 360-patch slog worth it.

  ├── Michael Larabel (Phoronix) → read

Phoronix flags the commit as a notable milestone for Linux 7.2, treating the complete eviction of strncpy() from the kernel as a headline-worthy hardening achievement. The framing presents this as a clear positive outcome of Kees Cook and the Kernel Self-Protection Project's sustained effort.

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

By submitting the Phoronix piece with a title emphasizing 'six years of work, 360 patches,' the submitter highlights the scale and persistence required. The 170-point score signals the HN community broadly endorses this as a meaningful safety milestone for the largest C codebase in existence.

What happened

With the merge of commit `1a3746ccbb0a` into Linus's tree, the Linux kernel no longer contains a single call to `strncpy()`. Phoronix flagged the milestone for the upcoming Linux 7.2 release: the C standard library's most notoriously misused string function has been fully evicted from the largest C codebase on the planet.

The cleanup took roughly six years and around 360 patches, driven primarily by Kees Cook and the Kernel Self-Protection Project, with help from dozens of subsystem maintainers who had to vouch for each conversion. Every driver, every filesystem, every architecture-specific bring-up path had its `strncpy()` calls audited and rewritten — usually to `strscpy()`, occasionally to `memcpy()` plus an explicit terminator, and in a handful of cases to `strtomem_pad()` when the destination genuinely needed NUL-padding semantics for ABI reasons.

The final patch is anticlimactic on purpose. It deletes the `strncpy()` declaration from `` and adds a comment pointing future contributors at `strscpy()`. The interesting work happened in the 359 patches before it.

Why it matters

`strncpy()` is the function everyone learns to reach for and almost everyone uses wrong. Despite the `n` in the name, it does not produce a null-terminated string when the source is longer than the destination — it stops copying at `n` bytes and leaves you with an unterminated buffer that the next `strlen()` or `printk("%s")` will happily read off the end of. It also does the opposite when the source is *shorter* than `n`: it zero-pads the entire remaining destination, which was useful for fixed-width records on a PDP-11 in 1979 and useful for almost nothing since.

The kernel's replacement, `strscpy()`, fixes both halves. It always null-terminates (as long as the destination has at least one byte), it doesn't waste cycles padding, and it returns the number of bytes copied or `-E2BIG` if the source was truncated. That return value is the part that matters: callers can now branch on truncation instead of discovering it three subsystems later when a device name shows up garbled in `dmesg`. The Kernel Self-Protection Project's own audit found that a meaningful fraction of the converted sites had latent bugs — either missing terminators or silent truncation that the original author hadn't considered.

The reason this took six years instead of an afternoon is the social half of the problem. Linus's standing rule is that tree-wide cleanups don't get to bypass the subsystem maintainers, so each conversion had to be reviewed by the people who own the code, in their merge window, on their schedule. Kees Cook and collaborators used Coccinelle (the kernel's semantic-patch tool) to generate candidate diffs, then hand-split them by subsystem and sent them through the normal review process. Some maintainers accepted the first patch; some pushed back on `strscpy()` semantics and forced a third variant (`strscpy_pad()`); some sat on patches for a year. The deprecation deadline only became enforceable once the call-site count was low enough that a `-Wdeprecated` warning wouldn't bury the build log.

The broader C ecosystem has been having a quieter version of this argument for a decade. OpenBSD shipped `strlcpy()` in 1998 — same idea, different return convention — and glibc still refuses to include it on the grounds that it encourages silent truncation. The kernel's `strscpy()` is essentially `strlcpy()` with a saner error path, and its success at scale is the strongest empirical argument anyone has made that the glibc position is wrong.

What this means for your stack

If you maintain an out-of-tree kernel module, your next rebase against 7.2 will fail to link if you call `strncpy()`. The fix is mechanical: `strscpy(dst, src, sizeof(dst))` is a drop-in for the common case, and the return value lets you handle truncation explicitly if you care. If you were relying on the zero-padding behavior — usually for a struct that gets memcmp'd or sent over a wire — you want `strtomem_pad()`, not `strscpy()`.

If you maintain any non-trivial C codebase, the more useful takeaway is the methodology, not the API change. The kernel did not flip a switch; it ran a six-year campaign with three ingredients: a semantic patch tool that could find every call site mechanically, a replacement API that was strictly better on every axis (so reviewers couldn't argue the trade-off), and a deprecation timeline measured in years rather than releases. Most C codebases that try to deprecate a footgun fail at step two — the replacement is 80% better and 20% worse, and the 20% generates enough bikeshedding to stall the cleanup indefinitely. `strscpy()` won because there is no case where `strncpy()` is preferable.

The Rust-in-kernel crowd will read this story and say it's why the kernel needs a memory-safe language. They're not wrong, but they're missing the more immediate lesson: even within C, the kernel has been quietly retiring its sharpest edges — `strcpy()` is gone, `sprintf()` is gone in new code, `strncpy()` is now gone, and `memcpy()` with non-constant sizes triggers a runtime bounds check under `CONFIG_FORTIFY_SOURCE`. The argument that "C is unsafe" is true in the abstract and increasingly false about the specific C that Linux actually ships. Whether that's enough to obviate Rust is a separate question; whether it raises the floor for every C codebase that copies kernel idioms is not.

Looking ahead

The next target on Kees Cook's list is the remaining `memcpy()` calls that copy across struct field boundaries — the class of bug that lets an overflow in one field clobber the next. The tooling (`__counted_by`, `__nonstring`, FORTIFY_SOURCE level 3) is in place; the patch campaign is in early innings. Expect the same six-year arc, the same per-subsystem review grind, and — if `strncpy()` is a fair predictor — the same anticlimactic final commit somewhere around 2031. The footgun-removal industry inside the kernel is now a permanent fixture, and that's the part of this story that should outlive the specific function being deprecated.

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.