[ DOTSTAR_SYSTEMS ]

blog article

Linux 7.2 Kills strncpy() — What the Kernel's 6-Year Exorcism Means for Your C Codebase

strncpy() is gone from the Linux kernel after 362 patches and 6 years. The real story isn't the removal — it's the five replacements, the forced audit of intent, and why your firmware probably still has the same bug.

Linux 7.2 Kills strncpy() — What the Kernel's 6-Year Exorcism Means for Your C Codebase

On June 20, Linux merged the patch that finally removes strncpy() from the kernel. 362 commits. 70 contributors. Six years.

Every tech outlet ran the same headline. Here is what they left out.

strncpy Does Not Do What Its Name Suggests

Most C developers learn strncpy() as “the safe version of strcpy().” It is not. It never was. The kernel’s own documentation calls it “actively dangerous.”

Two behaviours make it a persistent source of bugs:

1. It does not guarantee NUL termination.

char dest[8];
strncpy(dest, "Hello, World!", sizeof(dest));
// dest = {'H','e','l','l','o',',',' ','W'}
// No NUL terminator. Any subsequent string operation
// reads past the buffer into adjacent kernel memory.

When this happens in a kernel buffer that gets copied to userspace — an ioctl return, a /proc read, a netlink message — those extra bytes are an information leak. You are handing the caller whatever sits next to your buffer in kernel memory. Depending on the allocator and the slab, that can be cryptographic keys, pointers (defeating ASLR), or another user’s data.

2. It unconditionally zero-pads the entire remaining buffer.

char dest[4096];
strncpy(dest, "hi", sizeof(dest));
// Copies 2 bytes, then writes 4094 zeros. Every time.

On a hot path — a packet handler, a filesystem lookup, a Bluetooth event — that is a measurable performance penalty for zero semantic benefit. In a kernel that processes millions of these per second, it adds up.

The function is named str*, suggesting it operates on strings. It was actually designed in the 1970s for fixed-width directory entry fields in early Unix filesystems, where NUL-padding to a fixed width was the point. That context has been lost for decades, and generations of developers have used it as a “bounded strcpy” — which it is not.

Not One Replacement, but Five — And That Is the Point

Here is the insight that every summary of this story misses: the kernel did not replace strncpy() with a single safer function. It replaced it with five, because strncpy() was hiding five semantically distinct operations behind one misleading name.

Each of the 362 patches required a human to read the call site, determine the programmer’s actual intent, and choose the correct replacement. This is why it took six years. Coccinelle could find every call site automatically; it could not determine intent.

ReplacementUse whenBehaviour
strscpy()Destination is a NUL-terminated stringAlways terminates; returns -E2BIG on truncation
strscpy_pad()String destination that must be zero-paddedTerminates + pads remainder to zero
strtomem_pad()Destination is a fixed-width field, not a stringNo NUL terminator; pads remainder
memcpy_and_pad()Bounded copy with explicit padding semanticsFor wire protocols and hardware registers
memcpy()Source and destination lengths are knownNo ambiguity needed

This table is the real deliverable of the six-year campaign. It forces every C developer maintaining kernel code to answer a question that strncpy() let them avoid: “Is this destination a string, or a fixed-width byte field?”

A Wi-Fi Example: Why the Distinction Matters

An 802.11 SSID is a 32-byte field. Per the spec, it is not NUL-terminated — it is a fixed-width byte array that happens to often contain printable text. For decades, kernel wireless drivers copied SSIDs with strncpy():

/* Old — intent is ambiguous */
strncpy(bss->ssid, ie->data, IEEE80211_MAX_SSID_LEN);

Is this a string copy? A byte-field copy? strncpy() does not force you to decide, and that ambiguity is where bugs hide. The correct replacement depends on what the driver does next:

/* If the driver later uses strlen(bss->ssid) → string semantics */
strscpy(bss->ssid, ie->data, sizeof(bss->ssid));

/* If the driver sends the field on the wire as-is → byte-field */
strtomem_pad(bss->ssid, ie->data, 0);

Choosing wrong does not just look inelegant — it changes runtime behaviour. strscpy() on a field that hits the wire would truncate the 32nd byte to NUL. strtomem_pad() on a field that gets strlen()’d would read past the buffer.

The Pattern: How the Kernel Eliminates a Bug Class

This removal is not an isolated cleanup. It is the second phase of a systematic campaign led by Kees Cook and the Kernel Self Protection Project:

PhaseFunctionCompletedEffort
1strlcpy()January 2024~4 years
2strncpy()June 2026~6 years
3strcpy()In progress

The pattern each time is the same:

  1. Deprecate — mark the function dangerous in documentation
  2. Provide alternatives — implement the replacements
  3. Convert — patch every call site, one subsystem at a time
  4. Enforce — remove the function from the source tree so it becomes a compile error

Step 4 is what makes this permanent. You cannot reintroduce strncpy() now — any new code that uses it will not compile. The policy has moved from documentation into the toolchain.

What This Means Outside the Kernel

If you maintain C code in firmware, an RTOS, or any systems-level project, the kernel just did your audit for you. The question is whether you will act on it.

Zephyr RTOS: 308 Call Sites and Counting

The kernel finished its cleanup. Zephyr has not started.

I ran grep -rn "strncpy(" . against the Zephyr v4.4.0 tree. 308 call sites.

strncpy call sites in Zephyr v4.4.0 by subsystem: drivers/modem 71, subsys/net 58, drivers/wifi 38, tests 51, modules/hostap 16, subsys/bluetooth 8, everything else 66.

The Wi-Fi and Bluetooth numbers are the ones that should concern product teams. These are not test utilities — they are copying SSIDs, passphrases, and device names in production driver paths.

A concrete example from the NXP Wi-Fi driver (drivers/wifi/nxp/nxp_wifi_drv.c):

strncpy(res.ssid, scan_result.ssid, scan_result.ssid_len);

Is res.ssid a NUL-terminated string or a fixed-width field? The answer determines whether a 32-byte SSID gets silently truncated or leaks adjacent memory. strncpy() lets you avoid the question — and that is exactly the ambiguity the kernel just spent six years resolving.

Or from the hostap supplicant glue (modules/hostap/src/supp_api.c):

strncpy(ssid_null_terminated, params->ssid, WIFI_SSID_MAX_LEN);
ssid_null_terminated[params->ssid_length] = '\0';

The manual NUL-termination on the next line is the tell — the developer knew strncpy() would not do it. strscpy() eliminates the second line and the entire class of bugs where someone forgets it. (Full disclosure: I maintain the Zephyr Wi-Fi and hostap integration — so those 16 hostap call sites are now my problem. You’re welcome.)

Zephyr’s default C library (picolibc) does provide strlcpy() now, so a safer alternative exists — but there is no treewide campaign to migrate, no compiler enforcement to prevent new strncpy() usage, and 308 existing call sites sitting in production code.

Your Audit Starts Here

# Find every strncpy call site in your project
grep -rn "strncpy(" .

# Enable the compiler warning that flags truncation
# (GCC 8+, Clang 6+)
CFLAGS += -Wstringop-truncation

For each hit, ask the question the kernel forced 70 contributors to answer: Is this destination a NUL-terminated string, or a fixed-width byte field? Then pick the replacement from the table above.

The Linux kernel took six years because it has 30+ million lines of code and a process that requires per-subsystem maintainer review. Your firmware codebase is smaller. The audit is the same.