public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH next 0/3] fortify: Minor changes to strlen() and strnlen()
@ 2026-03-30 13:20 david.laight.linux
  2026-03-30 13:20 ` [PATCH next 1/3] fortify: replace __compiletime_lessthan() with statically_true() david.laight.linux
                   ` (2 more replies)
  0 siblings, 3 replies; 19+ messages in thread
From: david.laight.linux @ 2026-03-30 13:20 UTC (permalink / raw)
  To: Kees Cook, linux-hardening, linux-kernel; +Cc: David Laight

From: David Laight <david.laight.linux@gmail.com>

Looking at the object code for strnlen() of constant strings showed that
it sometimes contained an 'impossible to reach' error message.

Simplify some bits and adjust the condition so that the compiler
optimises it away.

David Laight (3):
  fortify: replace __compiletime_lessthan() with statically_true()
  fortify: Optimise strnlen()
  fortify: Simplify strlen() logic

 include/linux/fortify-string.h | 109 +++++++++++++++++----------------
 lib/tests/fortify_kunit.c      |   8 +--
 2 files changed, 59 insertions(+), 58 deletions(-)

-- 
2.39.5


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH next 1/3] fortify: replace __compiletime_lessthan() with statically_true()
  2026-03-30 13:20 [PATCH next 0/3] fortify: Minor changes to strlen() and strnlen() david.laight.linux
@ 2026-03-30 13:20 ` david.laight.linux
  2026-03-30 23:50   ` Kees Cook
  2026-03-30 13:20 ` [PATCH next 2/3] fortify: Optimise strnlen() david.laight.linux
  2026-03-30 13:20 ` [PATCH next 3/3] fortify: Simplify strlen() logic david.laight.linux
  2 siblings, 1 reply; 19+ messages in thread
From: david.laight.linux @ 2026-03-30 13:20 UTC (permalink / raw)
  To: Kees Cook, linux-hardening, linux-kernel; +Cc: David Laight

From: David Laight <david.laight.linux@gmail.com>

__compiletime_lessthan(a, b) is exactly the same as statically_true(a < b)
Replace the former by the latter.

Signed-off-by: David Laight <david.laight.linux@gmail.com>
---
 include/linux/fortify-string.h | 47 +++++++++++++++-------------------
 1 file changed, 21 insertions(+), 26 deletions(-)

diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h
index 171982e53c9a..214d237214d5 100644
--- a/include/linux/fortify-string.h
+++ b/include/linux/fortify-string.h
@@ -154,11 +154,6 @@ extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size)
 #define POS0			__pass_object_size(0)
 #endif
 
-#define __compiletime_lessthan(bounds, length)	(	\
-	__builtin_constant_p((bounds) < (length)) &&	\
-	(bounds) < (length)				\
-)
-
 /**
  * strncpy - Copy a string to memory with non-guaranteed NUL padding
  *
@@ -196,7 +191,7 @@ char *strncpy(char * const POS p, const char *q, __kernel_size_t size)
 {
 	const size_t p_size = __member_size(p);
 
-	if (__compiletime_lessthan(p_size, size))
+	if (statically_true(p_size < size))
 		__write_overflow();
 	if (p_size < size)
 		fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE, p_size, size, p);
@@ -287,14 +282,14 @@ __FORTIFY_INLINE ssize_t sized_strscpy(char * const POS p, const char * const PO
 	 * If size can be known at compile time and is greater than
 	 * p_size, generate a compile time write overflow error.
 	 */
-	if (__compiletime_lessthan(p_size, size))
+	if (statically_true(p_size < size))
 		__write_overflow();
 
 	/* Short-circuit for compile-time known-safe lengths. */
-	if (__compiletime_lessthan(p_size, SIZE_MAX)) {
+	if (statically_true(p_size < SIZE_MAX)) {
 		len = __compiletime_strlen(q);
 
-		if (len < SIZE_MAX && __compiletime_lessthan(len, size)) {
+		if (len < SIZE_MAX && statically_true(len < size)) {
 			__underlying_memcpy(p, q, len + 1);
 			return len;
 		}
@@ -469,12 +464,12 @@ __FORTIFY_INLINE bool fortify_memset_chk(__kernel_size_t size,
 		 */
 
 		/* Error when size is larger than enclosing struct. */
-		if (__compiletime_lessthan(p_size_field, p_size) &&
-		    __compiletime_lessthan(p_size, size))
+		if (statically_true(p_size_field < p_size) &&
+		    statically_true(p_size < size))
 			__write_overflow();
 
 		/* Warn when write size is larger than dest field. */
-		if (__compiletime_lessthan(p_size_field, size))
+		if (statically_true(p_size_field < size))
 			__write_overflow_field(p_size_field, size);
 	}
 	/*
@@ -557,15 +552,15 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
 		 */
 
 		/* Error when size is larger than enclosing struct. */
-		if (__compiletime_lessthan(p_size_field, p_size) &&
-		    __compiletime_lessthan(p_size, size))
+		if (statically_true(p_size_field < p_size) &&
+		    statically_true(p_size < size))
 			__write_overflow();
-		if (__compiletime_lessthan(q_size_field, q_size) &&
-		    __compiletime_lessthan(q_size, size))
+		if (statically_true(q_size_field < q_size) &&
+		    statically_true(q_size < size))
 			__read_overflow2();
 
 		/* Warn when write size argument larger than dest field. */
-		if (__compiletime_lessthan(p_size_field, size))
+		if (statically_true(p_size_field < size))
 			__write_overflow_field(p_size_field, size);
 		/*
 		 * Warn for source field over-read when building with W=1
@@ -573,8 +568,8 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
 		 * the same time.
 		 */
 		if ((IS_ENABLED(KBUILD_EXTRA_WARN1) ||
-		     __compiletime_lessthan(p_size_field, size)) &&
-		    __compiletime_lessthan(q_size_field, size))
+		     statically_true(p_size_field < size)) &&
+		    statically_true(q_size_field < size))
 			__read_overflow2_field(q_size_field, size);
 	}
 	/*
@@ -699,7 +694,7 @@ __FORTIFY_INLINE void *memscan(void * const POS0 p, int c, __kernel_size_t size)
 {
 	const size_t p_size = __struct_size(p);
 
-	if (__compiletime_lessthan(p_size, size))
+	if (statically_true(p_size < size))
 		__read_overflow();
 	if (p_size < size)
 		fortify_panic(FORTIFY_FUNC_memscan, FORTIFY_READ, p_size, size, NULL);
@@ -713,9 +708,9 @@ int memcmp(const void * const POS0 p, const void * const POS0 q, __kernel_size_t
 	const size_t q_size = __struct_size(q);
 
 	if (__builtin_constant_p(size)) {
-		if (__compiletime_lessthan(p_size, size))
+		if (statically_true(p_size < size))
 			__read_overflow();
-		if (__compiletime_lessthan(q_size, size))
+		if (statically_true(q_size < size))
 			__read_overflow2();
 	}
 	if (p_size < size)
@@ -730,7 +725,7 @@ void *memchr(const void * const POS0 p, int c, __kernel_size_t size)
 {
 	const size_t p_size = __struct_size(p);
 
-	if (__compiletime_lessthan(p_size, size))
+	if (statically_true(p_size < size))
 		__read_overflow();
 	if (p_size < size)
 		fortify_panic(FORTIFY_FUNC_memchr, FORTIFY_READ, p_size, size, NULL);
@@ -742,7 +737,7 @@ __FORTIFY_INLINE void *memchr_inv(const void * const POS0 p, int c, size_t size)
 {
 	const size_t p_size = __struct_size(p);
 
-	if (__compiletime_lessthan(p_size, size))
+	if (statically_true(p_size < size))
 		__read_overflow();
 	if (p_size < size)
 		fortify_panic(FORTIFY_FUNC_memchr_inv, FORTIFY_READ, p_size, size, NULL);
@@ -755,7 +750,7 @@ __FORTIFY_INLINE void *kmemdup_noprof(const void * const POS0 p, size_t size, gf
 {
 	const size_t p_size = __struct_size(p);
 
-	if (__compiletime_lessthan(p_size, size))
+	if (statically_true(p_size < size))
 		__read_overflow();
 	if (p_size < size)
 		fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ, p_size, size,
@@ -793,7 +788,7 @@ char *strcpy(char * const POS p, const char * const POS q)
 		return __underlying_strcpy(p, q);
 	size = strlen(q) + 1;
 	/* Compile-time check for const size overflow. */
