From nobody Thu Apr 2 05:49:24 2026 Received: from mail-wm1-f52.google.com (mail-wm1-f52.google.com [209.85.128.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4F3643D16E9 for ; Mon, 30 Mar 2026 13:20:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774876813; cv=none; b=WZFqP4BOb35umWBj66h674k55JN3cGLDTYEjMt1622LzECeZIGRQxpE22LkjA/LmtGZXftzCHwB1meIVxWPmTEQSpNGQA5I47xPYgikLGLeTwRmEiUI5GuqRMzpT2lXkjCij5CCGnXrnRvGCqBQT+kCL5Pn/iARTzj1JjuMOhes= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774876813; c=relaxed/simple; bh=HZW2oqrFDucg0/tmmpLC50psVTIubhNBRGZjkaePAI0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=nQuCyN1DVFmIqaJLLfqJgmZQmpiKLaUW5x9J/n2CUnnIQ5q66IlFhkBejvGpslt+USXiFO+u1nwNiCGEnl5cgkPCIdnEqT+drSFSNIJdzy4SdZraVBRhSYMHNleeo2Rw55LbD+dUk3VitufE3P6267Qvcjv3Khbin3tmminrGWo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GdnnS/2x; arc=none smtp.client-ip=209.85.128.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GdnnS/2x" Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-486b9675d36so37729195e9.0 for ; Mon, 30 Mar 2026 06:20:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774876811; x=1775481611; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=3WJtJ9/xLPusko++aTLmU08qrObcTNq16sGagCzREHs=; b=GdnnS/2x7M/gzkx8TdzaO6tcuA7ISCTZ9TKbEhQFYqtIN4AaqiXtX9cNSuA9UCJdMy ISQ136dV4UwrpoE5JyBh9Err4cwD6DQdXZ8J5Y5RGDesK6ryp0dXeS980Xm8wybxrHdF keGf13qEoapkuwFGkvTQJ79uElru548gVBQ8EOJElrmo31YzbGfY2RcQxE/u1L9iDP7x qsYCJ2PkyhaGzIQml7edja9N9pFJyQkH41h10H9tmMQc3VM5niK4gCR7mOl2DMdr7JGX l+KYZskbyyUTKhOsko3+EtqiEK9aYxyaQxM1V2zZNx+gSvibFn9/OUJl8gF1JTGPWq6P w/IA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774876811; x=1775481611; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=3WJtJ9/xLPusko++aTLmU08qrObcTNq16sGagCzREHs=; b=kzJLlMpPQMQsEG/nOOlLsc1DG3QoJoAvwljLpCffPRXBGNOQGqGUmwBe04Cs8NLv/H 1QcySNNIgjqXhHv79mC3t9OGFBLIQBoyiQJWr8PmSEaGCmKrWXqv3yJItGUC97/WP3Vp Cb1Pi/UdqARHv1ZrmJWLpRTWTB89dIpAxr9NGCVOpXV+2DhbaiKamcVrDjpCHaUpaMAl G+K7jFE8NX8Rn12STZpk+KCC+Vzvtl/gQelEY33qbGfZD4u5bidHFjX5wz/jRW3E1+vR Rjg8IFfkF2tf//yClu/ro2+/30EAvQI3JLkn+tgJlZgPhPgUcYhz4hRHIUh6rA+yO2g6 4ABQ== X-Forwarded-Encrypted: i=1; AJvYcCU7JEqnDgLZNOuQieDan9+wheLjpXUpedsTst/IEBUmoEMX5eOvRPegNipIhxeWvRCP4VdM4TIDswQWZhg=@vger.kernel.org X-Gm-Message-State: AOJu0YwWJ5T/gp+Sfap0Ng0+PHLyPZxdbbDDnUx1quDnQm9pxn5DRGxX L0uelD+Jq81I0izv77vRk1wO2nkoHP+VRGvz3Fo+CWM9TT3hXhHbdiWaG+kJ4Nm1 X-Gm-Gg: ATEYQzwirCzRuNVKM45STa+SxiEA+g5Fp6exzn8EURMHTsxpVdASNkVqhj+egU0x0Sf 7VEt+APE3hpXD2b0mHORzY3tNT5eM8yQAIPzlijUp5n7FMjcEeG3qCs/oI0ZJH5e3UDSmtWNW8s wTA39sgPOWLN0LAOSVYfUpTuMAEY6SJdVtFmQaowlmc22Xn3z3WX0nYH9htdIxJpNY1nFWEmdUg PcWhRD+2STce0Mn+TwNq8jhb5toQ9CXch0JVRMmEOf94seGTldoIwKQzA5IxQrKTrhYSwg9v4/n pCg6z6zUgEOlPQPlPlwU6x6w6mmRaYgfdiyyVwN+NusQ/01CrB6SDkjOe5n4/Ukq3ly+4yXAju5 ACQ8WagXWHOOycFJZjz8o4BckBlQCIPtYhIaCMb3TmMONupr4qZUNH3kalFqWNLjDpSInH2hvus KDRQeFzpACImu3jh2DKBHB+IAb2Nv00wQKaN6BrlGxjMacZXurygLxAyTWlibB/BDSpJWwCHYS1 qfCbjy7p4dDpFi6GVE= X-Received: by 2002:a05:600c:154c:b0:480:2521:4d92 with SMTP id 5b1f17b1804b1-48727ede0admr214729635e9.24.1774876810462; Mon, 30 Mar 2026 06:20:10 -0700 (PDT) Received: from snowdrop.snailnet.com (82-69-66-36.dsl.in-addr.zen.co.uk. [82.69.66.36]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48725eb52e5sm81322315e9.7.2026.03.30.06.20.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 30 Mar 2026 06:20:10 -0700 (PDT) From: david.laight.linux@gmail.com To: Kees Cook , linux-hardening@vger.kernel.org, linux-kernel@vger.kernel.org Cc: David Laight Subject: [PATCH next 2/3] fortify: Optimise strnlen() Date: Mon, 30 Mar 2026 14:20:02 +0100 Message-Id: <20260330132003.3379-3-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260330132003.3379-1-david.laight.linux@gmail.com> References: <20260330132003.3379-1-david.laight.linux@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: David Laight 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 --- 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 siz= e of object (1st parameter)"); void __write_overflow_field(size_t avail, size_t wanted) __compiletime_war= ning("detected write beyond size of field (1st parameter); maybe use struct= _group()?"); =20 -#define __compiletime_strlen(p) \ -({ \ - char *__p =3D (char *)(p); \ - size_t __ret =3D SIZE_MAX; \ - const size_t __p_size =3D __member_size(p); \ - if (__p_size !=3D SIZE_MAX && \ - __builtin_constant_p(*__p)) { \ - size_t __p_len =3D __p_size - 1; \ - if (__builtin_constant_p(__p[__p_len]) && \ - __p[__p_len] =3D=3D '\0') \ - __ret =3D __builtin_strlen(__p); \ - } \ - __ret; \ +/* + * __builtin_strlen() generates a compile-time error for 'const char foo[4= ] =3D "abcd";'. + * But that is a valid source for both strnlen() and strscpy() with a cons= tant + * 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 sm= aller + * 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 =3D (char *)(p); \ + const size_t __p_size =3D __member_size(p); \ + __p_size =3D=3D SIZE_MAX || !statically_true(__p[__p_size - 1] =3D=3D '\0= ') ? \ + __fortify_undefined : __builtin_strlen(__p); \ }) =20 #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 =3D __compiletime_strlen(p); size_t ret; =20 - /* We can take compile-time actions when maxlen is const. */ - if (__builtin_constant_p(maxlen) && p_len !=3D SIZE_MAX) { - /* If p is const, we can use its compile-time-known len. */ - if (maxlen >=3D 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; =20 /* Do not check characters beyond the end of p. */ - ret =3D __real_strnlen(p, maxlen < p_size ? maxlen : p_size); - if (p_size <=3D ret && maxlen !=3D ret) + ret =3D __real_strnlen(p, p_size < maxlen ? p_size : maxlen); + if (ret =3D=3D 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 =3D __compiletime_strlen(q); =20 - 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 *te= st) KUNIT_EXPECT_EQ(test, __compiletime_strlen(unchanging_12), 12); =20 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_unknow= n))); =20 /* 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))= ); } =20 /* 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 =3D "1"; break; } - return __compiletime_strlen(str); + return __builtin_constant_p(__compiletime_strlen(str)); } =20 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)); } =20 #define KUNIT_EXPECT_BOS(test, p, expected, name) \ --=20 2.39.5