string: Remove strncpy() from the kernel

strncpy() has been a persistent source of bugs due to its ambiguous
intended usage and frequently counter-intuitive semantics: it may not
NUL-terminate the destination, and it unconditionally zero-pads to the
full length, which isn't always needed. All former callers have been
migrated[1] to:

  - strscpy()        for NUL-terminated destinations
  - strscpy_pad()    for NUL-terminated destinations needing zero-padding
  - strtomem_pad()   for non-NUL-terminated fixed-width fields
  - memcpy_and_pad() for bounded copies with explicit padding
  - memcpy()         for known-length copies

Remove the generic implementation, its declaration, the FORTIFY_SOURCE
wrapper, and associated tests.

Link: https://github.com/KSPP/linux/issues/90 [1]
Signed-off-by: Kees Cook <kees@kernel.org>
This commit is contained in:
Kees Cook
2026-03-23 01:27:25 +00:00
parent 58c4ce8cd6
commit 079a028d63
8 changed files with 26 additions and 158 deletions

View File

@@ -88,22 +88,6 @@ char *strcpy(char *dest, const char *src)
EXPORT_SYMBOL(strcpy);
#endif
#ifndef __HAVE_ARCH_STRNCPY
char *strncpy(char *dest, const char *src, size_t count)
{
char *tmp = dest;
while (count) {
if ((*tmp = *src) != 0)
src++;
tmp++;
count--;
}
return dest;
}
EXPORT_SYMBOL(strncpy);
#endif
#ifdef __BIG_ENDIAN
# define ALLBUTLAST_BYTE_MASK (~255ul)
#else

View File

@@ -1,5 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#define TEST \
strncpy(small, large_src, sizeof(small) + 1)
#include "test_fortify.h"

View File

@@ -1,5 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
#define TEST \
strncpy(instance.buf, large_src, sizeof(instance.buf) + 1)
#include "test_fortify.h"

View File

@@ -458,7 +458,7 @@ static void fortify_test_strnlen(struct kunit *test)
/* Make string unterminated, and recount. */
pad.buf[end] = 'A';
end = sizeof(pad.buf);
/* Reading beyond with strncpy() will fail. */
/* Reading beyond will fail. */
KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 1), end);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 2), end);
@@ -531,64 +531,6 @@ static void fortify_test_strcpy(struct kunit *test)
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void fortify_test_strncpy(struct kunit *test)
{
struct fortify_padding pad = { };
char src[] = "Copy me fully into a small buffer and I will overflow!";
size_t sizeof_buf = sizeof(pad.buf);
OPTIMIZER_HIDE_VAR(sizeof_buf);
/* Destination is %NUL-filled to start with. */
KUNIT_EXPECT_EQ(test, pad.bytes_before, 0);
KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 2], '\0');
KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 3], '\0');
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Legitimate strncpy() 1 less than of max size. */
KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf - 1)
== pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* Only last byte should be %NUL */
KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 3], '\0');
/* Legitimate (though unterminated) max-size strncpy. */
KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf)
== pad.buf);
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
/* No trailing %NUL -- thanks strncpy API. */
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
/* But we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* Now verify that FORTIFY is working... */
KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf + 1)
== pad.buf);
/* Should catch the overflow. */
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1);
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
/* And further... */
KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf + 2)
== pad.buf);
/* Should catch the overflow. */
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
/* And we will not have gone beyond. */
KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
}
static void fortify_test_strscpy(struct kunit *test)
{
struct fortify_padding pad = { };
@@ -1100,7 +1042,6 @@ static struct kunit_case fortify_test_cases[] = {
KUNIT_CASE(fortify_test_strlen),
KUNIT_CASE(fortify_test_strnlen),
KUNIT_CASE(fortify_test_strcpy),
KUNIT_CASE(fortify_test_strncpy),
KUNIT_CASE(fortify_test_strscpy),
KUNIT_CASE(fortify_test_strcat),
KUNIT_CASE(fortify_test_strncat),