-	if (__compiletime_lessthan(p_size, size))
+	if (statically_true(p_size < size))
 		__write_overflow();
 	/* Run-time check for dynamic size overflow. */
 	if (p_size < size)
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-30 13:20 [PATCH next 0/3] fortify: Minor changes to strlen() and strnlen() david.laight.linux
  2026-03-30 13:20 ` [PATCH next 1/3] fortify: replace __compiletime_lessthan() with statically_true() david.laight.linux
@ 2026-03-30 13:20 ` david.laight.linux
  2026-03-30 23:54   ` Kees Cook
                     ` (2 more replies)
  2026-03-30 13:20 ` [PATCH next 3/3] fortify: Simplify strlen() logic david.laight.linux
  2 siblings, 3 replies; 19+ messages in thread
From: david.laight.linux @ 2026-03-30 13:20 UTC (permalink / raw)
  To: Kees Cook, linux-hardening, linux-kernel; +Cc: David Laight

From: David Laight <david.laight.linux@gmail.com>

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 <david.laight.linux@gmail.com>
---
 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


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH next 3/3] fortify: Simplify strlen() logic
  2026-03-30 13:20 [PATCH next 0/3] fortify: Minor changes to strlen() and strnlen() david.laight.linux
  2026-03-30 13:20 ` [PATCH next 1/3] fortify: replace __compiletime_lessthan() with statically_true() david.laight.linux
  2026-03-30 13:20 ` [PATCH next 2/3] fortify: Optimise strnlen() david.laight.linux
@ 2026-03-30 13:20 ` david.laight.linux
  2026-03-31  6:07   ` Kees Cook
  2026-03-31  6:18   ` Kees Cook
  2 siblings, 2 replies; 19+ messages in thread
From: david.laight.linux @ 2026-03-30 13:20 UTC (permalink / raw)
  To: Kees Cook, linux-hardening, linux-kernel; +Cc: David Laight

From: David Laight <david.laight.linux@gmail.com>

The __builtin_choose_expr() doesn't gain you anything, replace with
a simple ?: operator.
Then __is_constexpr() can then be replaced with __builtin_constant_p().
This still works for static initialisers - the expression can contain
a function call - provided it isn't actually called.

Calling the strnlen() wrapper just add a lot more logic to read through.
Replace with a call to __real_strnlen().

However the compiler can decide that __builtin_constant_p(__builtin_strlen(p))
is false, but split as ret = __builtin_strlen(p); __builtin_constant_p(ret)
and it suddenly becomes true.
So an additional check is needed before calling __real_strnlen().

Signed-off-by: David Laight <david.laight.linux@gmail.com>
---
 include/linux/fortify-string.h | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h
index 758afd7c5f8a..6cd670492270 100644
--- a/include/linux/fortify-string.h
+++ b/include/linux/fortify-string.h
@@ -230,9 +230,8 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size
 }
 
 /*
- * Defined after fortified strnlen to reuse it. However, it must still be
- * possible for strlen() to be used on compile-time strings for use in
- * static initializers (i.e. as a constant expression).
+ * strlen() of a compile-time string needs to be a constant expression
+ * so it can be used, for example, as a static initializer.
  */
 /**
  * strlen - Return count of characters in a NUL-terminated string
@@ -247,9 +246,9 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size
  * Returns number of characters in @p (NOT including the final NUL).
  *
  */
-#define strlen(p)							\
-	__builtin_choose_expr(__is_constexpr(__builtin_strlen(p)),	\
-		__builtin_strlen(p), __fortify_strlen(p))
+#define strlen(p)					\
+	(__builtin_constant_p(__builtin_strlen(p)) ?	\
+		__builtin_strlen(p) : __fortify_strlen(p))
 __FORTIFY_INLINE __diagnose_as(__builtin_strlen, 1)
 __kernel_size_t __fortify_strlen(const char * const POS p)
 {
@@ -259,7 +258,14 @@ __kernel_size_t __fortify_strlen(const char * const POS p)
 	/* Give up if we don't know how large p is. */
 	if (p_size == SIZE_MAX)
 		return __underlying_strlen(p);
-	ret = strnlen(p, p_size);
+	/*
+	 * 'ret' can be constant here even though the __builtin_constant_p(__builtin_strlen(p))
+	 * in the #define wrapper is false.
+	 */
+	ret = __builtin_strlen(p);
+	if (__builtin_constant_p(ret))
+		return ret;
+	ret = __real_strnlen(p, p_size);
 	if (p_size <= ret)
 		fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ, p_size, ret + 1, ret);
 	return ret;
