The strlen(p) function-like macro uses: __is_constexpr(__builtin_strlen(p))
in which GCC would only yield true if the argument p is a string literal. Otherwise, GCC would return false even if p is a const string. In contrary, by using: __builtin_constant_p(__builtin_strlen(p)) then GCC can also recognizes when p is a compile time constant string. The above is illustrated in [1]. N.B.: clang is not impacted by any of this and gives the same results with either __is_constexpr() and __builting_constant_p(). Use __builtin_constant_p() instead of __is_constexpr() so that GCC can do the folding on constant strings. This done, strlen() does not require any more to be a function-like macro, so turn it into a static inline function. In the process, __fortify_strlen() had to be moved above strlen() so that it became visible to strlen(). On a side note, strlen() did a double expansion of its argument p. Turning it into an inline function also resolved this side issue. [1] https://godbolt.org/z/rqr3YvoP4 Signed-off-by: Vincent Mailhol <mailhol.vinc...@wanadoo.fr> --- This patch is the successor of patch [1] which was part of a longer series [2]. Meanwhile, I decided to split it, so I am sending this again, but as a stand-alone patch. Changelog since [1]: use __builtin_constant_p() instead and turn strlen() into an inline function [1] https://lore.kernel.org/all/20241203-is_constexpr-refactor-v1-6-4e4cbaecc...@wanadoo.fr/ [2] https://lore.kernel.org/all/20241203-is_constexpr-refactor-v1-0-4e4cbaecc...@wanadoo.fr/ --- include/linux/fortify-string.h | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index e4ce1cae03bf770047ce8a7c032b183683388cd5..bd22dd66e5f5b66ad839df42247e6436e0afd053 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -4,7 +4,6 @@ #include <linux/bitfield.h> #include <linux/bug.h> -#include <linux/const.h> #include <linux/limits.h> #define __FORTIFY_INLINE extern __always_inline __gnu_inline __overloadable @@ -241,6 +240,21 @@ __FORTIFY_INLINE __kernel_size_t strnlen(const char * const POS p, __kernel_size * possible for strlen() to be used on compile-time strings for use in * static initializers (i.e. as a constant expression). */ +__FORTIFY_INLINE __diagnose_as(__builtin_strlen, 1) +__kernel_size_t __fortify_strlen(const char * const POS p) +{ + const size_t p_size = __member_size(p); + __kernel_size_t ret; + + /* 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); + if (p_size <= ret) + fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ, p_size, ret + 1, ret); + return ret; +} + /** * strlen - Return count of characters in a NUL-terminated string * @@ -254,22 +268,12 @@ __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)) __FORTIFY_INLINE __diagnose_as(__builtin_strlen, 1) -__kernel_size_t __fortify_strlen(const char * const POS p) +__kernel_size_t strlen(const char *p) { - const size_t p_size = __member_size(p); - __kernel_size_t ret; - - /* 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); - if (p_size <= ret) - fortify_panic(FORTIFY_FUNC_strlen, FORTIFY_READ, p_size, ret + 1, ret); - return ret; + if (__builtin_constant_p(__builtin_strlen(p))) + return __builtin_strlen(p); + return __fortify_strlen(p); } /* Defined after fortified strnlen() to reuse it. */ --- base-commit: 9d89551994a430b50c4fffcb1e617a057fa76e20 change-id: 20250105-strlen_use_builtin_constant_p-515aca505ca4 Best regards, -- Vincent Mailhol <mailhol.vinc...@wanadoo.fr>