Introduce __flex_counter() which wraps __builtin_counted_by_ref(), as newly introduced by GCC[1] and Clang[2]. Use of __flex_counter() allows access to the counter member of a struct's flexible array member when it has been annotated with __counted_by(). Introduce typeof_flex_counter(), can_set_flex_counter(), and set_flex_counter() to provide the needed _Generic() wrappers to get sane results out of __flex_counter(). For example, with: struct foo { int counter; short array[] __counted_by(counter); } *p; __flex_counter(p->array) will resolve to: &p->counter typeof_flex_counter(p->array) will resolve to "int". (If p->array was not annotated, it would resolve to "size_t".) can_set_flex_counter(p->array, COUNT) is the same as: COUNT <= type_max(p->counter) && COUNT >= type_min(p->counter) (If p->array was not annotated it would return true since everything fits in size_t.) set_flex_counter(p->array, COUNT) is the same as: p->counter = COUNT; (It is a no-op if p->array is not annotated with __counted_by().) Signed-off-by: Kees Cook <kees@xxxxxxxxxx> --- Cc: Miguel Ojeda <ojeda@xxxxxxxxxx> Cc: "Gustavo A. R. Silva" <gustavoars@xxxxxxxxxx> Cc: Nathan Chancellor <nathan@xxxxxxxxxx> Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx> Cc: Nick Desaulniers <nick.desaulniers+lkml@xxxxxxxxx> Cc: Marco Elver <elver@xxxxxxxxxx> Cc: Przemek Kitszel <przemyslaw.kitszel@xxxxxxxxx> Cc: linux-hardening@xxxxxxxxxxxxxxx --- include/linux/compiler_types.h | 31 +++++++++++++++++++++++++++++ include/linux/overflow.h | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 981cc3d7e3aa..8b45ecfad5b1 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -453,6 +453,37 @@ struct ftrace_likely_data { #define __annotated(var, attr) (false) #endif +/* + * Optional: only supported since gcc >= 15, clang >= 19 + * + * gcc: https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-_005f_005fbuiltin_005fcounted_005fby_005fref + * clang: https://github.com/llvm/llvm-project/pull/102549 + */ +#if __has_builtin(__builtin_counted_by_ref) +/** + * __flex_counter() - Get pointer to counter member for the given + * flexible array, if it was annotated with __counted_by() + * @FAM: Pointer to flexible array member of an addressable struct instance + * + * For example, with: + * + * struct foo { + * int counter; + * short array[] __counted_by(counter); + * } *p; + * + * __flex_counter(p->array) will resolve to &p->counter. + * + * Note that Clang may not allow this to be assigned to a separate + * variable; it must be used directly. + * + * If p->array is unannotated, this returns (void *)NULL. + */ +#define __flex_counter(FAM) __builtin_counted_by_ref(FAM) +#else +#define __flex_counter(FAM) ((void *)NULL) +#endif + /* * Some versions of gcc do not mark 'asm goto' volatile: * diff --git a/include/linux/overflow.h b/include/linux/overflow.h index 0c7e3dcfe867..e2b81cb5576e 100644 --- a/include/linux/overflow.h +++ b/include/linux/overflow.h @@ -440,4 +440,40 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend) #define DEFINE_FLEX(TYPE, NAME, MEMBER, COUNTER, COUNT) \ _DEFINE_FLEX(TYPE, NAME, MEMBER, COUNT, = { .obj.COUNTER = COUNT, }) +/** + * typeof_flex_counter() - Return the type of the counter variable of a given + * flexible array member annotated by __counted_by(). + * @FAM: Pointer to the flexible array member within a given struct. + * + * Returns "size_t" if no annotation exists. + */ +#define typeof_flex_counter(FAM) \ + typeof(_Generic(__flex_counter(FAM), \ + void *: (size_t)0, \ + default: *__flex_counter(FAM))) + +/** can_set_flex_counter() - Check if the counter associated with the given + * flexible array member can represent a value. + * @FAM: Pointer to the flexible array member within a given struct. + * @COUNT: Value to check against the __counted_by annotated @FAM's counter. + */ +#define can_set_flex_counter(FAM, COUNT) \ + (!overflows_type(COUNT, typeof_flex_counter(FAM))) + +/** + * set_flex_counter() - Set the counter associated with the given flexible + * array member that has been annoated by __counted_by(). + * @FAM: Pointer to the flexible array member within a given struct. + * @COUNT: Value to store to the __counted_by annotated @FAM's counter. + * + * This is a no-op if no annotation exists. Count needs to be checked with + * can_set_flex_counter(FAM, COUNT) before using this function. + */ +#define set_flex_counter(FAM, COUNT) \ +({ \ + *_Generic(__flex_counter(FAM), \ + void *: &(size_t){ 0 }, \ + default: __flex_counter(FAM)) = (COUNT); \ +}) + #endif /* __LINUX_OVERFLOW_H */ -- 2.34.1