-- 
2.39.5


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH next 1/3] fortify: replace __compiletime_lessthan() with statically_true()
  2026-03-30 13:20 ` [PATCH next 1/3] fortify: replace __compiletime_lessthan() with statically_true() david.laight.linux
@ 2026-03-30 23:50   ` Kees Cook
  0 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2026-03-30 23:50 UTC (permalink / raw)
  To: david.laight.linux; +Cc: linux-hardening, linux-kernel

On Mon, Mar 30, 2026 at 02:20:01PM +0100, david.laight.linux@gmail.com wrote:
> From: David Laight <david.laight.linux@gmail.com>
> 
> __compiletime_lessthan(a, b) is exactly the same as statically_true(a < b)
> Replace the former by the latter.
> 
> Signed-off-by: David Laight <david.laight.linux@gmail.com>

This one is a clear win: I didn't know about statically_true(). This
results in identical code.

I'll pick this one up.

-Kees

> ---
>  include/linux/fortify-string.h | 47 +++++++++++++++-------------------
>  1 file changed, 21 insertions(+), 26 deletions(-)
> 
> diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h
> index 171982e53c9a..214d237214d5 100644
> --- a/include/linux/fortify-string.h
> +++ b/include/linux/fortify-string.h
> @@ -154,11 +154,6 @@ extern char *__underlying_strncpy(char *p, const char *q, __kernel_size_t size)
>  #define POS0			__pass_object_size(0)
>  #endif
>  
> -#define __compiletime_lessthan(bounds, length)	(	\
> -	__builtin_constant_p((bounds) < (length)) &&	\
> -	(bounds) < (length)				\
> -)
> -
>  /**
>   * strncpy - Copy a string to memory with non-guaranteed NUL padding
>   *
> @@ -196,7 +191,7 @@ char *strncpy(char * const POS p, const char *q, __kernel_size_t size)
>  {
>  	const size_t p_size = __member_size(p);
>  
> -	if (__compiletime_lessthan(p_size, size))
> +	if (statically_true(p_size < size))
>  		__write_overflow();
>  	if (p_size < size)
>  		fortify_panic(FORTIFY_FUNC_strncpy, FORTIFY_WRITE, p_size, size, p);
> @@ -287,14 +282,14 @@ __FORTIFY_INLINE ssize_t sized_strscpy(char * const POS p, const char * const PO
>  	 * If size can be known at compile time and is greater than
>  	 * p_size, generate a compile time write overflow error.
>  	 */
> -	if (__compiletime_lessthan(p_size, size))
> +	if (statically_true(p_size < size))
>  		__write_overflow();
>  
>  	/* Short-circuit for compile-time known-safe lengths. */
> -	if (__compiletime_lessthan(p_size, SIZE_MAX)) {
> +	if (statically_true(p_size < SIZE_MAX)) {
>  		len = __compiletime_strlen(q);
>  
> -		if (len < SIZE_MAX && __compiletime_lessthan(len, size)) {
> +		if (len < SIZE_MAX && statically_true(len < size)) {
>  			__underlying_memcpy(p, q, len + 1);
>  			return len;
>  		}
> @@ -469,12 +464,12 @@ __FORTIFY_INLINE bool fortify_memset_chk(__kernel_size_t size,
>  		 */
>  
>  		/* Error when size is larger than enclosing struct. */
> -		if (__compiletime_lessthan(p_size_field, p_size) &&
> -		    __compiletime_lessthan(p_size, size))
> +		if (statically_true(p_size_field < p_size) &&
> +		    statically_true(p_size < size))
>  			__write_overflow();
>  
>  		/* Warn when write size is larger than dest field. */
> -		if (__compiletime_lessthan(p_size_field, size))
> +		if (statically_true(p_size_field < size))
>  			__write_overflow_field(p_size_field, size);
>  	}
>  	/*
> @@ -557,15 +552,15 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
>  		 */
>  
>  		/* Error when size is larger than enclosing struct. */
> -		if (__compiletime_lessthan(p_size_field, p_size) &&
> -		    __compiletime_lessthan(p_size, size))
> +		if (statically_true(p_size_field < p_size) &&
> +		    statically_true(p_size < size))
>  			__write_overflow();
> -		if (__compiletime_lessthan(q_size_field, q_size) &&
> -		    __compiletime_lessthan(q_size, size))
> +		if (statically_true(q_size_field < q_size) &&
> +		    statically_true(q_size < size))
>  			__read_overflow2();
>  
>  		/* Warn when write size argument larger than dest field. */
> -		if (__compiletime_lessthan(p_size_field, size))
> +		if (statically_true(p_size_field < size))
>  			__write_overflow_field(p_size_field, size);
>  		/*
>  		 * Warn for source field over-read when building with W=1
> @@ -573,8 +568,8 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
>  		 * the same time.
>  		 */
>  		if ((IS_ENABLED(KBUILD_EXTRA_WARN1) ||
> -		     __compiletime_lessthan(p_size_field, size)) &&
> -		    __compiletime_lessthan(q_size_field, size))
> +		     statically_true(p_size_field < size)) &&
> +		    statically_true(q_size_field < size))
>  			__read_overflow2_field(q_size_field, size);
>  	}
>  	/*
> @@ -699,7 +694,7 @@ __FORTIFY_INLINE void *memscan(void * const POS0 p, int c, __kernel_size_t size)
>  {
>  	const size_t p_size = __struct_size(p);
>  
> -	if (__compiletime_lessthan(p_size, size))
> +	if (statically_true(p_size < size))
>  		__read_overflow();
>  	if (p_size < size)
>  		fortify_panic(FORTIFY_FUNC_memscan, FORTIFY_READ, p_size, size, NULL);
> @@ -713,9 +708,9 @@ int memcmp(const void * const POS0 p, const void * const POS0 q, __kernel_size_t
>  	const size_t q_size = __struct_size(q);
>  
>  	if (__builtin_constant_p(size)) {
> -		if (__compiletime_lessthan(p_size, size))
> +		if (statically_true(p_size < size))
>  			__read_overflow();
> -		if (__compiletime_lessthan(q_size, size))
> +		if (statically_true(q_size < size))
>  			__read_overflow2();
>  	}
>  	if (p_size < size)
> @@ -730,7 +725,7 @@ void *memchr(const void * const POS0 p, int c, __kernel_size_t size)
>  {
>  	const size_t p_size = __struct_size(p);
>  
> -	if (__compiletime_lessthan(p_size, size))
> +	if (statically_true(p_size < size))
>  		__read_overflow();
>  	if (p_size < size)
>  		fortify_panic(FORTIFY_FUNC_memchr, FORTIFY_READ, p_size, size, NULL);
> @@ -742,7 +737,7 @@ __FORTIFY_INLINE void *memchr_inv(const void * const POS0 p, int c, size_t size)
>  {
>  	const size_t p_size = __struct_size(p);
>  
> -	if (__compiletime_lessthan(p_size, size))
> +	if (statically_true(p_size < size))
>  		__read_overflow();
>  	if (p_size < size)
>  		fortify_panic(FORTIFY_FUNC_memchr_inv, FORTIFY_READ, p_size, size, NULL);
> @@ -755,7 +750,7 @@ __FORTIFY_INLINE void *kmemdup_noprof(const void * const POS0 p, size_t size, gf
>  {
>  	const size_t p_size = __struct_size(p);
>  
> -	if (__compiletime_lessthan(p_size, size))
> +	if (statically_true(p_size < size))
>  		__read_overflow();
>  	if (p_size < size)
>  		fortify_panic(FORTIFY_FUNC_kmemdup, FORTIFY_READ, p_size, size,
> @@ -793,7 +788,7 @@ char *strcpy(char * const POS p, const char * const POS q)
>  		return __underlying_strcpy(p, q);
>  	size = strlen(q) + 1;
>  	/* Compile-time check for const size overflow. */
> -	if (__compiletime_lessthan(p_size, size))
> +	if (statically_true(p_size < size))
>  		__write_overflow();
>  	/* Run-time check for dynamic size overflow. */
>  	if (p_size < size)
> -- 
> 2.39.5
> 

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-30 13:20 ` [PATCH next 2/3] fortify: Optimise strnlen() david.laight.linux
@ 2026-03-30 23:54   ` Kees Cook
  2026-03-31 22:09     ` David Laight
  2026-03-31  6:36   ` Kees Cook
  2026-04-01  0:15   ` kernel test robot
  2 siblings, 1 reply; 19+ messages in thread
From: Kees Cook @ 2026-03-30 23:54 UTC (permalink / raw)
  To: david.laight.linux; +Cc: linux-hardening, linux-kernel

On Mon, Mar 30, 2026 at 02:20:02PM +0100, david.laight.linux@gmail.com wrote:
> From: David Laight <david.laight.linux@gmail.com>
> 
> 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.

This will need very very careful checking. It took a lot of tweaking to
get __compiletime_strlen() to behave correctly across all supported
versions of GCC and Clang. It has some very ugly glitches possible. I'm
not saying your replacement is broken: I mean to say I'm going to need
to spend a bunch of time proving to myself that this is a safe
replacement.

Outside of that, though, I don't think dropping the various SIZE_MAX
tests is correct. Again, I'll need to spend some time double-checking.

-Kees

> 
> Signed-off-by: David Laight <david.laight.linux@gmail.com>
> ---
>  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
> 

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 3/3] fortify: Simplify strlen() logic
  2026-03-30 13:20 ` [PATCH next 3/3] fortify: Simplify strlen() logic david.laight.linux
