Introduce Overflow Behavior Types
Subsystems (13)
Kees Cook
maintainercover Introduce Overflow Behavior Types
Hi,
This is the spiritual successor to the "Mitigating unexpected arithmetic overflow" thread from 2024[1]. After a long discussion there, and subsequent discussions with Peter at Linux Plumbers, Justin went off to build a type-based solution. After more than a year of RFCs and feedback from compiler folks and with an eye toward potentially making this part of the C Standard in the future, what has evolved is Overflow Behavior Types[2], which are first-class native types that mirror the existing native scalar types in C. They are created using a type qualifier ("__ob_trap" and "__ob_wrap"), but they are their own distinct types. e.g. "int" and "int __ob_trap" are different types, though they are mostly interchangable (e.g. format strings, implicit casts, etc), with some specific instrumentation in cases where wrap-around or truncation is possible.
This series provides support for building with them enabled, adds documentation, adds tests, and proposes the new typedefs (see the last patch in the series) for the corresponding kernel scalar types. With this, we can start converting variables (and types) that are never supposed to overflow/underflow to these new types[3]. (Or types that are always supposed to overflow/underflow.)
Enjoy! :)
-Kees
Justin Stitt (2):
hardening: Introduce Overflow Behavior Types support
compiler_attributes: Add overflow_behavior macros __ob_trap and
__ob_wrap
Kees Cook (3):
refcount: Remove unused __signed_wrap function annotations lkdtm/bugs: Add basic Overflow Behavior Types test
types: Add standard __ob_trap and __ob_wrap scalar types
lib/Kconfig.ubsan | 18 - security/Kconfig.hardening | 50 ++- Makefile | 1 + scripts/basic/Makefile | 2 +- scripts/Makefile.lib | 7 +- scripts/Makefile.obt | 28 ++ scripts/Makefile.ubsan | 10 - scripts/Makefile.warn | 7 + scripts/integer-wrap-ignore.scl | 3 +- Documentation/dev-tools/ubsan.rst | 13 + Documentation/process/arithmetic-overflow.rst | 323 ++++++++++++++++++ Documentation/process/deprecated.rst | 39 +++ Documentation/process/index.rst | 1 + include/linux/compiler-version.h | 2 +- include/linux/compiler_attributes.h | 12 + include/linux/compiler_types.h | 9 +- include/linux/refcount.h | 10 +- include/linux/sched.h | 3 +- include/linux/types.h | 24 ++ include/linux/ubsan.h | 12 +- drivers/misc/lkdtm/bugs.c | 253 ++++++++++++++ lib/ubsan.c | 17 +- MAINTAINERS | 10 + kernel/configs/hardening.config | 1 - tools/testing/selftests/lkdtm/tests.txt | 10 + 25 files changed, 807 insertions(+), 58 deletions(-) create mode 100644 scripts/Makefile.obt create mode 100644 Documentation/process/arithmetic-overflow.rst
Signature · 1 line
2.34.1
Kees Cook
maintainerPATCH 1/5 refcount: Remove unused __signed_wrap function annotations
With CONFIG_UBSAN_INTEGER_WRAP being replaced by Overflow Behavior Types, remove the __signed_wrap function annotation as it is already unused, and any future work here will use OBT annotations instead.
---
Cc: Will Deacon <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Boqun Feng <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Gary Guo <[email protected]>
Cc: Nathan Chancellor <[email protected]>
Cc: Marco Elver <[email protected]>
Cc: Miguel Ojeda <[email protected]>
Cc: Przemek Kitszel <[email protected]>
--- include/linux/compiler_types.h | 9 +-------- include/linux/refcount.h | 10 +++++----- 2 files changed, 6 insertions(+), 13 deletions(-)
include/linux/compiler_types.h +1 -8
@@ -432,18 +432,11 @@ struct ftrace_likely_data {#define at_least#endif -/* Do not trap wrapping arithmetic within an annotated function. */ -#ifdef CONFIG_UBSAN_INTEGER_WRAP -# define __signed_wrap __attribute__((no_sanitize("signed-integer-overflow"))) -#else -# define __signed_wrap -#endif -/* Section for code which can't be instrumented at all */ #define __noinstr_section (section) \ noinline notrace __attribute((__section__(section))) \ __no_kcsan __no_sanitize_address __no_profile __no_sanitize_coverage \ - __no_sanitize_memory __signed_wrap + __no_sanitize_memory#define noinstr __noinstr_section(".noinstr.text")
include/linux/refcount.h +5 -5
@@ -170,7 +170,7 @@ static inline unsigned int refcount_read(const refcount_t *r)return atomic_read (&r->refs); } -static inline __must_check __signed_wrap +static inline __must_check bool __refcount_add_not_zero (int i,refcount_t *r,int *oldp) {int old =refcount_read (r);
@@ -212,7 +212,7 @@ static inline __must_check bool refcount_add_not_zero(int i, refcount_t *r)return __refcount_add_not_zero (i, r,NULL ); } -static inline __must_check __signed_wrap +static inline __must_check bool __refcount_add_not_zero_limited_acquire (int i,refcount_t *r,int *oldp,int limit) {
@@ -244,7 +244,7 @@ __refcount_inc_not_zero_limited_acquire(refcount_t *r, int *oldp, int limit)return __refcount_add_not_zero_limited_acquire (1 , r, oldp, limit); } -static inline __must_check __signed_wrap +static inline __must_check bool __refcount_add_not_zero_acquire (int i,refcount_t *r,int *oldp) {return __refcount_add_not_zero_limited_acquire (i, r, oldp,INT_MAX );
@@ -277,7 +277,7 @@ static inline __must_check bool refcount_add_not_zero_acquire(int i, refcount_treturn __refcount_add_not_zero_acquire (i, r,NULL ); } -static inline __signed_wrap +static inline void __refcount_add (int i,refcount_t *r,int *oldp) {int old =atomic_fetch_add_relaxed (i, &r->refs);
@@ -383,7 +383,7 @@ static inline void refcount_inc(refcount_t *r)__refcount_inc (r,NULL ); } -static inline __must_check __signed_wrap +static inline __must_check bool __refcount_sub_and_test (int i,refcount_t *r,int *oldp) {int old =atomic_fetch_sub_release (i, &r->refs);
Signature · 1 line
2.34.1
Kees Cook
maintainerPATCH 2/5 hardening: Introduce Overflow Behavior Types support
In reply to Kees CookFrom: Justin Stitt <[email protected]>
Replace CONFIG_UBSAN_INTEGER_WRAP with CONFIG_OVERFLOW_BEHAVIOR_TYPES, which a more distinct implementation of the integer wrapping logic, with the introduction of the Overflow Behavior Types in Clang 23+. While this uses sanitizers, it isn't really part of the "undefined behavior" sanitizer collection (there is nothing "undefined" going on, but the warn/trap infrastructure is still used for common instrumentation).
As OBTs will be annotated in code, we no longer need type annotation in the sanitizer SCL file. A build with CONFIG_OVERFLOW_BEHAVIOR_TYPES=y will result in no new instrumentation being added (as there are no users of the coming __ob_trap and __ob_wrap annotations yet).
---
Cc: Nathan Chancellor <[email protected]>
Cc: Nicolas Schier <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>
--- lib/Kconfig.ubsan | 18 ------------ security/Kconfig.hardening | 50 +++++++++++++++++++++++++++++++- Makefile | 1 + scripts/basic/Makefile | 2 +- scripts/Makefile.lib | 7 +++-- scripts/Makefile.obt | 28 ++++++++++++++++++ scripts/Makefile.ubsan | 10 ------- scripts/Makefile.warn | 7 +++++ scripts/integer-wrap-ignore.scl | 3 +- include/linux/compiler-version.h | 2 +- include/linux/sched.h | 3 +- include/linux/ubsan.h | 12 +++++++- lib/ubsan.c | 17 ++++++----- MAINTAINERS | 9 ++++++ kernel/configs/hardening.config | 1 - 15 files changed, 125 insertions(+), 45 deletions(-) create mode 100644 scripts/Makefile.obt
lib/Kconfig.ubsan +0 -18
@@ -116,24 +116,6 @@ config UBSAN_UNREACHABLE This option enables -fsanitize=unreachable which checks for control flow reaching an expected-to-be-unreachable position. -config UBSAN_INTEGER_WRAP - bool "Perform checking for integer arithmetic wrap-around" - # This is very experimental so drop the next line if you really want it - depends on BROKEN - depends on !COMPILE_TEST - depends on $(cc-option,-fsanitize-undefined-ignore-overflow-pattern=all) - depends on $(cc-option,-fsanitize=signed-integer-overflow) - depends on $(cc-option,-fsanitize=unsigned-integer-overflow) - depends on $(cc-option,-fsanitize=implicit-signed-integer-truncation) - depends on $(cc-option,-fsanitize=implicit-unsigned-integer-truncation) - depends on $(cc-option,-fsanitize-ignorelist=/dev/null) - help - This option enables all of the sanitizers involved in integer overflow - (wrap-around) mitigation: signed-integer-overflow, unsigned-integer-overflow, - implicit-signed-integer-truncation, and implicit-unsigned-integer-truncation. - This is currently limited only to the size_t type while testing and - compiler development continues. - config UBSAN_BOOL bool "Perform checking for non-boolean values used as boolean" default UBSAN
security/Kconfig.hardening +49 -1
56 lines not on this page · raw message
Makefile +1 -0
@@ -1123,6 +1123,7 @@ include-$(CONFIG_KASAN) += scripts/Makefile.kasan include-$(CONFIG_KCSAN) += scripts/Makefile.kcsan include-$(CONFIG_KMSAN) += scripts/Makefile.kmsan include-$(CONFIG_UBSAN) += scripts/Makefile.ubsan +include-$(CONFIG_OVERFLOW_BEHAVIOR_TYPES) += scripts/Makefile.obt include-$(CONFIG_KCOV) += scripts/Makefile.kcov include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct include-$(CONFIG_KSTACK_ERASE) += scripts/Makefile.kstack_erase
scripts/basic/Makefile +1 -1
@@ -18,4 +18,4 @@ always-$(CONFIG_RANDSTRUCT) += randstruct.seed # integer-wrap: if the .scl file changes, we need to do a full rebuild. $(obj)/../../include/generated/integer-wrap.h: $(srctree)/scripts/integer-wrap-ignore.scl FORCE $(call if_changed,touch) -always-$(CONFIG_UBSAN_INTEGER_WRAP) += ../../include/generated/integer-wrap.h +always-$(CONFIG_OVERFLOW_BEHAVIOR_TYPES) += ../../include/generated/integer-wrap.h
scripts/Makefile.lib +5 -2
@@ -76,9 +76,12 @@ ifeq ($(CONFIG_UBSAN),y) _c_flags += $(if $(patsubst n%,, \ $(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_SANITIZE)$(is-kernel-object)), \ $(CFLAGS_UBSAN)) +endif + +ifeq ($(CONFIG_OVERFLOW_BEHAVIOR_TYPES),y) _c_flags += $(if $(patsubst n%,, \ - $(UBSAN_INTEGER_WRAP_$(target-stem).o)$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_INTEGER_WRAP)$(UBSAN_SANITIZE)$(is-kernel-object)), \ - $(CFLAGS_UBSAN_INTEGER_WRAP)) + $(OVERFLOW_BEHAVIOR_TYPES_$(target-stem).o)$(OVERFLOW_BEHAVIOR_TYPES)$(is-kernel-object)), \ + $(CFLAGS_OVERFLOW_BEHAVIOR_TYPES)) endif ifeq ($(CONFIG_KCOV),y)
scripts/Makefile.obt new file +28 -0
@@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Instead of specifying `all`, let's pick our patterns piecemeal so we aren't +# blindsided by compiler upgrades. +OVERFLOW_BEHAVIOR_TYPES_IGNORE_PATTERNS := negated-unsigned-const,$\ + unsigned-post-decr-while,$\ + add-signed-overflow-test,$\ + add-unsigned-overflow-test + +OVERFLOW_BEHAVIOR_TYPES_SANITIZERS := signed-integer-overflow,$\ + unsigned-integer-overflow,$\ + implicit-signed-integer-truncation,$\ + implicit-unsigned-integer-truncation,$\ + implicit-integer-sign-change + +overflow-behavior-types-cflags-$(CONFIG_OVERFLOW_BEHAVIOR_TYPES) += \ + -DOVERFLOW_BEHAVIOR_TYPES \ + -Xclang -fexperimental-overflow-behavior-types \ + -fsanitize=$(OVERFLOW_BEHAVIOR_TYPES_SANITIZERS) \ + -fsanitize-undefined-ignore-overflow-pattern=$(OVERFLOW_BEHAVIOR_TYPES_IGNORE_PATTERNS) \ + -fsanitize-ignorelist=$(srctree)/scripts/integer-wrap-ignore.scl \ + -Wno-incompatible-pointer-types-discards-overflow-behavior +overflow-behavior-types-cflags-$(CONFIG_OVERFLOW_BEHAVIOR_TYPES_WARN) += \ + -fno-sanitize-trap=$(OVERFLOW_BEHAVIOR_TYPES_SANITIZERS) +overflow-behavior-types-cflags-$(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP) += \ + -fsanitize-trap=$(OVERFLOW_BEHAVIOR_TYPES_SANITIZERS) + +export CFLAGS_OVERFLOW_BEHAVIOR_TYPES := $(overflow-behavior-types-cflags-y)
scripts/Makefile.ubsan +0 -10
@@ -16,13 +16,3 @@ ubsan-cflags-$(CONFIG_UBSAN_ENUM) += -fsanitize=enum ubsan-cflags-$(CONFIG_UBSAN_TRAP) += $(CFLAGS_UBSAN_TRAP) export CFLAGS_UBSAN := $(ubsan-cflags-y) - -ubsan-integer-wrap-cflags-$(CONFIG_UBSAN_INTEGER_WRAP) += \ - -DINTEGER_WRAP \ - -fsanitize-undefined-ignore-overflow-pattern=all \ - -fsanitize=signed-integer-overflow \ - -fsanitize=unsigned-integer-overflow \ - -fsanitize=implicit-signed-integer-truncation \ - -fsanitize=implicit-unsigned-integer-truncation \ - -fsanitize-ignorelist=$(srctree)/scripts/integer-wrap-ignore.scl -export CFLAGS_UBSAN_INTEGER_WRAP := $(ubsan-integer-wrap-cflags-y)
scripts/Makefile.warn +7 -0
@@ -49,6 +49,13 @@ KBUILD_CFLAGS += $(call cc-option, -Wno-format-truncation-non-kprintf) # because -Wuninitialized will still flag when an uninitialized const variable # is used. KBUILD_CFLAGS += $(call cc-option, -Wno-default-const-init-unsafe) + +# Clang with Overflow Behavior Types support but building a kernel without +# CONFIG_OVERFLOW_BEHAVIOR_TYPES needs to explicitly ignore the attribute. +ifndef CONFIG_OVERFLOW_BEHAVIOR_TYPES +KBUILD_CFLAGS += $(call cc-option, -Wno-overflow-behavior-attribute-ignored) +endif + else # gcc inanely warns about local variables called 'main'
scripts/integer-wrap-ignore.scl +1 -2
@@ -1,3 +1,2 @@ -[{unsigned-integer-overflow,signed-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation}] +[{unsigned-integer-overflow,signed-integer-overflow,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation,implicit-integer-sign-change}] type:* -type:size_t=sanitize
include/linux/compiler-version.h +1 -1
@@ -39,6 +39,6 @@* may have changed, which may impact the expected behaviors that should * not differ between compilation units. */ -#ifdef INTEGER_WRAP +#ifdef OVERFLOW_BEHAVIOR_TYPES #include <generated/integer-wrap.h> #endif
include/linux/sched.h +2 -1
@@ -48,6 +48,7 @@#include <linux/uidgid_types.h> #include <linux/tracepoint-defs.h> #include <linux/unwind_deferred_types.h> +#include <linux/ubsan.h> #include <asm/kmap_size.h> #include <linux/time64.h> #ifndef COMPILE_OFFSETS
@@ -1271,7 +1272,7 @@ struct task_struct {struct held_lock held_locks[MAX_LOCK_DEPTH ];#endif -#if defined(CONFIG_UBSAN ) && !defined(CONFIG_UBSAN_TRAP ) +#ifdef NEED_SANITIZER_WARN_HANDLER unsigned int in_ubsan;#endif
include/linux/ubsan.h +11 -1
@@ -2,7 +2,17 @@#ifndef _LINUX_UBSAN_H#define _LINUX_UBSAN_H -#if defined(CONFIG_UBSAN_TRAP ) || defined(CONFIG_UBSAN_KVM_EL2 ) +#if defined(CONFIG_UBSAN_TRAP ) || defined(CONFIG_UBSAN_KVM_EL2 ) || \ + defined(CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP ) +# define NEED_SANITIZER_TRAP_HANDLER +#endif + +#if (defined(CONFIG_UBSAN ) && !defined(CONFIG_UBSAN_TRAP )) || \ + defined(CONFIG_OVERFLOW_BEHAVIOR_TYPES_WARN ) +# define NEED_SANITIZER_WARN_HANDLER +#endif + +#ifdef NEED_SANITIZER_TRAP_HANDLER const char *report_ubsan_failure (u32 check_type);#else static inline const char *report_ubsan_failure (u32 check_type)
lib/ubsan.c +10 -7
@@ -19,7 +19,7 @@#include "ubsan.h" -#if defined(CONFIG_UBSAN_TRAP ) || defined(CONFIG_UBSAN_KVM_EL2 ) +#ifdef NEED_SANITIZER_TRAP_HANDLER /* * Only include matches for UBSAN checks that are actually compiled in. * The mappings of struct SanitizerKind (the -fsanitize=xxx args) to
@@ -44,7 +44,7 @@ const char *report_ubsan_failure(u32 check_type)case ubsan_shift_out_of_bounds:return "UBSAN: shift out of bounds" ;#endif -#if defined(CONFIG_UBSAN_DIV_ZERO ) || defined(CONFIG_UBSAN_INTEGER_WRAP ) +#if defined(CONFIG_UBSAN_DIV_ZERO ) || defined(CONFIG_OVERFLOW_BEHAVIOR_TYPES )/* * SanitizerKind::IntegerDivideByZero and * SanitizerKind::SignedIntegerOverflow emit
@@ -79,7 +79,7 @@ const char *report_ubsan_failure(u32 check_type)case ubsan_type_mismatch:return "UBSAN: type mismatch" ;#endif -#ifdef CONFIG_UBSAN_INTEGER_WRAP +#ifdef CONFIG_OVERFLOW_BEHAVIOR_TYPES /* * SanitizerKind::SignedIntegerOverflow emits * SanitizerHandler::AddOverflow, SanitizerHandler::SubOverflow,
@@ -91,15 +91,18 @@ const char *report_ubsan_failure(u32 check_type)return "UBSAN: integer subtraction overflow" ;case ubsan_mul_overflow:return "UBSAN: integer multiplication overflow" ; +case ubsan_implicit_conversion: +return "UBSAN: integer truncation" ; +case ubsan_negate_overflow: +return "UBSAN: negation overflow" ;#endif default : return "UBSAN: unrecognized failure code"; } } +#endif /* NEED_SANITIZER_TRAP_HANDLER */ -#endif - -#ifndef CONFIG_UBSAN_TRAP +#ifdef NEED_SANITIZER_WARN_HANDLER static const char *const type_check_kinds[] = {"load of" , "store to",
@@ -558,4 +561,4 @@ void __ubsan_handle_alignment_assumption(void *_data, unsigned long ptr, }EXPORT_SYMBOL (__ubsan_handle_alignment_assumption); -#endif /* !CONFIG_UBSAN_TRAP */ +#endif /* NEED_SANITIZER_WARN_HANDLER */
MAINTAINERS +9 -0
@@ -19919,6 +19919,15 @@ F: Documentation/devicetree/bindings/media/i2c/ovti,ov2659.txt F: drivers/media/i2c/ov2659.c F: include/media/i2c/ov2659.h +OVERFLOW BEHAVIOR TYPES +M: Kees Cook <[email protected]> +M: Justin Stitt <[email protected]> +L: [email protected] +S: Supported +T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening +F: scripts/integer-wrap-ignore.scl +F: scripts/Makefile.obt + OVERLAY FILESYSTEM M: Miklos Szeredi <[email protected]> M: Amir Goldstein <[email protected]>
kernel/configs/hardening.config +0 -1
@@ -46,7 +46,6 @@ CONFIG_UBSAN_BOUNDS=y # CONFIG_UBSAN_SHIFT is not set # CONFIG_UBSAN_DIV_ZERO is not set # CONFIG_UBSAN_UNREACHABLE is not set -# CONFIG_UBSAN_INTEGER_WRAP is not set # CONFIG_UBSAN_BOOL is not set # CONFIG_UBSAN_ENUM is not set # CONFIG_UBSAN_ALIGNMENT is not set
Signature · 1 line
2.34.1
Kees Cook
maintainerPATCH 3/5 compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
In reply to Kees CookFrom: Justin Stitt <[email protected]>
When CONFIG_OVERFLOW_BEHAVIOR_TYPES=y, Clang 23+'s Overflow Behavior Type[1] annotations are available (i.e. __ob_trap, __ob_wrap). When not enabled, these need to be empty macros. Document the new annotation and add links from sanitizer docs pointing to the arithmetic-overflow docs.
---
Cc: Marco Elver <[email protected]>
Cc: Andrey Konovalov <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: Jonathan Corbet <[email protected]>
Cc: Shuah Khan <[email protected]>
Cc: Miguel Ojeda <[email protected]>
Cc: Nathan Chancellor <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>
--- Documentation/dev-tools/ubsan.rst | 13 + Documentation/process/arithmetic-overflow.rst | 323 ++++++++++++++++++ Documentation/process/deprecated.rst | 39 +++ Documentation/process/index.rst | 1 + include/linux/compiler_attributes.h | 12 + MAINTAINERS | 1 + 6 files changed, 389 insertions(+) create mode 100644 Documentation/process/arithmetic-overflow.rst
Documentation/dev-tools/ubsan.rst +13 -0
@@ -71,6 +71,19 @@ unaligned accesses (CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y). One could still enable it in config, just note that it will produce a lot of UBSAN reports. +Additional sanitizer options include:: + + CONFIG_OVERFLOW_BEHAVIOR_TYPES=y + +This enables checking for integer arithmetic wrap-around (overflow/underflow). +It instruments signed and unsigned integer overflow, as well as implicit +truncation operations. This option is currently limited to specific types +via the ``__ob_trap`` and ``__ob_wrap`` annotations. + +For detailed information about arithmetic overflow handling, overflow behavior +annotations, and best practices, see: +Documentation/process/arithmetic-overflow.rst + References ----------
Documentation/process/arithmetic-overflow.rst new file +323 -0
323 lines not on this page · raw message
Documentation/process/deprecated.rst +39 -0
@@ -109,6 +109,45 @@ For more details, also see array3_size() and flex_array_size(), as well as the related check_mul_overflow(), check_add_overflow(), check_sub_overflow(), and check_shl_overflow() family of functions. +.. _open_coded_wrap_around: + +open-coded intentional arithmetic wrap-around +--------------------------------------------- +Depending on arithmetic wrap-around without annotations means the +kernel cannot distinguish between intentional wrap-around and accidental +wrap-around (when using things like the overflow sanitizers). + +For example, where an addition is intended to wrap around:: + + magic = counter + rotation; + +please use the wrapping_add() helper:: + + magic = wrapping_add(int, counter, rotation); + +To help minimize needless code churn, the kernel uses overflow idiom exclusions +(currently only supported by Clang). Some commonly used overflow-dependent code +patterns will not be instrumented by overflow sanitizers. The currently +supported patterns are:: + + /* wrap-around test */ + if (var + offset < var) ... + + /* standalone unsigned decrement used in while loop */ + while(size--) + + /* negated unsigned constants */ + -1UL; + -5U; + +In rare cases where helpers aren't available (e.g. in early boot code, etc) but +overflow instrumentation still needs to be avoided, utilize wrap-around tests +to check wrap-around outcomes before performing calculations:: + + if (var + offset < var) { + /* wrap-around has occurred */ + } + simple_strtol(), simple_strtoll(), simple_strtoul(), simple_strtoull() ---------------------------------------------------------------------- The simple_strtol(), simple_strtoll(),
Documentation/process/index.rst +1 -0
@@ -111,3 +111,4 @@ developers: kernel-docs deprecated + arithmetic-overflow
include/linux/compiler_attributes.h +12 -0
@@ -396,6 +396,18 @@# define __disable_sanitizer_instrumentation#endif +/* +* Optional: only supported by Clang with -Xclang -experimental-foverflow-behavior-types +* passed via CONFIG_OVERFLOW_BEHAVIOR_TYPES. When not available, define empty macros for +* the trap/wrap annotations. +* +* clang: https://clang.llvm.org/docs/OverflowBehaviorTypes.html +*/ +#if !__has_attribute (overflow_behavior) || !defined(OVERFLOW_BEHAVIOR_TYPES ) +# define __ob_trap +# define __ob_wrap +#endif +/* * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-weak-function-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-weak-variable-attribute
MAINTAINERS +1 -0
@@ -19925,6 +19925,7 @@ M: Justin Stitt <[email protected]> L: [email protected] S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening +F: Documentation/process/arithmetic-overflow.rst F: scripts/integer-wrap-ignore.scl F: scripts/Makefile.obt
Signature · 1 line
2.34.1
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
include/linux/compiler_attributes.h line 409
Quoted 12 lines · Kees Cook
include/linux/compiler_attributes.h +11
-0+/* +* Optional: only supported by Clang with -Xclang -experimental-foverflow-behavior-types +* passed via CONFIG_OVERFLOW_BEHAVIOR_TYPES. When not available, define empty macros for +* the trap/wrap annotations. +* +* clang: https://clang.llvm.org/docs/OverflowBehaviorTypes.html +*/ +#if !__has_attribute (overflow_behavior) || !defined(OVERFLOW_BEHAVIOR_TYPES ) +# define __ob_trap +# define __ob_wrap +#endif
Should that have CONFIG_*? i.e.
!defined(CONFIG_OVERFLOW_BEHAVIOR_TYPES)
In addition, since this depends on a CONFIG_, with the current setup we would put them elsewhere instead of compiler_attributes.h until they are promoted to be "unconditional" (i.e. without the compiler flag):
* Any other "attributes" (i.e. those that depend on a configuration option,
* on a compiler, on an architecture, on plugins, on other attributes...)
* should be defined elsewhere (e.g. compiler_types.h or compiler-*.h).
* The intention is to keep this file as simple as possible, as well as
* compiler- and version-agnostic (e.g. avoiding GCC_VERSION checks).
However, thinking about it, why is the config needed?
i.e. if the compiler is not passed that flag, shouldn't the __has_attribute simply return false?
Also, I am a bit confused -- does the compiler flag automatically recognize the names like __ob_trap? i.e. I see the docs mention using the attribute,
typedef unsigned int __attribute__((overflow_behavior(trap))) safe_uint;
typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint;But then we don't actually use it?
Or should this just be like the rest of the attributes, i.e. we actually define them here?
Thanks!
Cheers,
Miguel
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
Quoted 9 lines · Miguel Ojeda
Also, I am a bit confused -- does the compiler flag automatically recognize the names like
__ob_trap? i.e. I see the docs mention using the attribute,typedef unsigned int __attribute__((overflow_behavior(trap))) safe_uint; typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint;But then we don't actually use it?
Ah, it does, it is a keyword, and I should have read the docs better.
From a quick test in Compiler Explorer it seems to be fine to define a macro like the keyword:
#define __ob_trap __attribute__((overflow_behavior(trap)))
#define __ob_wrap __attribute__((overflow_behavior(wrap)))That could be a bit more flexible in the name we pick on our side and more like the rest of the attributes, but if the intention is to eventually use that keyword in standard C or similar, I guess it is fine.
Cheers,
Miguel
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
In reply to Miguel OjedaHi,
Quoted 35 lines · Miguel Ojeda
include/linux/compiler_attributes.h line 409
Quoted 12 lines · Kees Cook
include/linux/compiler_attributes.h +11
-0+/* +* Optional: only supported by Clang with -Xclang -experimental-foverflow-behavior-types +* passed via CONFIG_OVERFLOW_BEHAVIOR_TYPES. When not available, define empty macros for +* the trap/wrap annotations. +* +* clang: https://clang.llvm.org/docs/OverflowBehaviorTypes.html +*/ +#if !__has_attribute (overflow_behavior) || !defined(OVERFLOW_BEHAVIOR_TYPES ) +# define __ob_trap +# define __ob_wrap +#endif Should that have
CONFIG_*? i.e.!defined(CONFIG_OVERFLOW_BEHAVIOR_TYPES)
In addition, since this depends on a
CONFIG_, with the current setup we would put them elsewhere instead ofcompiler_attributes.huntil they are promoted to be "unconditional" (i.e. without the compiler flag):* Any other "attributes" (i.e. those that depend on a configuration option,
* on a compiler, on an architecture, on plugins, on other attributes...)
* should be defined elsewhere (e.g.compiler_types.hor compiler-*.h).
* The intention is to keep this file as simple as possible, as well as
* compiler- and version-agnostic (e.g. avoidingGCC_VERSIONchecks).However, thinking about it, why is the config needed?
i.e. if the compiler is not passed that flag, shouldn't the
__has_attributesimply return false?
Quoted 8 lines
Also, I am a bit confused -- does the compiler flag automatically recognize the names like
__ob_trap? i.e. I see the docs mention using the attribute,typedef unsigned int __attribute__((overflow_behavior(trap))) safe_uint; typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint;But then we don't actually use it?
__ob_trap and __ob_wrap are defined by the compiler.
There are some examples within the documentation additions of this patch.
Kees, is it possible to make it more clear about what we expect of kernel developers in terms of style? Should they use keyword spellings? attribute spellings? only use custom types?
Quoted 8 lines
Or should this just be like the rest of the attributes, i.e. we actually define them here?
Thanks!
Cheers,
Miguel
Justin
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
Quoted 8 lines · Justin Stitt
__ob_trapand__ob_wrapare defined by the compiler.There are some examples within the documentation additions of this patch.
Kees, is it possible to make it more clear about what we expect of kernel developers in terms of style? Should they use keyword spellings? attribute spellings? only use custom types?
Yeah, I noticed that right after sending the email, sorry.
So I tried to use a macro even if happens to have the same name as the keyword, since that form is a bit more flexible, but it is fine either way.
What I would suggest is adding to the comment that these were decided to be used as keywords, and thus we only need to define them as empty in the disabled case.
Thanks!
Cheers,
Miguel
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
Hi,
Quoted 13 lines · Miguel Ojeda
Quoted 8 lines · Justin Stitt
__ob_trapand__ob_wrapare defined by the compiler.There are some examples within the documentation additions of this patch.
Kees, is it possible to make it more clear about what we expect of kernel developers in terms of style? Should they use keyword spellings? attribute spellings? only use custom types?
Yeah, I noticed that right after sending the email, sorry.
Quoted 7 lines
So I tried to use a macro even if happens to have the same name as the keyword, since that form is a bit more flexible, but it is fine either way.
What I would suggest is adding to the comment that these were decided to be used as keywords, and thus we only need to define them as empty in the disabled case.
Agreed.
Quoted 5 lines
Thanks!
Cheers,
Miguel
Justin
Kees Cook
maintainerRe: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
In reply to Justin StittQuoted 48 lines · Justin Stitt
Hi,
Quoted 35 lines · Miguel Ojeda
include/linux/compiler_attributes.h line 409
Quoted 12 lines · Kees Cook
include/linux/compiler_attributes.h +11
-0+/* +* Optional: only supported by Clang with -Xclang -experimental-foverflow-behavior-types +* passed via CONFIG_OVERFLOW_BEHAVIOR_TYPES. When not available, define empty macros for +* the trap/wrap annotations. +* +* clang: https://clang.llvm.org/docs/OverflowBehaviorTypes.html +*/ +#if !__has_attribute (overflow_behavior) || !defined(OVERFLOW_BEHAVIOR_TYPES ) +# define __ob_trap +# define __ob_wrap +#endif Should that have
CONFIG_*? i.e.!defined(CONFIG_OVERFLOW_BEHAVIOR_TYPES)
In addition, since this depends on a
CONFIG_, with the current setup we would put them elsewhere instead ofcompiler_attributes.huntil they are promoted to be "unconditional" (i.e. without the compiler flag):* Any other "attributes" (i.e. those that depend on a configuration option,
* on a compiler, on an architecture, on plugins, on other attributes...)
* should be defined elsewhere (e.g.compiler_types.hor compiler-*.h).
* The intention is to keep this file as simple as possible, as well as
* compiler- and version-agnostic (e.g. avoidingGCC_VERSIONchecks).However, thinking about it, why is the config needed?
i.e. if the compiler is not passed that flag, shouldn't the
__has_attributesimply return false?Quoted 8 lines
Also, I am a bit confused -- does the compiler flag automatically recognize the names like
__ob_trap? i.e. I see the docs mention using the attribute,typedef unsigned int __attribute__((overflow_behavior(trap))) safe_uint; typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint;But then we don't actually use it?
Quoted 7 lines
__ob_trapand__ob_wrapare defined by the compiler.There are some examples within the documentation additions of this patch.
Kees, is it possible to make it more clear about what we expect of kernel developers in terms of style? Should they use keyword spellings? attribute spellings? only use custom types?
I think for this series, __ob_trap/__ob_wrap is what should be used.
And for other folks, the background here is that we originally wanted to use macros for "__trap" and "__wrap", but the powerpc C compiler (both Clang and GCC) have a builtin macro named "__trap" already. So I switched to just using the Clang-native type qualifier. We can use the attribute style too, but there was a lot of confusion during the Clang development phases where people kept forgetting this was a type qualifier, not an attribute (i.e. the attribute is an internal alias for the qualifier, and the qualifier is a new type).
Signature · 1 line
Kees Cook
Peter Zijlstra
maintainerRe: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
Quoted 10 lines · Kees Cook
I think for this series, __ob_trap/__ob_wrap is what should be used.
And for other folks, the background here is that we originally wanted to use macros for "
__trap" and "__wrap", but the powerpc C compiler (both Clang and GCC) have a builtin macro named "__trap" already. So I switched to just using the Clang-native type qualifier. We can use the attribute style too, but there was a lot of confusion during the Clang development phases where people kept forgetting this was a type qualifier, not an attribute (i.e. the attribute is an internal alias for the qualifier, and the qualifier is a new type).
Since you mention qualifiers...
What is the result of __typeof_unqual__(int __ob_trap) ?
Kees Cook
maintainerRe: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
Quoted 13 lines · Peter Zijlstra
Quoted 10 lines · Kees Cook
I think for this series, __ob_trap/__ob_wrap is what should be used.
And for other folks, the background here is that we originally wanted to use macros for "
__trap" and "__wrap", but the powerpc C compiler (both Clang and GCC) have a builtin macro named "__trap" already. So I switched to just using the Clang-native type qualifier. We can use the attribute style too, but there was a lot of confusion during the Clang development phases where people kept forgetting this was a type qualifier, not an attribute (i.e. the attribute is an internal alias for the qualifier, and the qualifier is a new type).
Quoted 3 lines
Since you mention qualifiers...
What is the result of
__typeof_unqual__(int __ob_trap)?
Hmm, it seems like "const" doesn't get peeled off. That can be fixed, if that's needed?
'typeof_unqual(int)' (aka 'int') 'typeof_unqual(__ob_trap int)' (aka '__ob_trap int') 'typeof_unqual(const int)' (aka 'int') 'typeof_unqual(__ob_trap const int)' (aka '__ob_trap const int')
-Kees
Signature · 1 line
Kees Cook
Peter Zijlstra
maintainerRe: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
Quoted 18 lines · Kees Cook
Quoted 13 lines · Peter Zijlstra
Quoted 10 lines · Kees Cook
I think for this series, __ob_trap/__ob_wrap is what should be used.
And for other folks, the background here is that we originally wanted to use macros for "
__trap" and "__wrap", but the powerpc C compiler (both Clang and GCC) have a builtin macro named "__trap" already. So I switched to just using the Clang-native type qualifier. We can use the attribute style too, but there was a lot of confusion during the Clang development phases where people kept forgetting this was a type qualifier, not an attribute (i.e. the attribute is an internal alias for the qualifier, and the qualifier is a new type).Quoted 3 lines
Since you mention qualifiers...
What is the result of
__typeof_unqual__(int __ob_trap)?
Quoted 7 lines
Hmm, it seems like "const" doesn't get peeled off. That can be fixed, if that's needed?
'
typeof_unqual(int)' (aka 'int') 'typeof_unqual(__ob_trap int)' (aka '__ob_trapint') 'typeof_unqual(const int)' (aka 'int') 'typeof_unqual(__ob_trap const int)' (aka '__ob_trapconst int')
So how can something be called a qualifier if unqual doesn't strip it?
(We might already have had this discussion, but I can't find the answer in the LLVM documentation page and didn't search our previous correspondence on this).
Kees Cook
maintainerRe: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
Quoted 27 lines · Peter Zijlstra
Quoted 18 lines · Kees Cook
Quoted 13 lines · Peter Zijlstra
Quoted 10 lines · Kees Cook
I think for this series, __ob_trap/__ob_wrap is what should be used.
And for other folks, the background here is that we originally wanted to use macros for "
__trap" and "__wrap", but the powerpc C compiler (both Clang and GCC) have a builtin macro named "__trap" already. So I switched to just using the Clang-native type qualifier. We can use the attribute style too, but there was a lot of confusion during the Clang development phases where people kept forgetting this was a type qualifier, not an attribute (i.e. the attribute is an internal alias for the qualifier, and the qualifier is a new type).Quoted 3 lines
Since you mention qualifiers...
What is the result of
__typeof_unqual__(int __ob_trap)?Quoted 7 lines
Hmm, it seems like "const" doesn't get peeled off. That can be fixed, if that's needed?
'
typeof_unqual(int)' (aka 'int') 'typeof_unqual(__ob_trap int)' (aka '__ob_trapint') 'typeof_unqual(const int)' (aka 'int') 'typeof_unqual(__ob_trap const int)' (aka '__ob_trapconst int')
Quoted 5 lines
So how can something be called a qualifier if unqual doesn't strip it?
(We might already have had this discussion, but I can't find the answer in the LLVM documentation page and didn't search our previous correspondence on this).
I'll let Justin answer this correctly, but I suspect I'm using the wrong name for things. I think I should have said "keyword"? The mechanism produces a distinct type, so it's not actually a "qualifier", but the intent is that they are distinct types (this came from a long long discussion with the LLVM devs, etc), but that they have implicit casts to the non-obt scalars, but Justin knows more on this.
Signature · 1 line
Kees Cook
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
In reply to Peter ZijlstraHi,
Quoted 23 lines · Peter Zijlstra
Quoted 21 lines · Kees Cook
Quoted 13 lines · Peter Zijlstra
Quoted 10 lines · Kees Cook
I think for this series, __ob_trap/__ob_wrap is what should be used.
And for other folks, the background here is that we originally wanted to use macros for "
__trap" and "__wrap", but the powerpc C compiler (both Clang and GCC) have a builtin macro named "__trap" already. So I switched to just using the Clang-native type qualifier. We can use the attribute style too, but there was a lot of confusion during the Clang development phases where people kept forgetting this was a type qualifier, not an attribute (i.e. the attribute is an internal alias for the qualifier, and the qualifier is a new type).Quoted 3 lines
Since you mention qualifiers...
What is the result of
__typeof_unqual__(int __ob_trap)?Hmm, it seems like "const" doesn't get peeled off. That can be fixed, if that's needed?
Quoted 7 lines
Quoted 4 lines
'
typeof_unqual(int)' (aka 'int') 'typeof_unqual(__ob_trap int)' (aka '__ob_trapint') 'typeof_unqual(const int)' (aka 'int') 'typeof_unqual(__ob_trap const int)' (aka '__ob_trapconst int')So how can something be called a qualifier if unqual doesn't strip it?
Within Clang internals we call it a "type specifier" keyword with the closest analogous thing being _BitInt. Even the attribute spelling of OBTs boils down to a type specifier being applied to a base type.
This hasn't been clearly externalized in the documentation -- we can work to improve that.
Quoted 4 lines
(We might already have had this discussion, but I can't find the answer in the LLVM documentation page and didn't search our previous correspondence on this).
Justin
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
In reply to Kees CookQuoted 21 lines · On Wed, 1 Apr 2026 13:21:17 -0700 Kees Cook <[email protected]> wrote:
Quoted 13 lines · Peter Zijlstra
Quoted 10 lines · Kees Cook
I think for this series, __ob_trap/__ob_wrap is what should be used.
And for other folks, the background here is that we originally wanted to use macros for "
__trap" and "__wrap", but the powerpc C compiler (both Clang and GCC) have a builtin macro named "__trap" already. So I switched to just using the Clang-native type qualifier. We can use the attribute style too, but there was a lot of confusion during the Clang development phases where people kept forgetting this was a type qualifier, not an attribute (i.e. the attribute is an internal alias for the qualifier, and the qualifier is a new type).Quoted 3 lines
Since you mention qualifiers...
What is the result of
__typeof_unqual__(int __ob_trap)?Hmm, it seems like "const" doesn't get peeled off. That can be fixed, if that's needed?
Quoted 7 lines
'
typeof_unqual(int)' (aka 'int') 'typeof_unqual(__ob_trap int)' (aka '__ob_trapint') 'typeof_unqual(const int)' (aka 'int') 'typeof_unqual(__ob_trap const int)' (aka '__ob_trapconst int')-Kees
Adding all the required cases to the _Generic() doesn't scale.
typeof_unqual() needs to die.
Just using 'auto a = b;' should remove const and volatile - but gcc is buggy. There are some alternatives that work in many cases.
(It has all been discussed before.)
In most cases you can use 'auto a = (b) + 0'.
That does do integer promotions - but they happen as soon as 'a' is used; so it pretty much doesn't change the type of value, just the type of the variable.
David
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
In reply to Kees CookDocumentation/process/arithmetic-overflow.rst line 108
Quoted 3 lines · Kees Cook
Quoted 1 line
+
Documentation/process/arithmetic-overflow.rst new file +2
-0+ typedef unsigned int __attribute__((overflow_behavior(trap))) safe_uint; + typedef unsigned int __attribute__((overflow_behavior(wrap))) wrapping_uint;
This is fundamentally broken sh*t.
Stop thinking that trapping is "safe".
It damn well isn't. A dead machine is not a safe machine.
Any patches that call trapping behavior safe will ne NAK'ed by me.,
We have decades of peoiple using BUG_ON() as a safety measure, and it has been a HUGE PROBLEM.
There is no way in hell that we are ever adding implicit BUG_ON() things that are this hidden, this easy to use, and then mislabeled as being "safe".
Guys, that's the same logic as having a airbag in your car that just shoots you in the head. You're certainly "safe" from the vagaries of bad healthcare. But dammit, if anybody thinks that a "bullet to the head" should be called "safe", then that person damn well shouldn't be involved with kernel development.
So NAK NAK NAK NAK.
The only safe trapping behavior is something that has a clear an unambiguous and simple to use way to *HANDLE* it. Not just "mark it trapping".
Linus
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
Quoted 4 lines · Linus Torvalds
The only safe trapping behavior is something that has a clear an unambiguous and simple to use way to *HANDLE* it. Not just "mark it trapping".
Side note: this is the same kind of complete and utter idiocy that made Rust people have allocators that abort when running out of memory, because it's "safer" than returning NULL.
THAT KIND OF THINKING IS NOT ACCEPTABLE IN THE KERNEL.
I don't know why people keep doing this. Stop it.
Linus
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
In reply to Kees CookHi Kees,
Many thanks for this series. Great work and I am ready it with a lot of interest!
Le 31/03/2026 à 18:37, Kees Cook a écrit :
Quoted 70 lines
From: Justin Stitt <[email protected]>
When
CONFIG_OVERFLOW_BEHAVIOR_TYPES=y, Clang 23+'s Overflow Behavior Type[1] annotations are available (i.e.__ob_trap,__ob_wrap). When not enabled, these need to be empty macros. Document the new annotation and add links from sanitizer docs pointing to the arithmetic-overflow docs.Signed-off-by Justin Stitt <[email protected]>Co-developed-by Kees Cook <[email protected]>Signed-off-by Kees Cook <[email protected]>---
Cc: Marco Elver <[email protected]>
Cc: Andrey Konovalov <[email protected]>
Cc: Andrey Ryabinin <[email protected]>
Cc: Jonathan Corbet <[email protected]>
Cc: Shuah Khan <[email protected]>
Cc: Miguel Ojeda <[email protected]>
Cc: Nathan Chancellor <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>--- Documentation/dev-tools/ubsan.rst | 13 + Documentation/process/arithmetic-overflow.rst | 323 ++++++++++++++++++ Documentation/process/deprecated.rst | 39 +++ Documentation/process/index.rst | 1 + include/linux/compiler_attributes.h | 12 + MAINTAINERS | 1 + 6 files changed, 389 insertions(+) create mode 100644 Documentation/process/arithmetic-overflow.rstDocumentation/dev-tools/ubsan.rst +13
-0@@ -71,6 +71,19 @@ unaligned accesses (CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y). One could still enable it in config, just note that it will produce a lot of UBSAN reports. +Additional sanitizer options include:: + + CONFIG_OVERFLOW_BEHAVIOR_TYPES=y + +This enables checking for integer arithmetic wrap-around (overflow/underflow). +It instruments signed and unsigned integer overflow, as well as implicit +truncation operations. This option is currently limited to specific types +via the ``__ob_trap`` and ``__ob_wrap`` annotations. + +For detailed information about arithmetic overflow handling, overflow behavior +annotations, and best practices, see: +Documentation/process/arithmetic-overflow.rst + References ----------Documentation/process/arithmetic-overflow.rst new file +8
-0@@ -0,0 +1,323 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _arithmetic_overflow: + +Arithmetic Overflow Resolutions for Linux +========================================= + +Background
Documentation/process/arithmetic-overflow.rst line 18
Quoted 10 lines
Documentation/process/arithmetic-overflow.rst new file +10
-0+---------- + +When a calculation's result exceeds the involved storage ranges, several +strategies can be followed to handle such an overflow (or underflow), +including: + + - Undefined (i.e. pretend it's impossible and the result depends on hardware) + - Wrap around (this is what 2s-complement representation does by default) + - Trap (create an exception so the problem can be handled in another way) + - Saturate (explicitly hold the maximum or minimum representable value)
I just wanted to ask how much consideration was put into this last "saturate" option.
When speaking of "safe" as in "functional safety" this seems a good option to me. The best option is of course proper handling, but as discussed, we are speaking of the scenario in which the code is already buggy and which is the fallout option doing the least damage.
What I have in mind is a new __ob_saturate type qualifier. Something like:
void foo(int num)
{
int __ob_saturate saturate_var = num;
saturate_var += 42;
}would just print a warning and continue execution, thus solving the trapping issue. The above code would generate something equivalent to that:
void foo(int num)
{
int __ob_saturate saturate_var = num;
if (check_add_overflow(saturate_var, increment,
&saturate_var) {
WARN(true, "saturation occurred");
saturate_var = type_max(saturate_var);
}People using those saturating integers could then later check that the value is still in bound.
This is basically what your size_add() from overflow.h is already doing. If an overflow occurred, the allocation the addition does not trap, it just saturates and let the allocation functions properly handle the issue.
The saturation can neutralize many security attacks and can mitigate some safety issues. Think of the Ariane 5 rocket launch: a saturation could have prevented the unintended fireworks.
The caveat I can think of is that the old overflow check pattern becomes invalid. Doing:
if (saturate_var + increment < increment)
is now bogus and would need to be caught if possible by static analysis. So those saturating integers will only be usable in newly written code and could not be easily retrofitted.
Documentation/process/arithmetic-overflow.rst line 25
Quoted 6 lines
Documentation/process/arithmetic-overflow.rst new file +6
-0+In the C standard, three basic types can be involved in arithmetic, and each +has a default strategy for solving the overflow problem: + + - Signed overflow is undefined + - Unsigned overflow explicitly wraps around + - Pointer overflow is undefined
Nitpick: the C standard uses different definitions than yours. In the standard:
- overflow is *always* undefined
- unsigned integer wraparound
- signed integer overflow
The nuance is that in the standard unsigned integers do not overflow, they just wraparound.
I am not asking you to change your terminology, but it could be good to state in your document that your definition of overflow differs from the standard's definition. Maybe a terminology section could help.
Yours sincerely,
Vincent Mailhol
Peter Zijlstra
maintainerRe: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
Quoted 24 lines · Vincent Mailhol
Le 31/03/2026 à 18:37, Kees Cook a écrit :
Quoted 1 line
+ - Saturate (explicitly hold the maximum or minimum representable value)
I just wanted to ask how much consideration was put into this last "saturate" option.
When speaking of "safe" as in "functional safety" this seems a good option to me. The best option is of course proper handling, but as discussed, we are speaking of the scenario in which the code is already buggy and which is the fallout option doing the least damage.
What I have in mind is a new
__ob_saturatetype qualifier. Something like:void foo(int num) { int __ob_saturate saturate_var = num; saturate_var += 42; }would just print a warning and continue execution, thus solving the trapping issue. The above code would generate something equivalent to that:
Quoted 9 lines
void foo(int num)
{
int __ob_saturate saturate_var = num;
if (check_add_overflow(saturate_var, increment,
&saturate_var) {
WARN(true, "saturation occurred");
saturate_var = type_max(saturate_var);
}So I would like to second this option as being interesting.
But while pondering it, I did want to note that all of the options, with the exception of __ob_wrap (which is effectively what we have today for *everything*), will be 'interesting' to compose with _Atomic, another one of these qualifiers.
Now, in the kernel we don't use _Atomic, so strictly speaking I don't care ;-) But here goes...
Something like _Atomic int __ob_wrap, is trivial and good.
_Atomic int __ob_trap is either doable or impossible depending on how you define the result to be on 'trap'. Specifically, the semantics proposed where it keeps the old value makes it impossible.
And _Atomic int __ob_saturate is equally 'challenging', since the fundamental thing of 'reset to min/max on under/over-flow' is rather a non-atomic kind of thing. Look at the trouble we went through with refcount_t to sort of make this work.
Kees Cook
maintainerRe: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
Quoted 47 lines · Peter Zijlstra
Quoted 24 lines · Vincent Mailhol
Le 31/03/2026 à 18:37, Kees Cook a écrit :
Quoted 1 line
+ - Saturate (explicitly hold the maximum or minimum representable value)
I just wanted to ask how much consideration was put into this last "saturate" option.
When speaking of "safe" as in "functional safety" this seems a good option to me. The best option is of course proper handling, but as discussed, we are speaking of the scenario in which the code is already buggy and which is the fallout option doing the least damage.
What I have in mind is a new
__ob_saturatetype qualifier. Something like:void foo(int num) { int __ob_saturate saturate_var = num; saturate_var += 42; }would just print a warning and continue execution, thus solving the trapping issue. The above code would generate something equivalent to that:
Quoted 9 lines
void foo(int num) { int __ob_saturate saturate_var = num; if (check_add_overflow(saturate_var, increment, &saturate_var) { WARN(true, "saturation occurred"); saturate_var = type_max(saturate_var); }So I would like to second this option as being interesting.
But while pondering it, I did want to note that all of the options, with the exception of
__ob_wrap(which is effectively what we have today for *everything*), will be 'interesting' to compose with_Atomic, another one of these qualifiers.Now, in the kernel we don't use
_Atomic, so strictly speaking I don't care ;-) But here goes...Something like
_Atomicint__ob_wrap, is trivial and good.
Quoted 8 lines
_Atomicint__ob_trapis either doable or impossible depending on how you define the result to be on 'trap'. Specifically, the semantics proposed where it keeps the old value makes it impossible.And
_Atomicint__ob_saturateis equally 'challenging', since the fundamental thing of 'reset to min/max on under/over-flow' is rather a non-atomic kind of thing. Look at the trouble we went through withrefcount_tto sort of make this work.
Yeah, this is mainly why we didn't spend time working on an __ob_saturate implementation: the primary place we want it in Linux is already solved with all the refcount_t work. That said, if the behavior could be replicated using a future __ob_saturate, that would be very nice. :)
Signature · 1 line
Kees Cook
Re: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
In reply to Peter ZijlstraHi,
On Wed, Apr 01, 2026 at 11:20:27AM +0200, Peter Zijlstra wrote:
...
Quoted 13 lines
So I would like to second this option as being interesting.
But while pondering it, I did want to note that all of the options, with the exception of
__ob_wrap(which is effectively what we have today for *everything*), will be 'interesting' to compose with_Atomic, another one of these qualifiers.Now, in the kernel we don't use
_Atomic, so strictly speaking I don't care ;-) But here goes...Something like
_Atomicint__ob_wrap, is trivial and good.
Quoted 9 lines
_Atomicint__ob_trapis either doable or impossible depending on how you define the result to be on 'trap'. Specifically, the semantics proposed where it keeps the old value makes it impossible.And
_Atomicint__ob_saturateis equally 'challenging', since the fundamental thing of 'reset to min/max on under/over-flow' is rather a non-atomic kind of thing. Look at the trouble we went through withrefcount_tto sort of make this work.
FWIW, I've moved to simply disallow OBT+Atomic. We've got this under -fexperimental so I'm well within my right :). The idea being: __ob_trap isn't workable with Atomic (depending on storage semantics) and __ob_wrap isn't very useful there either, plus I had some parsing bugs where specific ordering of things could result in OBT being silently dropped with _Atomic and those magically go away by disallowing this pairing all together.
I just opened the PR over at [1] and it is still pending but this is probably where I'll go with _Atomic and OBT :)
Thanks
Justin
Kees Cook
maintainerRe: [PATCH 3/5] compiler_attributes: Add overflow_behavior macros __ob_trap and __ob_wrap
In reply to Vincent MailholQuoted 2 lines · Vincent Mailhol
Many thanks for this series. Great work and I am ready it with a lot of interest!
Yay! Glad to have folks looking at it all.
Quoted 7 lines
I just wanted to ask how much consideration was put into this last "saturate" option.
When speaking of "safe" as in "functional safety" this seems a good option to me. The best option is of course proper handling, but as discussed, we are speaking of the scenario in which the code is already buggy and which is the fallout option doing the least damage.
Right -- harm reduction. :)
Quoted 12 lines
What I have in mind is a new
__ob_saturatetype qualifier. Something like:void foo(int num) { int __ob_saturate saturate_var = num; saturate_var += 42; }would just print a warning and continue execution, thus solving the trapping issue. The above code would generate something equivalent to that:
Quoted 9 lines
void foo(int num)
{
int __ob_saturate saturate_var = num;
if (check_add_overflow(saturate_var, increment,
&saturate_var) {
WARN(true, "saturation occurred");
saturate_var = type_max(saturate_var);
}Right, yes. Note that __ob_saturate is entirely unimplemented, but we wanted to leave the door open for other Overflow Behaviors. (It was tricky enough to even get the semantics worked out from wrap and trap, so we wanted to get to a distinct first step landed first.)
For the "warn" part with __ob_trap, we borrowed the Sanitizer infrastructure since architecturally it's in exactly the same places that __ob_trap needs to be checking, and already has everything available. In the case of __ob_saturate, it would only be informational. (Arguably, there should be no "warn" at all, as it's the "expected" behavior, just like __ob_wrap has no "warn" on wrap-around. But it seems sensible to me to make that available by enabling the sanitizers too.)
Quoted 6 lines
People using those saturating integers could then later check that the value is still in bound.
This is basically what your
size_add()fromoverflow.his already doing. If an overflow occurred, the allocation the addition does not trap, it just saturates and let the allocation functions properly handle the issue.
Right.
Quoted 4 lines
The saturation can neutralize many security attacks and can mitigate some safety issues. Think of the Ariane 5 rocket launch: a saturation could have prevented the unintended fireworks.
Quoted 8 lines
The caveat I can think of is that the old overflow check pattern becomes invalid. Doing:
if (
saturate_var+ increment < increment)is now bogus and would need to be caught if possible by static analysis. So those saturating integers will only be usable in newly written code and could not be easily retrofitted.
In theory, the "ignored patterns" (or "idiom exclusions") would already allow this to continue to behave correctly, though it may be worth trying to figure out if this is "correct" or not.
Quoted 10 lines
Documentation/process/arithmetic-overflow.rst line 25
Quoted 6 lines
Documentation/process/arithmetic-overflow.rst new file +6
-0+In the C standard, three basic types can be involved in arithmetic, and each +has a default strategy for solving the overflow problem: + + - Signed overflow is undefined + - Unsigned overflow explicitly wraps around + - Pointer overflow is undefinedNitpick: the C standard uses different definitions than yours. In the standard:
Quoted 6 lines
- overflow is *always* undefined
- unsigned integer wraparound
- signed integer overflowThe nuance is that in the standard unsigned integers do not overflow, they just wraparound.
I guess that's technically true, but for understanding the "overflow resolution" properties (from a mathematical perspective), the question is "what happens when a value cannot be represented by the bit pattern of the storage?" But I think we understand each other here. :) So given that under C, signed is undefined and unsigned in wraparound, this is how we ended up phrasing it.
Quoted 3 lines
I am not asking you to change your terminology, but it could be good to state in your document that your definition of overflow differs from the standard's definition. Maybe a terminology section could help.
I'm open to whatever you think would make this more clear. :)
-Kees
Signature · 1 line
Kees Cook
Kees Cook
maintainerPATCH 4/5 lkdtm/bugs: Add basic Overflow Behavior Types test
In reply to Kees CookExercise 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=y, as well as the corner cases associated with promotion, casting, etc.
For example, executing this test with CONFIG_OVERFLOW_BEHAVIOR_TYPES_WARN=y (instead of CONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP=y), will show:
$ echo OBT_ASSIGN_TRUNCATE_TO | cat >/sys/kernel/debug/provoke-crash/DIRECT
$ 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 'u8t' (aka '__ob_trap u8'), truncated to 255---
Cc: Arnd Bergmann <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Shuah Khan <[email protected]>
Cc: <[email protected]>
--- drivers/misc/lkdtm/bugs.c | 253 ++++++++++++++++++++++++ tools/testing/selftests/lkdtm/tests.txt | 10 + 2 files changed, 263 insertions(+)
drivers/misc/lkdtm/bugs.c +253 -0
@@ -817,6 +817,249 @@ static noinline void lkdtm_CORRUPT_PAC(void)#endif } +static void lkdtm_OBT_ASSIGN_TRUNCATE_TO (void ) +{ +volatile int big =INT_MAX ; +volatile int wide_low_value =5 ; + u8 __ob_trap narrow_low_value = 0; + s32 __ob_trap same = 0; + u8 __ob_trap small = 0; + +pr_info ("Performing same-width assignment to OBT\n" ); + same = big; + +pr_info ("Performing small-value assignment to OBT\n" ); + narrow_low_value = wide_low_value; + +pr_info ("Expecting trap on truncated assignment to OBT\n" ); + small = 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 = INT_MAX; + volatile s32 __ob_trap wide_low_value = 5; +u8 narrow_low_value =0 ; +s32 same =0 ; +u8 small =0 ; + +pr_info ("Performing same-width assignment from OBT\n" ); + same = big; + +pr_info ("Performing small-value assignment from OBT\n" ); + narrow_low_value = wide_low_value; + +pr_info ("Expecting trap on truncated assignment from OBT\n" ); + small = 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 = INT_MAX; +u32 trunc =0 ; +u32 small =0 ; + +pr_info ("Performing wrapping too-small cast\n" ); + trunc = (u16 __ob_wrap)big; + +pr_info ("Expecting trap on too-small cast\n" ); + small = (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 = UINT_MAX; +s32 neg =0 ; +s32 small =0 ; + +pr_info ("Performing explicit sign-changing cast\n" ); + neg = (s32 __ob_wrap)big; + +pr_info ("Expecting trap on unexpected sign-changing cast\n" ); + small = (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 = 100; + volatile u8 __ob_trap b8 = 3; +unsigned int promoted; +/* 32-bit or larger, however, get checked. */ + volatile u32 __ob_trap a = UINT_MAX - 1; + volatile u32 __ob_trap b = 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 = a8 * b8; + +pr_info ("Performing non-overflowing unsigned multiplication\n" ); + happy = b * b; + +pr_info ("Expecting trap on overflowing unsigned multiplication\n" ); + outcome = 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 = 100; + volatile s8 __ob_trap b8 = 3; +int promoted; +/* 32-bit or larger, however, get checked. */ + volatile s32 __ob_trap a = INT_MAX - 1; + volatile s32 __ob_trap b = 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 = a8 * b8; + +pr_info ("Performing non-overflowing signed multiplication\n" ); + happy = b * b; + +pr_info ("Expecting trap on overflowing signed multiplication\n" ); + outcome = a * b; + +pr_err ("FAIL: survived signed multiplication overflow: %d * %d -> %lld (ok: %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 = 250; + volatile u8 __ob_trap b8 = 30; +unsigned int promoted; +/* 32-bit or larger, however, get checked. */ + volatile u32 __ob_trap a = UINT_MAX - 1; + volatile u32 __ob_trap b = 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 = 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 = b + b; + +pr_info ("Expecting trap on overflowing unsigned addition\n" ); + outcome = 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 = 120; + volatile s8 __ob_trap b8 = 30; +int promoted; +/* 32-bit or larger, however, get checked. */ + volatile s32 __ob_trap a = INT_MAX - 1; + volatile s32 __ob_trap b = 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 = 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 = b + b; + +pr_info ("Expecting trap on overflowing signed addition\n" ); + outcome = 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 =256 ; +size_t outcome; + +pr_info ("Expecting trap on overflowing unsigned negation\n" ); + outcome = 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 =300 ; +volatile int flag =0 ; +int i; + u8 __ob_wrap wrapper = 0; /* Explicitly wrapping. */ + u8 __ob_trap counter = 0; + +pr_info ("Performing u8 __ob_wrap post-increment past 255\n" ); +for (i =0 ; i < target; i++) + wrapper++; +if (wrapper !=44 ) +pr_err ("FAIL: wrapped incorrecty: %u\n" , wrapper); + +pr_info ("Performing idiomatic post-decrement zero test\n" ); + counter = target /2 ; +while (counter--) +if (flag) +break ; +if (counter !=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 =0 ; +for (i =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[] = {CRASHTYPE (PANIC ),CRASHTYPE (PANIC_STOP_IRQOFF ),
@@ -850,6 +1093,16 @@ static struct crashtype crashtypes[] = {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), };struct crashtype_category bugs_crashtypes = {
tools/testing/selftests/lkdtm/tests.txt +10 -0
@@ -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
Signature · 1 line
2.34.1
Re: [PATCH 4/5] lkdtm/bugs: Add basic Overflow Behavior Types test
Hi,
Quoted 103 lines · Kees Cook
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 underCONFIG_OVERFLOW_BEHAVIOR_TYPES=y, as well as the corner cases associated with promotion, casting, etc.For example, executing this test with
CONFIG_OVERFLOW_BEHAVIOR_TYPES_WARN=y(instead ofCONFIG_OVERFLOW_BEHAVIOR_TYPES_TRAP=y), will show:$ echo OBT_ASSIGN_TRUNCATE_TO | cat >/sys/kernel/debug/provoke-crash/DIRECT $ 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 'u8t' (aka '__ob_trap u8'), truncated to 255Signed-off-by Kees Cook <[email protected]>---
Cc: Arnd Bergmann <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Cc: Shuah Khan <[email protected]>
Cc: <[email protected]>--- drivers/misc/lkdtm/bugs.c | 253 ++++++++++++++++++++++++ tools/testing/selftests/lkdtm/tests.txt | 10 + 2 files changed, 263 insertions(+)drivers/misc/lkdtm/bugs.c +67
-0@@ -817,6 +817,249 @@ static noinline void lkdtm_CORRUPT_PAC(void)#endif } +static void lkdtm_OBT_ASSIGN_TRUNCATE_TO (void ) +{ +volatile int big =INT_MAX ; +volatile int wide_low_value =5 ; + u8 __ob_trap narrow_low_value = 0; + s32 __ob_trap same = 0; + u8 __ob_trap small = 0; + +pr_info ("Performing same-width assignment to OBT\n" ); + same = big; + +pr_info ("Performing small-value assignment to OBT\n" ); + narrow_low_value = wide_low_value; + +pr_info ("Expecting trap on truncated assignment to OBT\n" ); + small = 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 = INT_MAX; + volatile s32 __ob_trap wide_low_value = 5; +u8 narrow_low_value =0 ; +s32 same =0 ; +u8 small =0 ; + +pr_info ("Performing same-width assignment from OBT\n" ); + same = big; + +pr_info ("Performing small-value assignment from OBT\n" ); + narrow_low_value = wide_low_value; + +pr_info ("Expecting trap on truncated assignment from OBT\n" ); + small = 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 = INT_MAX; +u32 trunc =0 ; +u32 small =0 ; + +pr_info ("Performing wrapping too-small cast\n" ); + trunc = (u16 __ob_wrap)big; + +pr_info ("Expecting trap on too-small cast\n" ); + small = (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 = UINT_MAX; +s32 neg =0 ; +s32 small =0 ; +
drivers/misc/lkdtm/bugs.c static noinline void lkdtm_CORRUPT_PAC(void) · line 896
Quoted 10 lines
drivers/misc/lkdtm/bugs.c +10
-0+pr_info ("Performing explicit sign-changing cast\n" ); + neg = (s32 __ob_wrap)big; + +pr_info ("Expecting trap on unexpected sign-changing cast\n" ); + small = (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 ); +}
Note to travelers and testers, we still have a compiler patch in flight [1] that fixes a bug with sign-change instrumentation concerning OBTs.
drivers/misc/lkdtm/bugs.c static noinline void lkdtm_CORRUPT_PAC(void) · line 901
Quoted 5 lines
drivers/misc/lkdtm/bugs.c +5
-0+ +static void lkdtm_OBT_MUL (void ) +{ +/* Promotion means no overflow checking can happen. */ + volatile u8 __ob_trap a8 = 100;
<snip>
[1]: https://github.com/llvm/llvm-project/pull/188340
Justin
Kees Cook
maintainerPATCH 5/5 types: Add standard __ob_trap and __ob_wrap scalar types
In reply to Kees CookWhile Linux's use of -fno-strict-overflow means that all arithmetic operations have a defined behavior (2's-complement wrapping), there isn't a way to unambiguously specify if a given variable was designed or intended to wrap around by the author.
Introduce explicit trapping and wrapping types for all bit widths including architecture word length (i.e. "long"), signed and unsigned, for use going forward for unambiguous arithmetic, now available via Clang 23+'s Overflow Behavior Types[1] (CONFIG_OVERFLOW_BEHAVIOR_TYPES=y).
Bike shedding time! How should these be named? We already have the short bit width types, named as: {u,s}{8,16,32,64}. We need to construct new type names that also indicate their overflow behavior: "trapping" or "wrapping". And we need to capture the "architectural word" length type too (i.e. what "unsigned long" or "size_t" captures).
Whole word addition:
- Pro: Unambiguous
- Con: Long. E.g. suffixed "u16_trap", or prefixed "wrap_u16"
Single letter addition, "t" for "trap" and "w" for "wrap":
- At the end: but "u8t" looks like the "t" is "type", like "uint8_t".
- At the front: but "wu8" looks like the "w" is "wide", like "wchar_t".
Current straw-man proposal is single letter suffix because it vaguely felt like the least bad of all choices, and they should be short or everyone will just continue to type "int". :)
---
Cc: Justin Stitt <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Linus Torvalds <[email protected]>
Cc: Miguel Ojeda <[email protected]>
Cc: Nathan Chancellor <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Andy Shevchenko <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: "Matthew Wilcox (Oracle)" <[email protected]>
Cc: Suren Baghdasaryan <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Finn Thain <[email protected]>
Cc: Geert Uytterhoeven <[email protected]>
Cc: "Thomas Weißschuh" <[email protected]>
Cc: <[email protected]>
--- include/linux/types.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
include/linux/types.h +24 -0
@@ -94,6 +94,30 @@ typedef unsigned int uint;typedef unsigned long ulong ;typedef unsigned long long ullong ; +/* Trapping types. */ +typedef u8 __ob_trap u8t; +typedef u16 __ob_trap u16t; +typedef u32 __ob_trap u32t; +typedef u64 __ob_trap u64t; +typedef unsigned long __ob_trap ulongt ; +typedef s8 __ob_trap s8t; +typedef s16 __ob_trap s16t; +typedef s32 __ob_trap s32t; +typedef s64 __ob_trap s64t; +typedef signed long __ob_trap slongt ; + +/* Wrapping types. */ +typedef u8 __ob_wrap u8w; +typedef u16 __ob_wrap u16w; +typedef u32 __ob_wrap u32w; +typedef u64 __ob_wrap u64w; +typedef unsigned long __ob_wrap ulongw ; +typedef s8 __ob_wrap s8w; +typedef s16 __ob_wrap s16w; +typedef s32 __ob_wrap s32w; +typedef s64 __ob_wrap s64w; +typedef signed long __ob_wrap slongw ; +#ifndef __BIT_TYPES_DEFINED__#define __BIT_TYPES_DEFINED__
Signature · 1 line
2.34.1
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 4 lines · Kees Cook
Current straw-man proposal is single letter suffix because it vaguely felt like the least bad of all choices, and they should be short or everyone will just continue to type "int". :)
Violently disagree.
I'd rather have people continue to use other types than have somethign that isn't visually obvious for *VERY* subtle semantic changes.
If somebody starts using explicitly trapping types, they need to say so. Not just *say* so, but scream it at the top of their lungs. No hidden subtle behavior changes. This needs to look _very_different_.
No stupid one-character things. If we go down this path it would need to be "wrapping_u32" or whatever.
That said, I think the trapping behavior is unacceptable unless there's some way to recover from it. An dno, that "some way" is not the broken C++ style exception handling. That thing is pure and utter shit, and fragile as hell. Anything that requires trying to unwind the stack is just a complete no-go because it's going to be buggy and untestable (sure, you can have test-cases and the unwinding will work for *those*, but...)
I don't actually see any sane interface. The "unsafe_get_user()" thing with actual labels and exception tables works very well, but it would require wrapping all trapping operations in a macro.
Which is maybe not a bad idea - it's almost certainly better than the overflow builtins - but might also be disgusting. Hard to tell.
Linus
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 3 lines · Linus Torvalds
No stupid one-character things. If we go down this path it would need to be "
wrapping_u32" or whatever.
That sounds close to similar types provided by Rust, i.e. if something like these types are used, then it would be nice to match names if possible (assuming they are similar enough, i.e. no surprises).
e.g. with generics they do Wrapping<u32> or, in other cases, without generics they name them something like e.g. NonZeroU32.
Having said that...
Quoted 1 line
This needs to look _very_different_.
I think there is an actual risk of someone e.g. copying an expression from one place to another, with the operators behaving differently, yeah.
In the Rust side, even if those "explicit" types like the wrapping_u32 you suggest exist, we generally use the methods on the normal integers instead, e.g.
i.wrapping_add(1)
micros.saturating_mul(NSEC_PER_USEC)
self.index.checked_mul(page::PAGE_SIZE)?etc.
The advantage is precisely that it is more explicit and avoids confusing the operators when copy-pasting code and so on.
So that could perhaps be an option? Kees et al. have been thinking about this for a long time as far as I recall.
[ And for the usual operators on integer primitives, we panic by default (configurable via Kconfig), but at least that applies to all Rust code, which is all new, and kernel developers get accustomed to think about what they actually want for a particular operation and whether they need one of the explicit behaviors above, so it has been good so far, but it remains to be seen what happens when we have way more code around etc. It does mean someone copying a simple math expression from C to Rust could forget about it, though... ]
Cheers,
Miguel
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 4 lines · Miguel Ojeda
In the Rust side, even if those "explicit" types like the
wrapping_u32you suggest exist, we generally use the methods on the normal integers instead, e.g.
In that case the types in question should always be very much opaque, and not usable as-is by existing compilers that don't have attributes.
My feeling is that that will discourage use enormously for when people want to just say "yes, I know this wraps, and it's ok".
That said, for the *trapping* types, I do think that we likely need an opaque type, because I really feel like using
trapping_u32 x;
...
x++;
is a complete and utter mis-design. It makes the "x++' have random behavior that
(a) cannot be recovered from (maybe we're holding random locks)
(b) is completely invisible in the context of the code, because the
type may be somewhere very different
and I think both of those are fundamental design mistakes.
And no, "dead machine" is *still* not an acceptable form of "that's not random behavior".
So I think wrapping and trapping are fundamentally very different. The words may look the same. The semantics may often be discussed together. But one is explicitly marking something as "overflow is safe and expected", and that's the actual real SAFE case.
The other is saying "overflow needs special handling". And the key here is that we need to have some way to *state* what said special handling is, and we need to do it at the point where that special handling is needed. Not some generic exception handler that has to figure things out from some unknown context.
Very very different things, and they need very very different interfaces and very very different infrastructure.
Anything that says "these are two faces of the same coin and are just different attributes on the type" is simply broken by design.
Linus
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 2 lines · Linus Torvalds
The other is saying "overflow needs special handling"
Btw, this is why I also completely despise the current overflow builtins.
Yes, they handle overflow. But they don't treat it as some *special* thing. They are garbage that forces code that uses them to be garbage.
If you want to use the overflow builtins (or our wrappers aorund them), you can't just do the math. You have to do crazy crap like
int end;
if (check_add_overflow(start, len, &end))
... handle overflow ...
do_something(start, end);which obviously no sane person will do, when they can just write
do_something(start, start+len);
instead (particularly since usually that "start+len" is just a tiny detail in the code anyway).
Notice how it breaks up the code logic, and also requires you to add random intermediate variables etc. It's bad, it makes the code look bad, and people don't use it as a result.
The reason people like exception handling is that you can make the fixup be done elsewhere, and you *don't* have to deal with it at the point where you just want to use the result and with silly intermediate variables etc.
IOW, exception handling means that you can continue to use the normal flow of code for the normal case, and you deal with errors separately.
That is good. Much better than the "check_sub_overflow()" kind of crazy thing.
So I very much understand why all modern languages do it - but at the same time most exception handling is complete garbage, because almost everybody ends up thinking that it should nest syntactically, which is completely wrong.
Error handling does not nest: it exits. If you have two different exceptional cases in the same expression or statement, they have no inherent nesting, and the order isn't even someting you should care about. But they can cause different error codes or different fixups, and they need *separate* handling.
This is why the kernel user space exception handling ended up with a "label" model the moment the compilers could deal with it (in fact, I very much asked for that interface, and compilers finally gave it to me after years).
It means that you can move the exception handling out of line, without having to interrupt the actual normal code flow. And youc an do it without the bogus nesting that makes no sense.
So I think overflow handling should do the same. Instead of the bad "check_sub_overflow()" model we have now, we should have
res = add_overflow(a,b,label);
and now you can use a trapping operation *without* having to break the normal flow of code, ie you can do
do_something(start, add_overflow(start, len, overflow))
...
overflow:
// Maybe needs to release locks, who knows
return -EINVAL;notice?
Wrapping does not need this kind of thing. Wrapping is literally a "I know I don't need to care", while trapping is a "I know I need to handle it".
It's just that handling the trapping should not need to be done right where the operation is done.
And in C, that means "goto". And I actualyl will claim that "goto" is superior to most crazy language models with some "try()" block that nests.
Language designers have been corrupted by "things must nest". BS.
Linus
Kees Cook
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 20 lines · Linus Torvalds
If you want to use the overflow builtins (or our wrappers aorund them), you can't just do the math. You have to do crazy crap like
int end; if (check_add_overflow(start, len, &end)) ... handle overflow ... do_something(start, end);which obviously no sane person will do, when they can just write
do_something(start, start+len);[...]
So I think overflow handling should do the same. Instead of the bad "check_sub_overflow()" model we have now, we should haveres = add_overflow(a,b,label);and now you can use a trapping operation *without* having to break the normal flow of code, ie you can do
Quoted 7 lines
do_something(start, add_overflow(start, len, overflow))
...
overflow:// Maybe needs to release locks, who knows return -EINVAL;notice?
The syntax problem (as made clear by many other people, and even you here in the first half of this email) is that no one will use function based math primitives. Everyone absolutely hates it. I would have gone this route (as it is the design of the user-access code), but everyone so violently rejected functional math that it seemed not even worth the attempt.
Quoted 6 lines
Wrapping does not need this kind of thing. Wrapping is literally a "I know I don't need to care", while trapping is a "I know I need to handle it".
It's just that handling the trapping should not need to be done right where the operation is done.
I agree completely. The trouble is how to build that into the existing arithmetic statement syntax of C. This has been an unsolved problem for 50 years. :(
Signature · 1 line
Kees Cook
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 4 lines · Kees Cook
The syntax problem (as made clear by many other people, and even you here in the first half of this email) is that no one will use function based math primitives.
I don't think that's true.
It's just that they have to be made simple enough to use, and have a good *reason* to use them without the end result becoming horrendous.
Ok, so I just spent fifteen minutes trying it out, trying to aim for a really simple syntax.
Look at this contrieved example where there are two different overflow things with two different exception handlers. I decided to add a "default" label that is just called "overflow", so you can write code like this:
static int testme(int a, int b, int c, int d)
{
return addo(addmulo(a,b,c,addmul_overflow),d);
overflow:
return -1;
addmul_overflow:
return -2;
}which obviously isn't pretty, but it's still at least somewhat readable. It does a "addmul" and an "add", both with overflow handling, and returns the end result.
If the final add overflows (which doesn't have an explicit overflow label name), it goes to the default "overflow:" label.
And if the addmul overflows, it goes to addmul_overflow. It's all kind of obvous, and not syntactically all that onerous.
And it does work:
#define TEST(x) printf(#x "=%d\n", x)
#define MAX_INT 2147483647
int main(int argc, char **argv)
{
TEST(testme(1,2,3,4));
TEST(testme(MAX_INT,2,3,4));
TEST(testme(1,2,3,MAX_INT));
return 0;
}results in:
$ gcc -O2 ov.c && ./a.out
testme(1,2,3,4)=11
testme(MAX_INT,2,3,4)=-2
testme(1,2,3,MAX_INT)=-1and in this case gcc actually did everything at compile-time, so code generation is actually good too: the compiler will optimize this to hell and back.
But even *without* constant arguments, the compiler can actually generate good code too:
.globl testme
.type testme, @function
testme:
.LFB11:
.cfi_startproc
imull %edx, %esi
jo .L6
addl %edi, %esi
jo .L6
addl %ecx, %esi
jo .L15
movl %esi, %eax
ret
.L6:
movl $-2, %eax
ret
.L15:
movl $-1, %eax
ret
that really isn't bad.
So the code is legible, the code generation is fine, and it's pretty flexible. And are the macros complicated? No. This is literally the code that did all this:
#define __default_exception(a,b,...) b
#define default_exception(...)
__default_exception(,##__VA_ARGS__,overflow)
#define overflow_op(op,a,b,c) __builtin_##op##_overflow(a,b,c)
#define __overflow(op,a,b,...) ({ \
__typeof__(a) __res; \
if (overflow_op(op,a,b,&__res)) \
goto default_exception(__VA_ARGS__); \
__res; })
#define addo(a,b,...) __overflow(add,a,b,##__VA_ARGS__)
#define mulo(a,b,...) __overflow(mul,a,b,##__VA_ARGS__)
#define addmulo(a,b,c,...) addo(a,mulo(b,c,##__VA_ARGS__),##__VA_ARGS__)Now will people ENJOY using "addo()" and things like that? No. Clearly it's still *easier* and even clearer to just write
return a + b*c + d;
and yes, that is more legible.
But no, I really *really* don't want people to be able to just randomly say "I'm just going to kill the kernel if this overflows".
Linus
Kees Cook
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
In reply to Linus TorvaldsQuoted 20 lines · Linus Torvalds
Quoted 4 lines · Miguel Ojeda
In the Rust side, even if those "explicit" types like the
wrapping_u32you suggest exist, we generally use the methods on the normal integers instead, e.g.In that case the types in question should always be very much opaque, and not usable as-is by existing compilers that don't have attributes.
My feeling is that that will discourage use enormously for when people want to just say "yes, I know this wraps, and it's ok".
That said, for the *trapping* types, I do think that we likely need an opaque type, because I really feel like using
trapping_u32 x; ... x++;
Quoted 8 lines
is a complete and utter mis-design. It makes the "x++' have random behavior that
(a) cannot be recovered from (maybe we're holding random locks)
(b) is completely invisible in the context of the code, because the
type may be somewhere very differentand I think both of those are fundamental design mistakes.
This design is specifically what Peter was requesting, and what actually integrates with C. I agree with you that the core problem is "cannot be recovered from", but that misses the point of these types. The point is that all of their uses are _supposed_ to have been written in a way that no overflow is possible (just like all the other types). But this is the problem: bugs keep happening, no matter what people try to do. And in fact, to support these kinds of in-code overflow checking, there are even idiom exclusions for these types (based on what you pointed out in the original RFC) to allow for things like:
if (var + offset < var) { ... }If the code was written perfectly, then there's no problem. If there was a bug that allows for overflow then you get a crash instead of totally insane behavior that is almost always exploitable in a way that the system gets compromised. That is a net benefit, even if crashes are still bad.
The point is to make a type that still works with C and all the associated APIs (e.g. format strings, native arithmetic, etc) without creating the mess that Jakub, Peter, and others (correctly) balked at around accessors for doing function based math.
Quoted 4 lines
So I think wrapping and trapping are fundamentally very different. The words may look the same. The semantics may often be discussed together. But one is explicitly marking something as "overflow is safe and expected", and that's the actual real SAFE case.
Right. Mixing the term "safe" between these is certainly a mistake in the documentation. We can fix all of that.
Quoted 5 lines
The other is saying "overflow needs special handling". And the key here is that we need to have some way to *state* what said special handling is, and we need to do it at the point where that special handling is needed. Not some generic exception handler that has to figure things out from some unknown context.
The generic exception handler, right now, is the distant back-stop to catch exceptional cases that nothing else was written to catch. Like uncorrectable RAM errors. Using a trapping type isn't there for people to _intend_ to crash the system. :)
But, yes, I agree that having a way to require in-place overflow management would be the perfect solution, but no one seems to be able to agree on it. The trouble with C arithmetic is that the overflow state is "hidden". It's like the remainder from division: math statements need an overflow case built in, almost like a ?:, but from a syntax perspective, there's not been anything that stuck. The state of the art in C is "make sure you test for overflow manually first", and these types allow for that.
-Kees
Signature · 1 line
Kees Cook
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 2 lines · Kees Cook
If the code was written perfectly, then there's no problem.
My point is that BUG_ON() DOES NTO SOLVE THE PROBLEM.
Quoted 4 lines
The point is to make a type that still works with C and all the associated APIs (e.g. format strings, native arithmetic, etc) without creating the mess that Jakub, Peter, and others (correctly) balked at around accessors for doing function based math.
Has anybody tried to suggest that "use a label" model?
Because I 100% agree that the current overflow handling is pure garbage, and doesn't allow the code to be used in any kind of sane code.
But I think that's solvable with the "branch out on error to be handled elsewhere" model.
Linus
Kees Cook
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
In reply to Miguel OjedaQuoted 10 lines · Miguel Ojeda
In the Rust side, even if those "explicit" types like the
wrapping_u32you suggest exist, we generally use the methods on the normal integers instead, e.g.i.wrapping_add(1) micros.saturating_mul(NSEC_PER_USEC) self.index.checked_mul(page::PAGE_SIZE)?
Quoted 7 lines
etc.
The advantage is precisely that it is more explicit and avoids confusing the operators when copy-pasting code and so on.
So that could perhaps be an option? Kees et al. have been thinking about this for a long time as far as I recall.
I went through 7 revisions of creating helpers/accessors[1] (and function-level annotations) and it ultimately went unused. From memory, this was specifically from Jakub Kicinski (found breaking up readable math statements into a series of helpers too bulky), Peter Zijlstra and Mark Rutland (wanted strictly type-based system)[2].
I view accessors as a non-starter given the near universal pushback against them in C.
Signature · 1 line
Kees Cook
Kees Cook
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
In reply to Linus TorvaldsQuoted 5 lines · Linus Torvalds
Quoted 4 lines · Kees Cook
Current straw-man proposal is single letter suffix because it vaguely felt like the least bad of all choices, and they should be short or everyone will just continue to type "int". :)
Quoted 7 lines
[...]
If somebody starts using explicitly trapping types, they need to say so. Not just *say* so, but scream it at the top of their lungs. No hidden subtle behavior changes. This needs to look _very_different_.No stupid one-character things. If we go down this path it would need to be "
wrapping_u32" or whatever.
Yeah, that's fine. I'm fine calling these types whatever we want (regardless of how we ultimately bolt exception handling to them).
The only reason I had this proposal using a short forms was to make it "easy" to get counters/indexes/iterators with as few characters as possible. It all comes back to my "favorite" security flaw where a u8 counter wrapped during post-increment in a while loop. Why was it "u8"? No good reason besides "it was even less to type than 'int'" AFAICT. :P
Quoted 3 lines
I don't actually see any sane interface. The "
unsafe_get_user()" thing with actual labels and exception tables works very well, but it would require wrapping all trapping operations in a macro.
Mark Rutland had strong reservations about function-level annotations, but I wonder if the combination of new type _and_ function-level annotation could get us something near what would be palatable:
int __overflow_label(boom)
something(...)
{
u8 __ob_trap count;
...
take_locks();
...
while (thing())
count++;
destroy_the_world_if_count_wraps(count);
...
return 0;boom:
unlock_and_clean_up(...); return -EINVAL; }
This way not _all_ math is covered by the label, only the trapping math.
Or we could make the label a global part of the language itself so it wouldn't need to be a function annotation, but rather a _required_ element of any function that uses a trapping type?
-Kees
Signature · 1 line
Kees Cook
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 4 lines · Kees Cook
Mark Rutland had strong reservations about function-level annotations, but I wonder if the combination of new type _and_ function-level annotation could get us something near what would be palatable:
Yes, if we had some way to specify the label, that actually looks really nice to me.
So with _this_ kind of interface, all my reservations about it go away.
And as long as the compiler actually requires that label to exist when trapping arithmetic is done, I don't think people will use it without having fixups.
Quoted 3 lines
Or we could make the label a global part of the language itself so it wouldn't need to be a function annotation, but rather a _required_ element of any function that uses a trapping type?
Yes, I'd be ok with that too, because I think in practice you typically only ever have one, and I guess you could use local labels - or multiple functions - if you really needed to have different targets.
We have a few years of experience with "unsafe_get_user()" and friends, and a few hundred places that use it, and while it's common to have several cases in one function, I can't think of a single case where we actually had more than one error target.
I tried a quick grep, and nothing jumped out at me.
(And a lot of them use "Efault" or "efault" as the target name, so it probably would have been fine with a default name)
Linus
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 7 lines · Linus Torvalds
We have a few years of experience with "
unsafe_get_user()" and friends, and a few hundred places that use it, and while it's common to have several cases in one function, I can't think of a single case where we actually had more than one error target.I tried a quick grep, and nothing jumped out at me.
And the *moment* I sent that reply, I went "Wait a minute", and looked at strncpy_from_user().
So we do actually have at least one case of multiple exception labels:
the first one in that function handles the "potentially unaligned word access causes page fault, fall back to byte-at-a-time" while the second one is final and fatal and results in -EFAULT.
But that case could have been written with the byte-at-a-time case as a separate inline function, so it would all have worked fine even without explicitly named exception entries.
In some situations, the explicit names may be very useful just to document things: in that case the 'byte_at_a_time" label does do that, but I don't think it's a very big issue.
And it's likely even less of an issue for arithmetic overflows than it is for user space accesses.
So yeah, with better compiler support, I think that whole trapping behavior would be great.
Linus
Kees Cook
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
In reply to Linus TorvaldsQuoted 15 lines · Linus Torvalds
Quoted 4 lines · Kees Cook
Mark Rutland had strong reservations about function-level annotations, but I wonder if the combination of new type _and_ function-level annotation could get us something near what would be palatable:
Yes, if we had some way to specify the label, that actually looks really nice to me.
So with _this_ kind of interface, all my reservations about it go away.
And as long as the compiler actually requires that label to exist when trapping arithmetic is done, I don't think people will use it without having fixups.
Quoted 8 lines
Quoted 3 lines
Or we could make the label a global part of the language itself so it wouldn't need to be a function annotation, but rather a _required_ element of any function that uses a trapping type?
Yes, I'd be ok with that too, because I think in practice you typically only ever have one, and I guess you could use local labels - or multiple functions - if you really needed to have different targets.
Yeah, as you mentioned earlier, I'd agree that nesting is rarely useful. The only thing I'd want to be careful about is ordering/scope. I *think* it would just operate as a "goto" and things like the cleanup.h handlers wouldn't be involved: they operate when a scope is crossed like before. And I think the overflow result wouldn't be represented anywhere. i.e. the wrapped/truncated value wouldn't be stored:
int func()
{
...
u8 __ob_trap product = 5;
...
product = a * b; // if store is truncated, goto __overflow
...
return product;__overflow:
pr_info("%u\n", product); // shows "5"
return -1;
}(Isn't this just an implicit "try"?)
-Kees
Signature · 1 line
Kees Cook
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 2 lines · Kees Cook
(Isn't this just an implicit "try"?)
Yes. And I think that's ok.
I think try/catch is broken for a few reasons, but the fact that catch and try are tied together so closely is the main one. You can't "try" inside a scope without having the "catch" inside the same scope.
So then the solution is to just move the try to the outermost layer, and I think that's pretty much what everybody does.
But at that point, why not just move it *all* the way out, and make it implicit and invisible?
Which is kind of exactly what your suggestion is all about, and that's why I like it so much.
It *literally* fixes try/catch. It makes the only really valid usage model just work better.
(There are other reasons I dislike try/catch too, the whole "exceptions across function boundaries" being another one that your model avoids).
Linus
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Hi,
Quoted 11 lines · Linus Torvalds
Quoted 2 lines · Kees Cook
(Isn't this just an implicit "try"?)
Yes. And I think that's ok.
I think try/catch is broken for a few reasons, but the fact that catch and try are tied together so closely is the main one. You can't "try" inside a scope without having the "catch" inside the same scope.
Quoted 5 lines
So then the solution is to just move the try to the outermost layer, and I think that's pretty much what everybody does.
But at that point, why not just move it *all* the way out, and make it implicit and invisible?
How do we feel about type-defined labels? We can specify sane default handlers where we define the types:
typedef int __attribute__((overflow_behavior(trap, __handle_me)));
... and define specialized handlers later on
int func()
{
...
u8 __attribute__((overflow_behavior(trap, __BOOOOM))) product = 5;
...
product = a * b; // if store is truncated, goto __overflow
...
return product;__BOOOOM:
pr_info("%u\n", product); // shows "5"
return -1;
}We would then probably want a kernel shorthand to avoid long type definitions.
#define __TRAP(handler) __attribute__((overflow_behavior(trap,
handler)))
At least this way the label is always defined somewhere in-source rather than by a magic -fhandle-my-overflow-at=__overflow
Quoted 4 lines
Which is kind of exactly what your suggestion is all about, and that's why I like it so much.
Quoted 8 lines
It *literally* fixes try/catch. It makes the only really valid usage model just work better.
(There are other reasons I dislike try/catch too, the whole "exceptions across function boundaries" being another one that your model avoids).
Linus
Justin
Kees Cook
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 35 lines · Justin Stitt
Quoted 11 lines · Linus Torvalds
Quoted 2 lines · Kees Cook
(Isn't this just an implicit "try"?)
Yes. And I think that's ok.
I think try/catch is broken for a few reasons, but the fact that catch and try are tied together so closely is the main one. You can't "try" inside a scope without having the "catch" inside the same scope.
Quoted 5 lines
So then the solution is to just move the try to the outermost layer, and I think that's pretty much what everybody does.
But at that point, why not just move it *all* the way out, and make it implicit and invisible?
How do we feel about type-defined labels? We can specify sane default handlers where we define the types:
typedef int __attribute__((overflow_behavior(trap, __handle_me))) trapping_int;... and define specialized handlers later on
int
func(){ ... u8 __attribute__((overflow_behavior(trap, __BOOOOM))) product = 5; ... product = a * b; // if store is truncated, goto __overflow ... return product;
Quoted 4 lines
__BOOOOM:pr_info("%u\n", product); // shows "5" return -1; }
Yeah, I think this could work. As long as there's nothing special about the label mapping (i.e. many types can share the same label).
Quoted 8 lines
We would then probably want a kernel shorthand to avoid long type definitions.
#define __TRAP(handler) __attribute__((overflow_behavior(trap,handler)))
At least this way the label is always defined somewhere in-source rather than by a magic -fhandle-my-overflow-at=__overflow
Yeah, this sticks to Peter's "define in source" mandate from Plumbers.
Linus, does that still sound reasonable?
Also, is the preference still for "loud" names ("trap_u32") for the trapping scalar typedefs, or does the advent of the "required label on usage" make it more reasonable to have shorter names ("u32t")? I'm fine either way. I think there are pros and cons all over the place for the naming.
-Kees
Signature · 1 line
Kees Cook
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
In reply to Justin StittQuoted 7 lines · Justin Stitt
How do we feel about type-defined labels? We can specify sane default handlers where we define the types:
typedef int __attribute__((overflow_behavior(trap, __handle_me)));... and define specialized handlers later on
That sounds like an interesting interface, but I think it ends up being kind of odd, because normally the type definition would be in some global scope, while the 'handler' would be a local label name.
I think that in some situations - and certainly other projects - it would make a lot of sense to have the trap handler be a global function in that situation (ie "abort"), but with that being explicitly *not* what the kernel would want, it seems a bit odd to specify the name of a label that then is used in a totally different context.
So in your example:
Quoted 9 lines
int
func(){ ... u8 __attribute__((overflow_behavior(trap, __BOOOOM))) product = 5; ... product = a * b; // if store is truncated, goto __overflow ... return product;
Quoted 4 lines
__BOOOOM:pr_info("%u\n", product); // shows "5" return -1; }
The above makes total sense, but imagine instead the code being something more like this:
Some header file does this:
typedef unsigned int __attribute__((overflow_behavior(trap,__BOOOOM))) u32_overflow_t;
struct some_random_struct {
u32_overflow_t usage_count;
...
};The implementation for looking up those structs then looks something like this:
struct some_random_struct *get_entry(int hashval)
{
struct some_random_struct *n;
spin_lock(&hashlock);
n = find_entry(hashval);
if (n)
n->usage_count++;
spin_unlock(&hashlock);
return n;
__BOOOOM:
WARN_ON_ONCE("Entry 'n' overflowed\n", n->name);
spin_unlock(&hashlock);
return NULL;
}does that still make sense?
Now, I'm not *opposed* to this kind of interface - we'd presumably just use something like "overflow" for the label name anyway - and it allows people to use whatever label names make sense for a project so it's clearly a nice feature.
It just feels a bit odd. But maybe it makes perfect sense at a compiler level as a "it's just declaring the token to be used for the exception label".
Linus
Peter Zijlstra
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
In reply to Kees CookOn Tue, Mar 31, 2026 at 01:31:16PM -0700, Kees Cook wrote:
(still slowly digesting the thread)
Quoted 16 lines
Yeah, as you mentioned earlier, I'd agree that nesting is rarely useful. The only thing I'd want to be careful about is ordering/scope. I *think* it would just operate as a "goto" and things like the
cleanup.hhandlers wouldn't be involved: they operate when a scope is crossed like before. And I think the overflow result wouldn't be represented anywhere. i.e. the wrapped/truncated value wouldn't be stored:int
func(){ ... u8 __ob_trap product = 5; ... product = a * b; // if store is truncated, goto __overflow ... return product;
Quoted 4 lines
__overflow:pr_info("%u\n", product); // shows "5" return -1; }
Note that there is a 'fun' problem with this in combination with cleanup.h.
Something like:
int func()
{
u8 __ob_trap prod = 0;
scoped_guard (mutex, &my_lock) {
prod = a * b;
}
return prod;__overflow:
// whatever return -1; }
is fine. *HOWEVER*, something like:
int func()
{
int __ob_trap size = base + count * extra;
int err;
struct my_obj *obj __cleanup(kfree) = kzalloc(size, GFP_KERNEL);
err = my_obj_init(obj);
if (err)
return ERR_PTR(err);
return_ptr(obj);__overflow:
// what now.. return NULL; }
is most terribly broken. Specifically, the goto will jump into the scope of obj -- and that is not allowed.
Kees Cook
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 60 lines · Peter Zijlstra
On Tue, Mar 31, 2026 at 01:31:16PM -0700, Kees Cook wrote:
(still slowly digesting the thread)
Quoted 16 lines
Yeah, as you mentioned earlier, I'd agree that nesting is rarely useful. The only thing I'd want to be careful about is ordering/scope. I *think* it would just operate as a "goto" and things like the
cleanup.hhandlers wouldn't be involved: they operate when a scope is crossed like before. And I think the overflow result wouldn't be represented anywhere. i.e. the wrapped/truncated value wouldn't be stored:int
func(){ ... u8 __ob_trap product = 5; ... product = a * b; // if store is truncated, goto __overflow ... return product;Quoted 4 lines
__overflow:pr_info("%u\n", product); // shows "5" return -1; }Note that there is a 'fun' problem with this in combination with
cleanup.h.Something like:
int
func(){ u8 __ob_trap prod = 0; scoped_guard (mutex, &my_lock) { prod = a * b; } return prod;
__overflow:// whatever return -1; }is fine. *HOWEVER*, something like:
int
func(){ int __ob_trap size = base + count * extra; int err; struct my_obj *obj __cleanup(kfree) = kzalloc(size, GFP_KERNEL); err = my_obj_init(obj); if (err) return ERR_PTR(err); return_ptr(obj);
Quoted 7 lines
__overflow:// what now.. return NULL; }is most terribly broken. Specifically, the goto will jump into the scope of obj -- and that is not allowed.
Right, this has been my primary concern about having an implicit "goto" sprinkled basically anywhere into the code flow. However, it does seem that initialization checking is aware of the problem:
void func(void)
{
unsigned long __ob_trap value = ({ goto weird; 256; });
size_t outcome = 99;
outcome = get_outcome();
pr_info("outcome: %zu\n", outcome);
return;
weird:
pr_info("value: %lu\n", value);
pr_info("outcome: %zu\n", outcome);
}../drivers/misc/lkdtm/bugs.c:1059:35: warning: variable 'outcome' is uninitialized when used here [-Wuninitialized]
1059 | pr_info("outcome: %zu\n", outcome);
| ^~~~~~~../drivers/misc/lkdtm/bugs.c:1021:2: note: variable 'outcome' is declared here
1021 | size_t outcome = 99;
| ^../drivers/misc/lkdtm/bugs.c:1058:33: warning: variable 'value' is uninitialized when used here [-Wuninitialized]
1058 | pr_info("value: %lu\n", value);
| ^~~~~../drivers/misc/lkdtm/bugs.c:1020:2: note: variable 'value' is declared here
1020 | unsigned long __ob_trap value = ({ goto weird; 256; });
| ^But most importantly, if I add a cleanup after it, it gets rejected:
unsigned long __ob_trap value = ({ goto weird; 256; });
size_t outcome = 99;
u8 *obj __cleanup(kfree) = kzalloc(33, GFP_KERNEL);
...../drivers/misc/lkdtm/bugs.c:1021:37: error: cannot jump from this goto statement to its label
1021 | unsigned long __ob_trap value = ({ goto weird; 256; });
| ^../drivers/misc/lkdtm/bugs.c:1023:6: note: jump bypasses initialization of variable with __attribute__((cleanup))
1023 | u8 *obj __cleanup(kfree) = kzalloc(33, GFP_KERNEL);
| ^
(Though I would note that GCC does _not_ refuse the jump when there is a cleanup; it only see the other two uninitialized values.)
So that makes it not totally broken, but it does make it a bit fragile.
Another concern I have is dealing with older compilers and how to "hide" the label and its code. e.g. if I remove the "goto" from above:
../drivers/misc/lkdtm/bugs.c:1060:1: warning: label 'weird' defined but not used [-Wunused-label]
1060 | weird:
| ^~~~~
Oddly, the unreachable code isn't a problem, so we could just wrap the label is some macro like:
#define force_label(x) if (0) goto x; x
force_label(weird):
pr_info("value: %lu\n", value);
pr_info("outcome: %zu\n", outcome);Signature · 1 line
Kees Cook
Peter Zijlstra
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 2 lines · Kees Cook
(Though I would note that GCC does _not_ refuse the jump when there is a cleanup; it only see the other two uninitialized values.)
Yeah.. I know, but since we also build with clang, any such issue will get discovered.
Quoted 1 line
So that makes it not totally broken, but it does make it a bit fragile.
Right.
Quoted 7 lines
Another concern I have is dealing with older compilers and how to "hide" the label and its code. e.g. if I remove the "goto" from above:
../drivers/misc/lkdtm/bugs.c:1060:1: warning: label 'weird' defined but not used [-Wunused-label]
1060 | weird:| ^~~~~
Quoted 9 lines
Oddly, the unreachable code isn't a problem, so we could just wrap the label is some macro like:
#define
force_label(x)if (0) goto x; x
force_label(weird):pr_info("value: %lu\n", value); pr_info("outcome: %zu\n", outcome);
__maybe_unused also works on labels. Like:
__overflow: __maybe_unused
dead-code-here;
Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Hi,
Quoted 30 lines · Peter Zijlstra
Quoted 2 lines · Kees Cook
(Though I would note that GCC does _not_ refuse the jump when there is a cleanup; it only see the other two uninitialized values.)
Yeah.. I know, but since we also build with clang, any such issue will get discovered.
Quoted 1 line
So that makes it not totally broken, but it does make it a bit fragile.
Right.
Quoted 7 lines
Another concern I have is dealing with older compilers and how to "hide" the label and its code. e.g. if I remove the "goto" from above:
../drivers/misc/lkdtm/bugs.c:1060:1: warning: label 'weird' defined but not used [-Wunused-label]
1060 | weird:| ^~~~~Quoted 9 lines
Oddly, the unreachable code isn't a problem, so we could just wrap the label is some macro like:
#define
force_label(x)if (0) goto x; x
force_label(weird):pr_info("value: %lu\n", value); pr_info("outcome: %zu\n", outcome);
Quoted 6 lines
__maybe_unusedalso works on labels. Like:
__overflow:__maybe_unuseddead-code-here;
Completing the loop: Here's the Clang RFC discussing obt label handler design [1]
[1]: https://discourse.llvm.org/t/rfc-linux-kernel-discusses-overflowbehaviortypes/90486
Jusin
Peter Zijlstra
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
In reply to Kees CookQuoted 11 lines · Kees Cook
int
func(){ ... u8 __ob_trap product = 5; ... product = a * b; // if store is truncated, goto __overflow ... return product;
__overflow:pr_info("%u\n", product); // shows "5"
I'm confused by this 'product is still 5' thing. It seems to me that making this happen will, in general, require more instructions/registers than allowing the old value to be clobbered and have product be the truncated result of whatever overflow.
Specifically, what is the value of preserving the old value?
Quoted 2 lines
return -1; }
Kees Cook
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 11 lines · Peter Zijlstra
Quoted 9 lines · Kees Cook
int
func(){ ... u8 __ob_trap product = 5; ... product = a * b; // if store is truncated, goto __overflow ... return product;
Quoted 7 lines
Quoted 2 lines
__overflow:pr_info("%u\n", product); // shows "5"I'm confused by this 'product is still 5' thing. It seems to me that making this happen will, in general, require more instructions/registers than allowing the old value to be clobbered and have product be the truncated result of whatever overflow.
Yeah, that's fair. That's what happens to "out" already with the existing check_{op}_overflow(a, b, &out) builtins, and what happens right now with the "while (var--)" exception (var will wrap even as an __ob_trap).
Signature · 1 line
Kees Cook
Peter Zijlstra
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
In reply to Kees CookQuoted 9 lines · Kees Cook
int
func(){ ... u8 __ob_trap product = 5; ... product = a * b; // if store is truncated, goto __overflow ... return product;
Quoted 6 lines
__overflow:pr_info("%u\n", product); // shows "5" return -1; }(Isn't this just an implicit "try"?)
So I like this implicit try with a default label, and mostly I expect this will be fine.
But as Linus already mentioned, sometimes you might want more. Could we perhaps also have an explicit version, something along the lines of:
int func()
{
int __ob_trap size;
size = try(count * flex_size, __mul_overflow);
size = try(size + base_size, __add_overflow);
obj = kzalloc(size,...);}
where we have something like:
#define try(stmt, _label) ({ \
__label __overflow; \
if (0) { \
__overflow: \
goto _label; \
} \
stmt; })That is, have the overflow trapped and confined in the statement-expression by using the overflow label as a local label and use this little trampoline to re-direct to a custom label.
Kees Cook
maintainerRe: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types
Quoted 45 lines · Peter Zijlstra
Quoted 9 lines · Kees Cook
int
func(){ ... u8 __ob_trap product = 5; ... product = a * b; // if store is truncated, goto __overflow ... return product;Quoted 6 lines
__overflow:pr_info("%u\n", product); // shows "5" return -1; }(Isn't this just an implicit "try"?)
So I like this implicit try with a default label, and mostly I expect this will be fine.
But as Linus already mentioned, sometimes you might want more. Could we perhaps also have an explicit version, something along the lines of:
int
func(){ int __ob_trap size; size = try(count * flex_size, __mul_overflow); size = try(size + base_size, __add_overflow); obj = kzalloc(size,...);}
where we have something like:
#define try(stmt, _label) ({ \ __label __overflow; \ if (0) { \ __overflow: \ goto _label; \ } \ stmt; })
Quoted 3 lines
That is, have the overflow trapped and confined in the statement-expression by using the overflow label as a local label and use this little trampoline to re-direct to a custom label.
Yeah, that should work, and gives us a nice way to create handler overrides. We've have to make sure the "locally defined" labels (with __label__) and __ob_trap worked together sanely.
Signature · 1 line
Kees Cook