History

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.

Kees Cook committed 7 months ago

Commit 079a0281 parent 58c4ce88 files · +28 −160Browse files

Unified · Split

8 files
  1. Documentation/process/deprecated.rst +26 −21
  2. drivers/misc/lkdtm/fortify.c +1 −1
  3. include/linux/fortify-string.h +0 −49
  4. include/linux/string.h +0 −3
  5. lib/string.c +0 −16
  6. lib/test_fortify/write_overflow-strncpy-src.c +0 −5
  7. lib/test_fortify/write_overflow-strncpy.c +0 −5
  8. lib/tests/fortify_kunit.c +1 −60
Documentation/process/deprecated.rst+26−21
@@ -131,27 +131,32 @@ value of strcpy() was used, since strscpy() does not return a pointer to · 130 unmodified lines
131the destination, but rather a count of non-NUL bytes copied (or negative131the destination, but rather a count of non-NUL bytes copied (or negative
132errno when it truncates).132errno when it truncates).
133 133
134strncpy() on NUL-terminated strings134strncpy()
135-----------------------------------135---------
136Use of strncpy() does not guarantee that the destination buffer will136strncpy() has been removed from the kernel. All former callers have
137be NUL terminated. This can lead to various linear read overflows and137been migrated to safer alternatives.
138other misbehavior due to the missing termination. It also NUL-pads138
139the destination buffer if the source contents are shorter than the139strncpy() did not guarantee NUL-termination of the destination buffer,
140destination buffer size, which may be a needless performance penalty140leading to linear read overflows and other misbehavior. It also
141for callers using only NUL-terminated strings.141unconditionally NUL-padded the destination, which was a needless
142 142performance penalty for callers using only NUL-terminated strings. Due
143When the destination is required to be NUL-terminated, the replacement is143to its various behaviors, it was an ambiguous API for determining what
144strscpy(), though care must be given to any cases where the return value144an author's true intent was for the copy.
145of strncpy() was used, since strscpy() does not return a pointer to the145
146destination, but rather a count of non-NUL bytes copied (or negative146The replacements for strncpy() are:
147errno when it truncates). Any cases still needing NUL-padding should147
148instead use strscpy_pad().148- strscpy() when the destination must be NUL-terminated.
149 149- strscpy_pad() when the destination must be NUL-terminated and
150If a caller is using non-NUL-terminated strings, strtomem() should be150 zero-padded (e.g., structs crossing privilege boundaries).
151used, and the destinations should be marked with the `__nonstring151- memtostr() for NUL-terminated destinations from non-NUL-terminated
152<https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html>`_152 fixed-width sources (with the `__nonstring` attribute on the source).
153attribute to avoid future compiler warnings. For cases still needing153- memtostr_pad() for the same, but with zero-padding.
154NUL-padding, strtomem_pad() can be used.154- strtomem() for non-NUL-terminated fixed-width destinations, with
155 the `__nonstring` attribute on the destination.
156- strtomem_pad() for non-NUL-terminated destinations that also need
157 zero-padding.
158- memcpy_and_pad() for bounded copies from potentially unterminated
159 sources where the destination size is a runtime value.
155 160
156strlcpy()161strlcpy()
157---------162---------
drivers/misc/lkdtm/fortify.c+1−1
@@ -98,7 +98,7 @@ static void lkdtm_FORTIFY_MEM_MEMBER(void) · 97 unmodified lines
98 pr_info("trying to memcpy() past the end of a struct member...\n");98 pr_info("trying to memcpy() past the end of a struct member...\n");
99 99
100 /*100 /*
101 * strncpy(target.a, src, 20); will hit a compile error because the101 * memcpy(target.a, src, 20); will hit a compile error because the
102 * compiler knows at build time that target.a < 20 bytes. Use a102 * compiler knows at build time that target.a < 20 bytes. Use a
103 * volatile to force a runtime error.103 * volatile to force a runtime error.
104 */104 */
include/linux/fortify-string.h+0−49
@@ -26,7 +26,6 @@ · 25 unmodified lines
26#define FORTIFY_WRITE 126#define FORTIFY_WRITE 1
27 27
28#define EACH_FORTIFY_FUNC(macro) \28#define EACH_FORTIFY_FUNC(macro) \
29 macro(strncpy), \
30 macro(strnlen), \29 macro(strnlen), \
31 macro(strlen), \30 macro(strlen), \
32 macro(strscpy), \31 macro(strscpy), \
@@ -95,8 +94,6 @@ extern char *__underlying_strcat(char *p, const char *q) __RENAME(strcat); · 62 unmodified lines
95extern char *__underlying_strcpy(char *p, const char *q) __RENAME(strcpy);94extern char *__underlying_strcpy(char *p, const char *q) __RENAME(strcpy);
96extern __kernel_size_t __underlying_strlen(const char *p) __RENAME(strlen);95extern __kernel_size_t __underlying_strlen(const char *p) __RENAME(strlen);
97extern char *__underlying_strncat(char *p, const char *q, __kernel_size_t count) __RENAME(strncat);96extern char *__underlying_strncat(char *p, const char *q, __kernel_size_t count) __RENAME(strncat);
98extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size) __RENAME(strncpy);
99
100#else97#else
101 98
102#if defined(__SANITIZE_MEMORY__)99#if defined(__SANITIZE_MEMORY__)
@@ -120,7 +117,6 @@ extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size) · 17 unmodified lines
120#define __underlying_strcpy __builtin_strcpy117#define __underlying_strcpy __builtin_strcpy
121#define __underlying_strlen __builtin_strlen118#define __underlying_strlen __builtin_strlen
122#define __underlying_strncat __builtin_strncat119#define __underlying_strncat __builtin_strncat
123#define __underlying_strncpy __builtin_strncpy
124 120
125#endif121#endif
126 122
@@ -159,50 +155,6 @@ extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size) · 32 unmodified lines
159 (bounds) < (length) \155 (bounds) < (length) \
160)156)
161 157
162/**
163 * strncpy - Copy a string to memory with non-guaranteed NUL padding
164 *
165 * @p: pointer to destination of copy
166 * @q: pointer to NUL-terminated source string to copy
167 * @size: bytes to write at @p
168 *
169 * If strlen(@q) >= @size, the copy of @q will stop after @size bytes,
170 * and @p will NOT be NUL-terminated
171 *
172 * If strlen(@q) < @size, following the copy of @q, trailing NUL bytes
173 * will be written to @p until @size total bytes have been written.
174 *
175 * Do not use this function. While FORTIFY_SOURCE tries to avoid
176 * over-reads of @q, it cannot defend against writing unterminated
177 * results to @p. Using strncpy() remains ambiguous and fragile.
178 * Instead, please choose an alternative, so that the expectation
179 * of @p's contents is unambiguous:
180 *
181 * +--------------------+--------------------+------------+
182 * | **p** needs to be: | padded to **size** | not padded |
183 * +====================+====================+============+
184 * | NUL-terminated | strscpy_pad() | strscpy() |
185 * +--------------------+--------------------+------------+
186 * | not NUL-terminated | strtomem_pad() | strtomem() |
187 * +--------------------+--------------------+------------+
188 *
189 * Note strscpy*()'s differing return values for detecting truncation,
190 * and strtomem*()'s expectation that the destination is marked with
191 * __nonstring when it is a character array.
192 *
193 */
194__FORTIFY_INLINE __diagnose_as(__builtin_strncpy, 1, 2, 3)
195char *strncpy(char * const POS p, const char *q, __kernel_size_t size)
196{
197 const size_t p_size = __member_size(p);
198
199 if (__compiletime_lessthan(p_size, size))
200 __write_overflow();
201 if (p_size < size)
202 fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE, p_size, size, p);
203 return __underlying_strncpy(p, q, size);
204}
205
206extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen);158extern __kernel_size_t __real_strnlen(const char *, __kernel_size_t) __RENAME(strnlen);
207/**159/**
208 * strnlen - Return bounded count of characters in a NUL-terminated string160 * strnlen - Return bounded count of characters in a NUL-terminated string
@@ -809,7 +761,6 @@ char *strcpy(char * const POS p, const char * const POS q) · 600 unmodified lines
809#undef __underlying_strcpy761#undef __underlying_strcpy
810#undef __underlying_strlen762#undef __underlying_strlen
811#undef __underlying_strncat763#undef __underlying_strncat
812#undef __underlying_strncpy
813 764
814#undef POS765#undef POS
815#undef POS0766#undef POS0
include/linux/string.h+0−3
@@ -67,9 +67,6 @@ void *vmemdup_array_user(const void __user *src, size_t n, size_t size) · 66 unmodified lines
67#ifndef __HAVE_ARCH_STRCPY67#ifndef __HAVE_ARCH_STRCPY
68extern char * strcpy(char *,const char *);68extern char * strcpy(char *,const char *);
69#endif69#endif
70#ifndef __HAVE_ARCH_STRNCPY
71extern char * strncpy(char *,const char *, __kernel_size_t);
72#endif
73ssize_t sized_strscpy(char *, const char *, size_t);70ssize_t sized_strscpy(char *, const char *, size_t);
74 71
75/*72/*
lib/string.c+0−16
@@ -88,22 +88,6 @@ char *strcpy(char *dest, const char *src) · 87 unmodified lines
88EXPORT_SYMBOL(strcpy);88EXPORT_SYMBOL(strcpy);
89#endif89#endif
90 90
91#ifndef __HAVE_ARCH_STRNCPY
92char *strncpy(char *dest, const char *src, size_t count)
93{
94 char *tmp = dest;
95
96 while (count) {
97 if ((*tmp = *src) != 0)
98 src++;
99 tmp++;
100 count--;
101 }
102 return dest;
103}
104EXPORT_SYMBOL(strncpy);
105#endif
106
107#ifdef __BIG_ENDIAN91#ifdef __BIG_ENDIAN
108# define ALLBUTLAST_BYTE_MASK (~255ul)92# define ALLBUTLAST_BYTE_MASK (~255ul)
109#else93#else
lib/test_fortify/write_overflow-strncpy-src.c+0−5
@@ -1,5 +0,0 @@
1// SPDX-License-Identifier: GPL-2.0-only
2#define TEST \
3 strncpy(small, large_src, sizeof(small) + 1)
4
5#include "test_fortify.h"
lib/test_fortify/write_overflow-strncpy.c+0−5
@@ -1,5 +0,0 @@
1// SPDX-License-Identifier: GPL-2.0-only
2#define TEST \
3 strncpy(instance.buf, large_src, sizeof(instance.buf) + 1)
4
5#include "test_fortify.h"
lib/tests/fortify_kunit.c+1−60
@@ -458,7 +458,7 @@ static void fortify_test_strnlen(struct kunit *test) · 457 unmodified lines
458 /* Make string unterminated, and recount. */458 /* Make string unterminated, and recount. */
459 pad.buf[end] = 'A';459 pad.buf[end] = 'A';
460 end = sizeof(pad.buf);460 end = sizeof(pad.buf);
461 /* Reading beyond with strncpy() will fail. */461 /* Reading beyond will fail. */
462 KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 1), end);462 KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 1), end);
463 KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);463 KUNIT_EXPECT_EQ(test, fortify_read_overflows, 1);
464 KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 2), end);464 KUNIT_EXPECT_EQ(test, strnlen(pad.buf, end + 2), end);
@@ -531,64 +531,6 @@ static void fortify_test_strcpy(struct kunit *test) · 66 unmodified lines
531 KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);531 KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
532}532}
533 533
534static void fortify_test_strncpy(struct kunit *test)
535{
536 struct fortify_padding pad = { };
537 char src[] = "Copy me fully into a small buffer and I will overflow!";
538 size_t sizeof_buf = sizeof(pad.buf);
539
540 OPTIMIZER_HIDE_VAR(sizeof_buf);
541
542 /* Destination is %NUL-filled to start with. */
543 KUNIT_EXPECT_EQ(test, pad.bytes_before, 0);
544 KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
545 KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 2], '\0');
546 KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 3], '\0');
547 KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
548
549 /* Legitimate strncpy() 1 less than of max size. */
550 KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf - 1)
551 == pad.buf);
552 KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
553 /* Only last byte should be %NUL */
554 KUNIT_EXPECT_EQ(test, pad.buf[sizeof_buf - 1], '\0');
555 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
556 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 3], '\0');
557
558 /* Legitimate (though unterminated) max-size strncpy. */
559 KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf)
560 == pad.buf);
561 KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0);
562 /* No trailing %NUL -- thanks strncpy API. */
563 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0');
564 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
565 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
566 /* But we will not have gone beyond. */
567 KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
568
569 /* Now verify that FORTIFY is working... */
570 KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf + 1)
571 == pad.buf);
572 /* Should catch the overflow. */
573 KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1);
574 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0');
575 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
576 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
577 /* And we will not have gone beyond. */
578 KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
579
580 /* And further... */
581 KUNIT_ASSERT_TRUE(test, strncpy(pad.buf, src, sizeof_buf + 2)
582 == pad.buf);
583 /* Should catch the overflow. */
584 KUNIT_EXPECT_EQ(test, fortify_write_overflows, 2);
585 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 1], '\0');
586 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
587 KUNIT_EXPECT_NE(test, pad.buf[sizeof_buf - 2], '\0');
588 /* And we will not have gone beyond. */
589 KUNIT_EXPECT_EQ(test, pad.bytes_after, 0);
590}
591
592static void fortify_test_strscpy(struct kunit *test)534static void fortify_test_strscpy(struct kunit *test)
593{535{
594 struct fortify_padding pad = { };536 struct fortify_padding pad = { };
@@ -1100,7 +1042,6 @@ static struct kunit_case fortify_test_cases[] = { · 505 unmodified lines
1100 KUNIT_CASE(fortify_test_strlen),1042 KUNIT_CASE(fortify_test_strlen),
1101 KUNIT_CASE(fortify_test_strnlen),1043 KUNIT_CASE(fortify_test_strnlen),
1102 KUNIT_CASE(fortify_test_strcpy),1044 KUNIT_CASE(fortify_test_strcpy),
1103 KUNIT_CASE(fortify_test_strncpy),
1104 KUNIT_CASE(fortify_test_strscpy),1045 KUNIT_CASE(fortify_test_strscpy),
1105 KUNIT_CASE(fortify_test_strcat),1046 KUNIT_CASE(fortify_test_strcat),
1106 KUNIT_CASE(fortify_test_strncat),1047 KUNIT_CASE(fortify_test_strncat),