mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
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:
16
lib/string.c
16
lib/string.c
@@ -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
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#define TEST \
|
||||
strncpy(small, large_src, sizeof(small) + 1)
|
||||
|
||||
#include "test_fortify.h"
|
||||
@@ -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"
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user