@ 2026-03-31  6:07   ` Kees Cook
  2026-03-31  8:58     ` David Laight
  2026-03-31  6:18   ` Kees Cook
  1 sibling, 1 reply; 19+ messages in thread
From: Kees Cook @ 2026-03-31  6:07 UTC (permalink / raw)
  To: david.laight.linux; +Cc: linux-hardening, linux-kernel

On Mon, Mar 30, 2026 at 02:20:03PM +0100, david.laight.linux@gmail.com wrote:
> From: David Laight <david.laight.linux@gmail.com>
> 
> The __builtin_choose_expr() doesn't gain you anything, replace with
> a simple ?: operator.
> Then __is_constexpr() can then be replaced with __builtin_constant_p().
> This still works for static initialisers - the expression can contain
> a function call - provided it isn't actually called.

But __is_constexpr() != __builtin_constant_p(). I will go find the
horrible examples of why this, too, needed so much careful construction.

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 3/3] fortify: Simplify strlen() logic
  2026-03-30 13:20 ` [PATCH next 3/3] fortify: Simplify strlen() logic david.laight.linux
  2026-03-31  6:07   ` Kees Cook
@ 2026-03-31  6:18   ` Kees Cook
  1 sibling, 0 replies; 19+ messages in thread
From: Kees Cook @ 2026-03-31  6:18 UTC (permalink / raw)
  To: david.laight.linux; +Cc: linux-hardening, linux-kernel

On Mon, Mar 30, 2026 at 02:20:03PM +0100, david.laight.linux@gmail.com wrote:
> From: David Laight <david.laight.linux@gmail.com>
> 
> The __builtin_choose_expr() doesn't gain you anything, replace with
> a simple ?: operator.
> Then __is_constexpr() can then be replaced with __builtin_constant_p().
> This still works for static initialisers - the expression can contain
> a function call - provided it isn't actually called.
> 
> Calling the strnlen() wrapper just add a lot more logic to read through.
> Replace with a call to __real_strnlen().
> 
> However the compiler can decide that __builtin_constant_p(__builtin_strlen(p))
> is false, but split as ret = __builtin_strlen(p); __builtin_constant_p(ret)
> and it suddenly becomes true.
> So an additional check is needed before calling __real_strnlen().

Ah, there it is, exactly the issue I'm remembering, see
commit 4f3d1be4c2f8 ("compiler.h: add const_true()")

Instead of this patch, I should likely replace the open-coded versions
of const_true() here.

Regardless, we should not change this or the compiletime_strlen() macro
as you've suggested, IMO. They both work as they already are, so I see
no reason re-open this can of worms without a good reason.

What was the specific code that caused an issue for you with
__fortify_strlen ?

-Kees

