From: David Laight <[email protected]> If the string is constant there is no need to call __real_strlen() even when maxlen is a variable - just return the smaller value.
If the size of the string variable is unknown fortify_panic() can't be called, change the condition so that the compiler can optimise it away. Change __compiletime_strlen(p) to return a 'non-constant' value for non-constant strings (the same as __builtin_strlen()). Simplify since it is only necessary to check that the size is constant and that the last character is '\0'. Explain why it is different from __builtin_strlen(). Update the kunit tests to match. Signed-off-by: David Laight <[email protected]> --- include/linux/fortify-string.h | 44 +++++++++++++++++----------------- lib/tests/fortify_kunit.c | 8 +++---- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index 214d237214d5..758afd7c5f8a 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -58,19 +58,22 @@ void __read_overflow2_field(size_t avail, size_t wanted) __compiletime_warning(" void __write_overflow(void) __compiletime_error("detected write beyond size of object (1st parameter)"); void __write_overflow_field(size_t avail, size_t wanted) __compiletime_warning("detected write beyond size of field (1st parameter); maybe use struct_group()?"); -#define __compiletime_strlen(p) \ -({ \ - char *__p = (char *)(p); \ - size_t __ret = SIZE_MAX; \ - const size_t __p_size = __member_size(p); \ - if (__p_size != SIZE_MAX && \ - __builtin_constant_p(*__p)) { \ - size_t __p_len = __p_size - 1; \ - if (__builtin_constant_p(__p[__p_len]) && \ - __p[__p_len] == '\0') \ - __ret = __builtin_strlen(__p); \ - } \ - __ret; \ +/* + * __builtin_strlen() generates a compile-time error for 'const char foo[4] = "abcd";'. + * But that is a valid source for both strnlen() and strscpy() with a constant + * length less than or equal to 4. + * __compiletime_strlen() returns a non-constant for such items. + * Beware of strings with embedded '\0', __builtin_strlen() can be much smaller + * than __member_size(); + * The return value must only be used when it is a constant. + */ +extern size_t __fortify_undefined; +#define __compiletime_strlen(p) \ +({ \ + char *__p = (char *)(p); \ + const size_t __p_size = __member_size(p); \ + __p_size == SIZE_MAX || !statically_true(__p[__p_size - 1] == '\0') ? \ + __fortify_undefined : __builtin_strlen(__p); \ }) #if defined(__SANITIZE_ADDRESS__) @@ -215,16 +218,13 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size const size_t p_len = __compiletime_strlen(p); size_t ret; - /* We can take compile-time actions when maxlen is const. */ - if (__builtin_constant_p(maxlen) && p_len != SIZE_MAX) { - /* If p is const, we can use its compile-time-known len. */ - if (maxlen >= p_size) - return p_len; - } + /* If p is const, we can use its compile-time-known len. */ + if (__builtin_constant_p(p_len)) + return p_len < maxlen ? p_len : maxlen; /* Do not check characters beyond the end of p. */ - ret = __real_strnlen(p, maxlen < p_size ? maxlen : p_size); - if (p_size <= ret && maxlen != ret) + ret = __real_strnlen(p, p_size < maxlen ? p_size : maxlen); + if (ret == p_size && p_size < maxlen) fortify_panic(FORTIFY_FUNC_strnlen, FORTIFY_READ, p_size, ret + 1, ret); return ret; } @@ -289,7 +289,7 @@ __FORTIFY_INLINE ssize_t sized_strscpy(char * const POS p, const char * const PO if (statically_true(p_size < SIZE_MAX)) { len = __compiletime_strlen(q); - if (len < SIZE_MAX && statically_true(len < size)) { + if (statically_true(len < size)) { __underlying_memcpy(p, q, len + 1); return len; } diff --git a/lib/tests/fortify_kunit.c b/lib/tests/fortify_kunit.c index fc9c76f026d6..9b3b7201c02d 100644 --- a/lib/tests/fortify_kunit.c +++ b/lib/tests/fortify_kunit.c @@ -102,11 +102,11 @@ static void fortify_test_known_sizes(struct kunit *test) KUNIT_EXPECT_EQ(test, __compiletime_strlen(unchanging_12), 12); KUNIT_EXPECT_FALSE(test, __is_constexpr(__builtin_strlen(array_unknown))); - KUNIT_EXPECT_EQ(test, __compiletime_strlen(array_unknown), SIZE_MAX); + KUNIT_EXPECT_FALSE(test, __is_constexpr(__compiletime_strlen(array_unknown))); /* Externally defined and dynamically sized string pointer: */ KUNIT_EXPECT_FALSE(test, __is_constexpr(__builtin_strlen(test->name))); - KUNIT_EXPECT_EQ(test, __compiletime_strlen(test->name), SIZE_MAX); + KUNIT_EXPECT_FALSE(test, __is_constexpr(__compiletime_strlen(test->name))); } /* This is volatile so the optimizer can't perform DCE below. */ @@ -128,12 +128,12 @@ static noinline size_t want_minus_one(int pick) str = "1"; break; } - return __compiletime_strlen(str); + return __builtin_constant_p(__compiletime_strlen(str)); } static void fortify_test_control_flow_split(struct kunit *test) { - KUNIT_EXPECT_EQ(test, want_minus_one(pick), SIZE_MAX); + KUNIT_EXPECT_FALSE(test, want_minus_one(pick)); } #define KUNIT_EXPECT_BOS(test, p, expected, name) \ -- 2.39.5

