From nobody Wed Apr 1 10:26:27 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 949BD425CEE; Tue, 31 Mar 2026 16:37:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774975046; cv=none; b=By/hSEYB6VYz02j3v+XYzNuNHEvFlpFo+gWWZcEsHuglM89FLIJvCtpuAITxZPF2Ji0g0UdJZLvVlbhbSzMOFI+C5x+s33nzgKwN23WQ3+4RSZEK6sTVDKA97ELlLWZxYBiJwITNqgEsIXyfOuTwPXhYPMQP/J7jTToQEFajBFA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774975046; c=relaxed/simple; bh=8ICpeLoyfF2BIMXBU7MfdBMil+QYa1a2pfcUa1hQh+c=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=iKJ/wn3xL92dei7hodEkDRv91KsFkWBD8f/gtbu9bfsfqzqQXaJG8cg7dKv9EKbF4RYSVzSttqgJ414+tDR6N1TZyw2ELMJlLvjL40TGnDAh0bqQHC1RVFaU2WyOyzVXY42Lb/qxrXmtjnlA0vb7vSkNstT0evS0PGXtYm6jmcM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=o9I9T/0E; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="o9I9T/0E" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 512F7C2BCB1; Tue, 31 Mar 2026 16:37:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1774975046; bh=8ICpeLoyfF2BIMXBU7MfdBMil+QYa1a2pfcUa1hQh+c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=o9I9T/0EVFqYtlh0EtKMYzkcoKp4fU7mtqknM9yHsHq3gJeOBQD8xYQSkTns6xxrw GnPGvq1iti2AykrCx+ot7rXEpAPYD8Nl7zotOBjl9vNccYu+K/7r2tvK2H96bPrZ3e UDyvEs0bQM4wVBFxhH59HfJQD4YlZ2MjPI0s0YXs2YmCqXTNOqAYXKLlo6HleWhVqq aVJDM21YM3ihEA1mpjT1E5BVBCpyL07mSAjSHga2XJcivhelzHrF0mCLznCWzoqySZ LaWVnE8s1LfQ2QkGYqpAqRZoInX/lrB7/JzxWlcxVzBuPULzCV7xhPa8JkH8FiGJbO rQm4TnL0HXwPw== From: Kees Cook To: Peter Zijlstra Cc: Kees Cook , Arnd Bergmann , Greg Kroah-Hartman , Shuah Khan , linux-kselftest@vger.kernel.org, Justin Stitt , Linus Torvalds , Marco Elver , Jonathan Corbet , Nathan Chancellor , Nicolas Schier , Miguel Ojeda , Andrew Morton , linux-kernel@vger.kernel.org, kasan-dev@googlegroups.com, linux-hardening@vger.kernel.org, linux-doc@vger.kernel.org, linux-kbuild@vger.kernel.org, llvm@lists.linux.dev Subject: [PATCH 4/5] lkdtm/bugs: Add basic Overflow Behavior Types test Date: Tue, 31 Mar 2026 09:37:22 -0700 Message-Id: <20260331163725.2765789-4-kees@kernel.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260331163716.work.696-kees@kernel.org> References: <20260331163716.work.696-kees@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=10793; i=kees@kernel.org; h=from:subject; bh=8ICpeLoyfF2BIMXBU7MfdBMil+QYa1a2pfcUa1hQh+c=; b=owGbwMvMwCVmps19z/KJym7G02pJDJmnfzjVvF91Tmzp1mmz667KPNVp49U5pK3buvK+84c8J oPtD7ufdpSyMIhxMciKKbIE2bnHuXi8bQ93n6sIM4eVCWQIAxenAEzk7h1Ghkk/tZKWfDz+/6S3 9LUvv05kNkz7xqfz0eMTz9xbPwyLQ30Y/mcUf256m5izYz3rm8ApHLrvcr2fa055Elztsf4FV2R WCRcA X-Developer-Key: i=kees@kernel.org; a=openpgp; fpr=A5C3F68F229DD60F723E6E138972F4DFDC6DC026 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Exercise the end-to-end build and trap infrastructure in the kernel for __ob_trap, __ob_wrap, and associated sanitizer ignore patterns (i.e. idiom exclusions). Add a test for each of the basic overflow conditions under CONFIG_OVERFLOW_BEHAVIOR_TYPES=3Dy, as well as the corner cases associated with promotion, casting, etc. For example, executing this test with CONFIG_OVERFLOW_BEHAVIOR_TYPES_WARN= =3Dy (instead of CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP=3Dy), will show: $ echo OBT_ASSIGN_TRUNCATE_TO | cat >/sys/kernel/debug/provoke-crash/DI= RECT $ dmesg ... lkdtm: Performing direct entry OBT_ASSIGN_TRUNCATE_TO UBSAN: implicit-conversion in ../drivers/misc/lkdtm/bugs.c:825:10 cannot represent 'int' value 2147483647 during reference binding to 'u8= t' (aka '__ob_trap u8'), truncated to 255 Signed-off-by: Kees Cook --- Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Shuah Khan Cc: --- drivers/misc/lkdtm/bugs.c | 253 ++++++++++++++++++++++++ tools/testing/selftests/lkdtm/tests.txt | 10 + 2 files changed, 263 insertions(+) diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index e0098f314570..f00c9099957e 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -817,6 +817,249 @@ static noinline void lkdtm_CORRUPT_PAC(void) #endif } =20 +static void lkdtm_OBT_ASSIGN_TRUNCATE_TO(void) +{ + volatile int big =3D INT_MAX; + volatile int wide_low_value =3D 5; + u8 __ob_trap narrow_low_value =3D 0; + s32 __ob_trap same =3D 0; + u8 __ob_trap small =3D 0; + + pr_info("Performing same-width assignment to OBT\n"); + same =3D big; + + pr_info("Performing small-value assignment to OBT\n"); + narrow_low_value =3D wide_low_value; + + pr_info("Expecting trap on truncated assignment to OBT\n"); + small =3D big; + + pr_err("FAIL: survived overflowing truncated assignment to OBT: %d -> %u = (ok: %d -> %u)\n", + same, small, wide_low_value, narrow_low_value); + pr_expected_config(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP); +} + +static void lkdtm_OBT_ASSIGN_TRUNCATE_FROM(void) +{ + volatile s32 __ob_trap big =3D INT_MAX; + volatile s32 __ob_trap wide_low_value =3D 5; + u8 narrow_low_value =3D 0; + s32 same =3D 0; + u8 small =3D 0; + + pr_info("Performing same-width assignment from OBT\n"); + same =3D big; + + pr_info("Performing small-value assignment from OBT\n"); + narrow_low_value =3D wide_low_value; + + pr_info("Expecting trap on truncated assignment from OBT\n"); + small =3D big; + + pr_err("FAIL: survived overflowing truncated assignment from OBT: %d -> %= u (ok: %d -> %u)\n", + same, small, wide_low_value, narrow_low_value); + pr_expected_config(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP); +} + +static void lkdtm_OBT_CAST_TRUNCATE(void) +{ + volatile u32 __ob_trap big =3D INT_MAX; + u32 trunc =3D 0; + u32 small =3D 0; + + pr_info("Performing wrapping too-small cast\n"); + trunc =3D (u16 __ob_wrap)big; + + pr_info("Expecting trap on too-small cast\n"); + small =3D (s16)big; + + pr_err("FAIL: survived truncated casting: %u -> %u (ok: %u -> %u)\n", + big, small, big, trunc); + pr_expected_config(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP); +} + +static void lkdtm_OBT_CAST_SIGNED(void) +{ + volatile u32 __ob_trap big =3D UINT_MAX; + s32 neg =3D 0; + s32 small =3D 0; + + pr_info("Performing explicit sign-changing cast\n"); + neg =3D (s32 __ob_wrap)big; + + pr_info("Expecting trap on unexpected sign-changing cast\n"); + small =3D (s32)big; + + pr_err("FAIL: survived lossy sign conversion: %u -> %d (forced: %u -> %d)= \n", + big, small, big, neg); + pr_expected_config(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP); +} + +static void lkdtm_OBT_MUL(void) +{ + /* Promotion means no overflow checking can happen. */ + volatile u8 __ob_trap a8 =3D 100; + volatile u8 __ob_trap b8 =3D 3; + unsigned int promoted; + /* 32-bit or larger, however, get checked. */ + volatile u32 __ob_trap a =3D UINT_MAX - 1; + volatile u32 __ob_trap b =3D 2; + unsigned long long happy; + unsigned long long outcome; + + /* Promotion means a * b happens as "int __ob_trap", so no trap. */ + pr_info("Performing promoted overflowing unsigned multiplication\n"); + promoted =3D a8 * b8; + + pr_info("Performing non-overflowing unsigned multiplication\n"); + happy =3D b * b; + + pr_info("Expecting trap on overflowing unsigned multiplication\n"); + outcome =3D a * b; + + pr_err("FAIL: survived unsigned multiplication overflow: %u * %u -> %llu = (ok: %u * %u -> %llu, %u)\n", + a, b, outcome, b, b, happy, promoted); + pr_expected_config(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP); +} + +static void lkdtm_OBT_MUL_SIGNED(void) +{ + /* Promotion means no overflow checking can happen. */ + volatile s8 __ob_trap a8 =3D 100; + volatile s8 __ob_trap b8 =3D 3; + int promoted; + /* 32-bit or larger, however, get checked. */ + volatile s32 __ob_trap a =3D INT_MAX - 1; + volatile s32 __ob_trap b =3D 2; + signed long long happy; + signed long long outcome; + + /* Promotion means a8 * b8 happens as "int __ob_trap", so no trap. */ + pr_info("Performing promoted overflowing signed multiplication\n"); + promoted =3D a8 * b8; + + pr_info("Performing non-overflowing signed multiplication\n"); + happy =3D b * b; + + pr_info("Expecting trap on overflowing signed multiplication\n"); + outcome =3D a * b; + + pr_err("FAIL: survived signed multiplication overflow: %d * %d -> %lld (o= k: %d * %d -> %lld, %d)\n", + a, b, outcome, b, b, happy, promoted); + pr_expected_config(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP); +} + +static void lkdtm_OBT_ADD(void) +{ + /* Promotion means no overflow checking can happen. */ + volatile u8 __ob_trap a8 =3D 250; + volatile u8 __ob_trap b8 =3D 30; + unsigned int promoted; + /* 32-bit or larger, however, get checked. */ + volatile u32 __ob_trap a =3D UINT_MAX - 1; + volatile u32 __ob_trap b =3D 2; + unsigned long long happy; + unsigned long long outcome; + + /* Promotion means a8 + b8 happens as "int __ob_trap", so no trap. */ + pr_info("Performing promoted overflowing unsigned addition\n"); + promoted =3D a8 + b8; + + pr_info("Performing idiomatic unsigned overflow addition test\n"); + if (a + b < a) { + /* Report status so test isn't elided by compiler. */ + pr_info("ok: overflow contained by conditional\n"); + } + + pr_info("Performing non-overflowing unsigned addition\n"); + happy =3D b + b; + + pr_info("Expecting trap on overflowing unsigned addition\n"); + outcome =3D a + b; + + pr_err("FAIL: survived unsigned addition overflow: %u + %u -> %llu (ok: %= u + %u -> %llu)\n", + a, b, outcome, b, b, happy); + pr_expected_config(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP); +} + +static void lkdtm_OBT_ADD_SIGNED(void) +{ + /* Promotion means no overflow checking can happen. */ + volatile s8 __ob_trap a8 =3D 120; + volatile s8 __ob_trap b8 =3D 30; + int promoted; + /* 32-bit or larger, however, get checked. */ + volatile s32 __ob_trap a =3D INT_MAX - 1; + volatile s32 __ob_trap b =3D 2; + signed long long happy; + signed long long outcome; + + /* Promotion means a8 + b8 happens as "int __ob_trap", so no trap. */ + pr_info("Performing promoted overflowing signed addition\n"); + promoted =3D a8 + b8; + + pr_info("Performing idiomatic signed overflow addition test\n"); + if (a + b < a) { + /* Report status so test isn't elided by compiler. */ + pr_info("ok: overflow contained by conditional\n"); + } + + pr_info("Performing non-overflowing signed addition\n"); + happy =3D b + b; + + pr_info("Expecting trap on overflowing signed addition\n"); + outcome =3D a + b; + + pr_err("FAIL: survived signed addition overflow: %u + %u -> %llu (ok: %u = + %u -> %llu)\n", + a, b, outcome, b, b, happy); + pr_expected_config(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP); +} + +static void lkdtm_OBT_NEGATED_UNSIGNED(void) +{ + volatile unsigned long __ob_trap value =3D 256; + size_t outcome; + + pr_info("Expecting trap on overflowing unsigned negation\n"); + outcome =3D value & -value; + + pr_err("FAIL: survived negated unsigned value: %lu -> %zu\n", + value, outcome); + pr_expected_config(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP); +} + +static void lkdtm_OBT_POSTFIX_OPERATORS(void) +{ + volatile int target =3D 300; + volatile int flag =3D 0; + int i; + u8 __ob_wrap wrapper =3D 0; /* Explicitly wrapping. */ + u8 __ob_trap counter =3D 0; + + pr_info("Performing u8 __ob_wrap post-increment past 255\n"); + for (i =3D 0; i < target; i++) + wrapper++; + if (wrapper !=3D 44) + pr_err("FAIL: wrapped incorrecty: %u\n", wrapper); + + pr_info("Performing idiomatic post-decrement zero test\n"); + counter =3D target / 2; + while (counter--) + if (flag) + break; + if (counter !=3D 255) + pr_err("FAIL: u8 __ob_trap post-decrement zero-test did not wrap: %u\n", + counter); + + pr_info("Expecting trap on u8 __ob_trap post-increment past 255\n"); + counter =3D 0; + for (i =3D 0; i < target; i++) + counter++; + + pr_err("FAIL: survived overflowed post-increment: %u\n", counter); + pr_expected_config(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP); +} + static struct crashtype crashtypes[] =3D { CRASHTYPE(PANIC), CRASHTYPE(PANIC_STOP_IRQOFF), @@ -850,6 +1093,16 @@ static struct crashtype crashtypes[] =3D { CRASHTYPE(UNSET_SMEP), CRASHTYPE(DOUBLE_FAULT), CRASHTYPE(CORRUPT_PAC), + CRASHTYPE(OBT_ASSIGN_TRUNCATE_TO), + CRASHTYPE(OBT_ASSIGN_TRUNCATE_FROM), + CRASHTYPE(OBT_CAST_TRUNCATE), + CRASHTYPE(OBT_CAST_SIGNED), + CRASHTYPE(OBT_MUL), + CRASHTYPE(OBT_MUL_SIGNED), + CRASHTYPE(OBT_ADD), + CRASHTYPE(OBT_ADD_SIGNED), + CRASHTYPE(OBT_NEGATED_UNSIGNED), + CRASHTYPE(OBT_POSTFIX_OPERATORS), }; =20 struct crashtype_category bugs_crashtypes =3D { diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selfte= sts/lkdtm/tests.txt index e62b85b591be..231299ba3959 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -87,3 +87,13 @@ FORTIFY_STR_MEMBER detected buffer overflow FORTIFY_MEM_OBJECT detected buffer overflow FORTIFY_MEM_MEMBER detected field-spanning write PPC_SLB_MULTIHIT Recovered +OBT_ASSIGN_TRUNCATE_TO traps: UBSAN: integer truncation +OBT_ASSIGN_TRUNCATE_FROM traps: UBSAN: integer truncation +OBT_CAST_TRUNCATE traps: UBSAN: integer truncation +OBT_CAST_SIGNED traps: UBSAN: integer truncation +OBT_MUL traps: UBSAN: integer multiplication overflow +OBT_MUL_SIGNED traps: UBSAN: integer multiplication overflow +OBT_ADD traps: UBSAN: integer addition overflow +OBT_ADD_SIGNED traps: UBSAN: integer addition overflow +OBT_NEGATED_UNSIGNED traps: UBSAN: negation overflow +OBT_POSTFIX_OPERATORS traps: UBSAN: integer truncation --=20 2.34.1