> 
> Signed-off-by: David Laight <david.laight.linux@gmail.com>
> ---
>  include/linux/fortify-string.h | 20 +++++++++++++-------
>  1 file changed, 13 insertions(+), 7 deletions(-)
> 
> diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h
> index 758afd7c5f8a..6cd670492270 100644
> --- a/include/linux/fortify-string.h
> +++ b/include/linux/fortify-string.h
> @@ -230,9 +230,8 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size
>  }
>  
>  /*
> - * Defined after fortified strnlen to reuse it. However, it must still be
> - * possible for strlen() to be used on compile-time strings for use in
> - * static initializers (i.e. as a constant expression).
> + * strlen() of a compile-time string needs to be a constant expression
> + * so it can be used, for example, as a static initializer.
>   */
>  /**
>   * strlen - Return count of characters in a NUL-terminated string
> @@ -247,9 +246,9 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size
>   * Returns number of characters in @p (NOT including the final NUL).
>   *
>   */
> -#define strlen(p)							\
> -	__builtin_choose_expr(__is_constexpr(__builtin_strlen(p)),	\
> -		__builtin_strlen(p), __fortify_strlen(p))
> +#define strlen(p)					\
> +	(__builtin_constant_p(__builtin_strlen(p)) ?	\
> +		__builtin_strlen(p) : __fortify_strlen(p))
>  __FORTIFY_INLINE __diagnose_as(__builtin_strlen, 1)
>  __kernel_size_t __fortify_strlen(const char * const POS p)
>  {
> @@ -259,7 +258,14 @@ __kernel_size_t __fortify_strlen(const char * const POS p)
>  	/* Give up if we don't know how large p is. */
>  	if (p_size == SIZE_MAX)
>  		return __underlying_strlen(p);
> -	ret = strnlen(p, p_size);
> +	/*
> +	 * 'ret' can be constant here even though the __builtin_constant_p(__builtin_strlen(p))
> +	 * in the #define wrapper is false.
> +	 */
> +	ret = __builtin_strlen(p);
> +	if (__builtin_constant_p(ret))
> +		return ret;
> +	ret = __real_strnlen(p, p_size);
>  	if (p_size <= ret)
>  		fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ, p_size, ret + 1, ret);
>  	return ret;
> -- 
> 2.39.5
> 

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-30 13:20 ` [PATCH next 2/3] fortify: Optimise strnlen() david.laight.linux
  2026-03-30 23:54   ` Kees Cook
@ 2026-03-31  6:36   ` Kees Cook
  2026-03-31 10:14     ` David Laight
  2026-04-01  0:15   ` kernel test robot
  2 siblings, 1 reply; 19+ messages in thread
From: Kees Cook @ 2026-03-31  6:36 UTC (permalink / raw)
  To: david.laight.linux; +Cc: linux-hardening, linux-kernel

On Mon, Mar 30, 2026 at 02:20:02PM +0100, david.laight.linux@gmail.com wrote:
> From: David Laight <david.laight.linux@gmail.com>
> 
> 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.

See also
commit d07c0acb4f41 ("fortify: Fix __compiletime_strlen() under UBSAN_BOUNDS_LOCAL")

-Kees

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 3/3] fortify: Simplify strlen() logic
  2026-03-31  6:07   ` Kees Cook
@ 2026-03-31  8:58     ` David Laight
  0 siblings, 0 replies; 19+ messages in thread
From: David Laight @ 2026-03-31  8:58 UTC (permalink / raw)
  To: Kees Cook; +Cc: linux-hardening, linux-kernel

On Mon, 30 Mar 2026 23:07:01 -0700
Kees Cook <kees@kernel.org> wrote:

> On Mon, Mar 30, 2026 at 02:20:03PM +0100, david.laight.linux@gmail.com wrote:
> > From: David Laight <david.laight.linux@gmail.com>
> > 
> > The __builtin_choose_expr() doesn't gain you anything, replace with
> > a simple ?: operator.
> > Then __is_constexpr() can then be replaced with __builtin_constant_p().
> > This still works for static initialisers - the expression can contain
> > a function call - provided it isn't actually called.  
> 
> But __is_constexpr() != __builtin_constant_p(). I will go find the
> horrible examples of why this, too, needed so much careful construction.
> 

I know all about that.
Loosely __is_constexpr() requires that the initial compilation pass
sees something that is constant, whereas __builtin_constant_p() can
initially say 'not sure' and then a later compilation pass (eg after
function inlining) can determine that it is true after all.
There are a few places where C requires an 'integer constant expression',
otherwise __builtin_constant_p() is good enough.

__builtin_choose_expr() is also pretty much exactly the same as ?:
except that the types of the two expressions can differ.
In particular both bits of code have to compile without warnings
and have to be valid where it is used.

Note that you can have a function call in a static initialiser but not a
statement expression ({...}). C requires the expression be constant
- so the function can't be called, but it is syntactically valid.
So if you have a ({...}) in the unselected code of a __builtin_choose_expr()
you can't use it for a static initialiser.

Once you've relaxed the __builtin_choose_expr() to ?: you can relax
the test to __builtin_constant_p().
That is then (usually) true for constant values passed into inline functions.
I think I found a few cases where it made a difference.

	David



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-31  6:36   ` Kees Cook
@ 2026-03-31 10:14     ` David Laight
  2026-03-31 14:55       ` David Laight
  0 siblings, 1 reply; 19+ messages in thread
From: David Laight @ 2026-03-31 10:14 UTC (permalink / raw)
  To: Kees Cook; +Cc: linux-hardening, linux-kernel

On Mon, 30 Mar 2026 23:36:07 -0700
Kees Cook <kees@kernel.org> wrote:

> On Mon, Mar 30, 2026 at 02:20:02PM +0100, david.laight.linux@gmail.com wrote:
> > From: David Laight <david.laight.linux@gmail.com>
> > 
> > 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.  
> 
> See also
> commit d07c0acb4f41 ("fortify: Fix __compiletime_strlen() under UBSAN_BOUNDS_LOCAL")
> 
> -Kees
> 

It is far more subtle that that;
There shouldn't be a run-time access to __p[__p_len] at all.
And you really don't want one.

The problematic code was:
	if (__builtin_constant(__p[__p_len]) && __p[__p_len] == 0)
If the compiler thinks __p[__p_len] is constant then it will also
think that __p[0] is constant.
So the extra check should really make no difference.

I suspect this is what happened, consider:
	const char *foo;
	if (cond)
		foo = "foo";
	else
		foo = "fubar";
	return __compiletime_strlen(foo);

This is first converted to (ignoring any silly typos):
	const char *foo;
	if (cond)
		foo = "foo";
	else
		foo = "fubar";
	len = __builtin_object_size(foo,1) - 1; // 6 - 1
	if (__builtin_constant(foo[len]) && foo[len] == 0)
		return __builtin_strlen(foo);
	return SIZE_MAX;
Since foo isn't constant that returns SIZE_MAX.

The code is then moved into the conditional giving:
	if (cond) {
		foo = "foo";
		if (__builtin_constant(foo[5]) && foo[5] == 0)
			return __builtin_strlen(foo);
		return SIZE_MAX;
	} else {
		foo = "fubar";
		if (__builtin_constant(foo[5]) && foo[5] == 0)
			return __builtin_strlen(foo);
		return SIZE_MAX;
	}	

Since since foo is now 'pointer to constant' foo[] is constant, giving: 
	if (cond) {
		foo = "foo";
		if (foo[5] == 0)
			return __builtin_strlen(foo);
		return SIZE_MAX;
	} else {
		foo = "fubar";
		if (foo[5] == 0)
			return __builtin_strlen(foo);
		return SIZE_MAX;
	}	

In the bottom bit foo[5] is well defined and known to be zero.
In the top bit foo[5] is UB and gcc leaves the code it, giving:
	if (cond) {
		foo = "foo";
		if (foo[5] == 0)
			return __builtin_strlen(foo);
		return SIZE_MAX;
	} else {
		foo = "fubar";
		return __builtin_strlen(foo);
	}

and you get a real reference off the end of foo[] - which UBSAN_LOCAL_BOUNDS
rightly picks up on.

clang has a habit of silently deleting everything after UB, so might
generate:
	if (cond) {
		return whatever_happens_to_be_in_ax;
	} else {
		foo = "fubar";
		return __builtin_strlen(foo);
	}

The 'fix' of checking __p[0] actually makes no real difference.
I'd guess that the longer code block stops gcc moving the code
into the conditional and hides the bug.
But that could easily change by just breathing on the code somewhere
or in a future compiler version.

I suspect this should be a compiler bug.
But with the compiler behaving this way you can't write
__compiletime_strlen() with a check for the '\0' terminator.

That really means you can only use __builtin_strlen().
Which means you'll get a compile-time error from:
	char foo[3] = "foo";
	__builtin_strlen(foo);
rather the 'not a constant' when checking strscpy(tgt, foo, 3);
At a guess that never happens except in the tests.

	David


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-31 10:14     ` David Laight
@ 2026-03-31 14:55       ` David Laight
  2026-03-31 15:56         ` Kees Cook
  0 siblings, 1 reply; 19+ messages in thread
From: David Laight @ 2026-03-31 14:55 UTC (permalink / raw)
  To: Kees Cook; +Cc: linux-hardening, linux-kernel

On Tue, 31 Mar 2026 11:14:28 +0100
David Laight <david.laight.linux@gmail.com> wrote:

> On Mon, 30 Mar 2026 23:36:07 -0700
> Kees Cook <kees@kernel.org> wrote:
> 
> > On Mon, Mar 30, 2026 at 02:20:02PM +0100, david.laight.linux@gmail.com wrote:  
> > > From: David Laight <david.laight.linux@gmail.com>
> > > 
> > > 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.    
> > 
> > See also
> > commit d07c0acb4f41 ("fortify: Fix __compiletime_strlen() under UBSAN_BOUNDS_LOCAL")
> > 
> > -Kees
...
> That really means you can only use __builtin_strlen().
> Which means you'll get a compile-time error from:
> 	char foo[3] = "foo";
> 	__builtin_strlen(foo);
> rather the 'not a constant' when checking strscpy(tgt, foo, 3);
> At a guess that never happens except in the tests.

I wrote this change a while ago, I tried using __builtin_strlen()
but got a compile error in the tests.

However I've just built an x86-64 allmodconfig kernel on top of
my patches with:
#define __compiletime_strlen(p) __builtin_strlen()
so something must have changed since then (probably related to the
__nonstring changes).

So the actual fix for the above is to use __builtin_strlen().
IIRC it also detects a few more strings being constant.

	David



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-31 14:55       ` David Laight
@ 2026-03-31 15:56         ` Kees Cook
  0 siblings, 0 replies; 19+ messages in thread
From: Kees Cook @ 2026-03-31 15:56 UTC (permalink / raw)
  To: David Laight; +Cc: linux-hardening, linux-kernel

On Tue, Mar 31, 2026 at 03:55:41PM +0100, David Laight wrote:
> On Tue, 31 Mar 2026 11:14:28 +0100
> David Laight <david.laight.linux@gmail.com> wrote:
> 
> > On Mon, 30 Mar 2026 23:36:07 -0700
> > Kees Cook <kees@kernel.org> wrote:
> > 
> > > On Mon, Mar 30, 2026 at 02:20:02PM +0100, david.laight.linux@gmail.com wrote:  
> > > > From: David Laight <david.laight.linux@gmail.com>
> > > > 
> > > > 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.    
> > > 
> > > See also
> > > commit d07c0acb4f41 ("fortify: Fix __compiletime_strlen() under UBSAN_BOUNDS_LOCAL")
> > > 
> > > -Kees
> ...

Your series does pass all the wacky corner case tests that got added
over the years, though I need to go through older GCC and Clang releases
to double-check. I've pushed a branch to kernel.org for 0day testing:
https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git/log/?h=dev/v7.0-rc2/fortify-strlen

> > That really means you can only use __builtin_strlen().
> > Which means you'll get a compile-time error from:
> > 	char foo[3] = "foo";
> > 	__builtin_strlen(foo);
> > rather the 'not a constant' when checking strscpy(tgt, foo, 3);
> > At a guess that never happens except in the tests.
> 
> I wrote this change a while ago, I tried using __builtin_strlen()
> but got a compile error in the tests.
> 
> However I've just built an x86-64 allmodconfig kernel on top of
> my patches with:
> #define __compiletime_strlen(p) __builtin_strlen()
> so something must have changed since then (probably related to the
> __nonstring changes).
> 
> So the actual fix for the above is to use __builtin_strlen().
> IIRC it also detects a few more strings being constant.

Hrm, the "no NUL in the string" case is a weird one, but yeah, probably
hit by the __nonstring work.

Can you write up new test cases to validate what you're after? I can
update the branch I pushed.

-Kees

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-30 23:54   ` Kees Cook
@ 2026-03-31 22:09     ` David Laight
  2026-03-31 23:51       ` Kees Cook
  0 siblings, 1 reply; 19+ messages in thread
From: David Laight @ 2026-03-31 22:09 UTC (permalink / raw)
  To: Kees Cook; +Cc: linux-hardening, linux-kernel

On Mon, 30 Mar 2026 16:54:27 -0700
Kees Cook <kees@kernel.org> wrote:

> On Mon, Mar 30, 2026 at 02:20:02PM +0100, david.laight.linux@gmail.com wrote:
> > From: David Laight <david.laight.linux@gmail.com>
> > 
> > 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.  
> 
> This will need very very careful checking. It took a lot of tweaking to
> get __compiletime_strlen() to behave correctly across all supported
> versions of GCC and Clang. It has some very ugly glitches possible. I'm
> not saying your replacement is broken: I mean to say I'm going to need
> to spend a bunch of time proving to myself that this is a safe
> replacement.

There are two differences:
1) I removed the check for p[0] being constant, just p[size - 1].
2) The return value is 'not constant' (rather than SIZE_MAX) for
   non-constant strings.
   This includes any case where __builtin_strlen() returns a non-constant.

I did wonder what happens if you have:
static char foo[] = "foo";
then later:
	foo[1] = 0;
The compiler could determine that both foo[0] and foo[3] are never
changed, so (both versions of) __compiletime_strlen() would return
the non-constant result of __builtin_strlen().
The current compilers don't report foo[3] as being constant unless
the string itself in constant, but they might get 'better'. 

> Outside of that, though, I don't think dropping the various SIZE_MAX
> tests is correct. Again, I'll need to spend some time double-checking.

They needed changing to match the function returning 'not constant'
rather than SIZE_MAX.

Actually, as I noted in the other email (after writing, but not sending,
this one) __compiletime_strlen() is fundamentally broken with the current
compilers.
Any uses should be replaced by __builtin_strlen().

	David

> 
> -Kees
> 
> > 
> > Signed-off-by: David Laight <david.laight.linux@gmail.com>
> > ---
> >  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
> >   
> 


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-31 22:09     ` David Laight
@ 2026-03-31 23:51       ` Kees Cook
  2026-04-01 13:48         ` David Laight
  2026-04-03  8:50         ` David Laight
  0 siblings, 2 replies; 19+ messages in thread
From: Kees Cook @ 2026-03-31 23:51 UTC (permalink / raw)
  To: David Laight; +Cc: linux-hardening, linux-kernel

On Tue, Mar 31, 2026 at 11:09:14PM +0100, David Laight wrote:
> Any uses should be replaced by __builtin_strlen().

When I looked at this before, __builtin_strlen() flip to run-time strlen
on non-constant strings, which is why I had to jump through all the
hoops to avoid calling it in those cases.

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-30 13:20 ` [PATCH next 2/3] fortify: Optimise strnlen() david.laight.linux
  2026-03-30 23:54   ` Kees Cook
  2026-03-31  6:36   ` Kees Cook
@ 2026-04-01  0:15   ` kernel test robot
  2026-04-03  8:23     ` David Laight
  2 siblings, 1 reply; 19+ messages in thread
From: kernel test robot @ 2026-04-01  0:15 UTC (permalink / raw)
  To: david.laight.linux, Kees Cook, linux-hardening, linux-kernel
  Cc: llvm, oe-kbuild-all, David Laight

Hi,

kernel test robot noticed the following build errors:

[auto build test ERROR on next-20260327]

url:    https://github.com/intel-lab-lkp/linux/commits/david-laight-linux-gmail-com/fortify-replace-__compiletime_lessthan-with-statically_true/20260331-041123
base:   next-20260327
patch link:    https://lore.kernel.org/r/20260330132003.3379-3-david.laight.linux%40gmail.com
patch subject: [PATCH next 2/3] fortify: Optimise strnlen()
config: x86_64-buildonly-randconfig-001-20260331 (https://download.01.org/0day-ci/archive/20260401/202604010835.IWCsUV7z-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260401/202604010835.IWCsUV7z-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202604010835.IWCsUV7z-lkp@intel.com/

All errors (new ones prefixed by >>):

>> ld.lld: error: undefined symbol: __fortify_undefined
   >>> referenced by fortify-string.h:290 (include/linux/fortify-string.h:290)
   >>>               vmlinux.o:(parse_early_param)
   >>> referenced by fortify-string.h:218 (include/linux/fortify-string.h:218)
   >>>               vmlinux.o:(parse_early_param)
   >>> referenced by fortify-string.h:290 (include/linux/fortify-string.h:290)
   >>>               vmlinux.o:(setup_boot_config)
   >>> referenced 2103 more times

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-31 23:51       ` Kees Cook
@ 2026-04-01 13:48         ` David Laight
  2026-04-03  8:50         ` David Laight
  1 sibling, 0 replies; 19+ messages in thread
From: David Laight @ 2026-04-01 13:48 UTC (permalink / raw)
  To: Kees Cook; +Cc: linux-hardening, linux-kernel

On Tue, 31 Mar 2026 16:51:26 -0700
Kees Cook <kees@kernel.org> wrote:

> On Tue, Mar 31, 2026 at 11:09:14PM +0100, David Laight wrote:
> > Any uses should be replaced by __builtin_strlen().  
> 
> When I looked at this before, __builtin_strlen() flip to run-time strlen
> on non-constant strings, which is why I had to jump through all the
> hoops to avoid calling it in those cases.
> 

It should be fine provided that you check that the result is constant.
So doing:
	size_t len = __builtin_strlen(p);
	if (__builtin_constant_p(len))
		...
should never generate a run-time call to strlen().
(Probably the optimiser throws the call away because it knows it has
no side effects.)

I did notice that:
	if (__builtin_constant_p(__builtin_strlen(p)))
		...
is true less often (more so with clang than gcc).
I suspect than an early compiler pass generates 'no' rather than 'maybe'
when used inside an inlined function.

There is also something odd going on with one of the 'bot' builds.
I've compiled x86 allmodconfig with clang-18, no warning or link fails.
But I've not tried the specific config being tested.
The link for reproducing the error isn't entirely helpful.

Looking into that error I noticed that clang fails to optimise the
strscpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE) in
init/main.c:setup_boot_config() to a memcpy().
That means it calls strnlen() and then strscpy() - two scans to find
the length is also silly.
(At some point early on that code needs to call a real function to
do all the work instead of inlining everything into the caller.)

	David

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-04-01  0:15   ` kernel test robot
@ 2026-04-03  8:23     ` David Laight
  0 siblings, 0 replies; 19+ messages in thread
From: David Laight @ 2026-04-03  8:23 UTC (permalink / raw)
  To: kernel test robot
  Cc: Kees Cook, linux-hardening, linux-kernel, llvm, oe-kbuild-all

On Wed, 1 Apr 2026 08:15:22 +0800
kernel test robot <lkp@intel.com> wrote:

> Hi,
> 
> kernel test robot noticed the following build errors:
> 
> [auto build test ERROR on next-20260327]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/david-laight-linux-gmail-com/fortify-replace-__compiletime_lessthan-with-statically_true/20260331-041123
> base:   next-20260327
> patch link:    https://lore.kernel.org/r/20260330132003.3379-3-david.laight.linux%40gmail.com
> patch subject: [PATCH next 2/3] fortify: Optimise strnlen()
> config: x86_64-buildonly-randconfig-001-20260331 (https://download.01.org/0day-ci/archive/20260401/202604010835.IWCsUV7z-lkp@intel.com/config)
> compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260401/202604010835.IWCsUV7z-lkp@intel.com/reproduce)
> 
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202604010835.IWCsUV7z-lkp@intel.com/
> 
> All errors (new ones prefixed by >>):
> 
> >> ld.lld: error: undefined symbol: __fortify_undefined  
>    >>> referenced by fortify-string.h:290 (include/linux/fortify-string.h:290)
>    >>>               vmlinux.o:(parse_early_param)
>    >>> referenced by fortify-string.h:218 (include/linux/fortify-string.h:218)
>    >>>               vmlinux.o:(parse_early_param)
>    >>> referenced by fortify-string.h:290 (include/linux/fortify-string.h:290)
>    >>>               vmlinux.o:(setup_boot_config)
>    >>> referenced 2103 more times  
> 

I'm not sure how this can happen.
The references are all behind a __builtin_constant_p() check.
I haven't tested the actual config, but allmodconfig compiled for me
with clang 18.

	David

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH next 2/3] fortify: Optimise strnlen()
  2026-03-31 23:51       ` Kees Cook
  2026-04-01 13:48         ` David Laight
@ 2026-04-03  8:50         ` David Laight
  1 sibling, 0 replies; 19+ messages in thread
From: David Laight @ 2026-04-03  8:50 UTC (permalink / raw)
  To: Kees Cook; +Cc: linux-hardening, linux-kernel

On Tue, 31 Mar 2026 16:51:26 -0700
Kees Cook <kees@kernel.org> wrote:

> On Tue, Mar 31, 2026 at 11:09:14PM +0100, David Laight wrote:
> > Any uses should be replaced by __builtin_strlen().  
> 
> When I looked at this before, __builtin_strlen() flip to run-time strlen
> on non-constant strings, which is why I had to jump through all the
> hoops to avoid calling it in those cases.
> 

Thinks further.
Can you remember anywhere where:
	len = __builtin_strlen(x);
	if (__builtin_constant_p(len))
		...
actually called strlen() for a non-constant string.
I did do some tests and it was always optimised away.

I might try getting all this code to use a renamed strlen() and
then scan the entire kernel for references to strlen() itself.
There might be a small number of valid ones, but I'd expect most
would come from the compiler.
(Or get the compiler to generate 'rep scasb' and look for that.)

I suspect it might be enough to check that both str and str[0]
are constant before calling __builtin_strlen() and then check
the returned length is constant.
All the checks might be needed for:
	str = cond ? "four" : "f\0ur";
since the compile might realise that str[0] is always 'f' and
str[4] always 0 - but strlen differs.

However I suspect that __builtin_constant_p(array[index]) currently
requires that both the array and index are constant.
So testing array[0] is equivalent.

Given it needs all the separate paths, writing strscpy with:
	if (__builtin_constant_p(src[0]) {
		len = __builtin_strlen(src);
		if (__builtin_constant_p(len)) {
			/* code for constant length */
			return xxx;
		}
	}
	/* code for non-constant length */

One thing I did notice is that for:
char src[32];
char dst[32];

void func(void)
{
	strscpy(dst, src, 32);
}

it seems to generate a call to strnlen() followed by a call to
strscpy_sized().
That seems wrong, since all three lengths are 32 it should be
safe to just call strscpy_sized().
And having done the strnlen() it ought to use memcpy().
But, really most of that ought to be moved into the called function.
So you want:
int strcpy_sized(char *dst, const char *src, size_t dst_len, size_t src_len);
where the wrapper fills in src_len.

	David


			

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-04-03  8:50 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-30 13:20 [PATCH next 0/3] fortify: Minor changes to strlen() and strnlen() david.laight.linux
2026-03-30 13:20 ` [PATCH next 1/3] fortify: replace __compiletime_lessthan() with statically_true() david.laight.linux
2026-03-30 23:50   ` Kees Cook
2026-03-30 13:20 ` [PATCH next 2/3] fortify: Optimise strnlen() david.laight.linux
2026-03-30 23:54   ` Kees Cook
2026-03-31 22:09     ` David Laight
2026-03-31 23:51       ` Kees Cook
2026-04-01 13:48         ` David Laight
2026-04-03  8:50         ` David Laight
2026-03-31  6:36   ` Kees Cook
2026-03-31 10:14     ` David Laight
2026-03-31 14:55       ` David Laight
2026-03-31 15:56         ` Kees Cook
2026-04-01  0:15   ` kernel test robot
2026-04-03  8:23     ` David Laight
2026-03-30 13:20 ` [PATCH next 3/3] fortify: Simplify strlen() logic david.laight.linux
2026-03-31  6:07   ` Kees Cook
2026-03-31  8:58     ` David Laight
2026-03-31  6:18   ` Kees Cook

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox