 
            Hi,
Add the __counted_by_ptr() macro for annotating pointer struct members with the "counted_by" attribute. Add LKDTM test, and a first user.
-Kees
Kees Cook (3): compiler_types: Introduce __counted_by_ptr() lkdtm/bugs: Add __counted_by_ptr() test PTR_BOUNDS coredump: Use __counted_by_ptr for struct core_name::corename
init/Kconfig | 11 +++ Makefile | 4 ++ include/linux/compiler_types.h | 21 +++++- include/uapi/linux/stddef.h | 4 ++ drivers/misc/lkdtm/bugs.c | 90 ++++++++++++++++++++++--- fs/coredump.c | 8 +-- tools/testing/selftests/lkdtm/tests.txt | 2 + 7 files changed, 127 insertions(+), 13 deletions(-)
 
            Introduce __counted_by_ptr(), which works like __counted_by(), but for pointer struct members:
struct foo { int a, b, c; char *buffer __counted_by_ptr(bytes); short nr_bars; struct bar *bars __counted_by_ptr(nr_bars); size_t bytes; };
Since "counted_by" can only be applied to pointer members in very recent compiler versions, its application ends up needing to be distinct from flexible array "counted_by" annotations, hence a separate macro.
Unfortunately, this annotation cannot be used for "void *" members (since such a member is considered a pointer to an incomplete type, and neither Clang nor GCC developers could be convinced otherwise[1], even in the face of the GNU extension that "void *" has size "1 byte" for pointer arithmetic). For "void *" members, we must use the coming "sized_by" attribute.
Link: https://gcc.gnu.org/pipermail/gcc-patches/2025-May/683136.html [1] Signed-off-by: Kees Cook kees@kernel.org --- Cc: Miguel Ojeda ojeda@kernel.org Cc: Nathan Chancellor nathan@kernel.org Cc: Nick Desaulniers nick.desaulniers+lkml@gmail.com Cc: Bill Wendling morbo@google.com Cc: Justin Stitt justinstitt@google.com Cc: Peter Zijlstra peterz@infradead.org Cc: Marco Elver elver@google.com Cc: Przemek Kitszel przemyslaw.kitszel@intel.com Cc: Andrew Morton akpm@linux-foundation.org Cc: Masahiro Yamada masahiroy@kernel.org Cc: Christophe Leroy christophe.leroy@csgroup.eu Cc: Johannes Weiner hannes@cmpxchg.org Cc: llvm@lists.linux.dev --- init/Kconfig | 11 +++++++++++ Makefile | 4 ++++ include/linux/compiler_types.h | 21 ++++++++++++++++++++- include/uapi/linux/stddef.h | 4 ++++ 4 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/init/Kconfig b/init/Kconfig index cab3ad28ca49..54691b086bc6 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -139,6 +139,17 @@ config CC_HAS_COUNTED_BY # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896 default y if CC_IS_GCC && GCC_VERSION >= 150100
+config CC_HAS_COUNTED_BY_PTR_BARE + def_bool $(success,echo 'struct foo { int *ptr __attribute__((__counted_by__(count))); int count; };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror) + +config CC_HAS_COUNTED_BY_PTR_EXP + def_bool $(success,echo 'struct foo { int *ptr __attribute__((__counted_by__(count))); int count; };' | $(CC) $(CLANG_FLAGS) -fexperimental-late-parse-attributes -x c - -c -o /dev/null -Werror) + depends on !CC_HAS_COUNTED_BY_PTR_BARE + +config CC_HAS_COUNTED_BY_PTR + def_bool y + depends on CC_HAS_COUNTED_BY_PTR_BARE || CC_HAS_COUNTED_BY_PTR_EXP + config CC_HAS_MULTIDIMENSIONAL_NONSTRING def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)
diff --git a/Makefile b/Makefile index d14824792227..1b297dcbb0df 100644 --- a/Makefile +++ b/Makefile @@ -933,6 +933,10 @@ KBUILD_CFLAGS += $(CC_AUTO_VAR_INIT_ZERO_ENABLER) endif endif
+ifdef CONFIG_CC_HAS_COUNTED_BY_PTR_EXP +KBUILD_CFLAGS += -fexperimental-late-parse-attributes +endif + # Explicitly clear padding bits during variable initialization KBUILD_CFLAGS += $(call cc-option,-fzero-init-padding-bits=all)
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 59288a2c1ad2..f197ea03b593 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -353,11 +353,14 @@ struct ftrace_likely_data { #endif
/* + * Runtime track number of flexible array member elements for use by + * CONFIG_FORTIFY_SOURCE and CONFIG_UBSAN_BOUNDS. + * * Optional: only supported since gcc >= 15 * Optional: only supported since clang >= 18 * * gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896 - * clang: https://github.com/llvm/llvm-project/pull/76348 + * clang: https://clang.llvm.org/docs/AttributeReference.html#counted-by-counted-by-or... * * __bdos on clang < 19.1.2 can erroneously return 0: * https://github.com/llvm/llvm-project/pull/110497 @@ -371,6 +374,22 @@ struct ftrace_likely_data { # define __counted_by(member) #endif
+/* + * Runtime track number of objects pointed to by a pointer member for + * use by CONFIG_FORTIFY_SOURCE and CONFIG_UBSAN_BOUNDS. + * + * Optional: only supported since gcc >= 16 + * Optional: only supported since clang >= 20 + * + * gcc: https://gcc.gnu.org/pipermail/gcc-patches/2025-April/681727.html + * clang: ... + */ +#ifdef CONFIG_CC_HAS_COUNTED_BY_PTR +# define __counted_by_ptr(member) __attribute__((__counted_by__(member))) +#else +# define __counted_by_ptr(member) +#endif + /* * Optional: only supported since gcc >= 15 * Optional: not supported by Clang diff --git a/include/uapi/linux/stddef.h b/include/uapi/linux/stddef.h index 9a28f7d9a334..111b097ec00b 100644 --- a/include/uapi/linux/stddef.h +++ b/include/uapi/linux/stddef.h @@ -72,6 +72,10 @@ #define __counted_by_be(m) #endif
+#ifndef __counted_by_ptr +#define __counted_by_ptr(m) +#endif + #ifdef __KERNEL__ #define __kernel_nonstring __nonstring #else
 
            On Tue, 21 Oct 2025 at 00:01, Kees Cook kees@kernel.org wrote:
Introduce __counted_by_ptr(), which works like __counted_by(), but for pointer struct members:
struct foo { int a, b, c; char *buffer __counted_by_ptr(bytes); short nr_bars; struct bar *bars __counted_by_ptr(nr_bars); size_t bytes; };
Since "counted_by" can only be applied to pointer members in very recent compiler versions, its application ends up needing to be distinct from flexible array "counted_by" annotations, hence a separate macro.
Unfortunately, this annotation cannot be used for "void *" members (since such a member is considered a pointer to an incomplete type, and neither Clang nor GCC developers could be convinced otherwise[1], even in the face of the GNU extension that "void *" has size "1 byte" for pointer arithmetic). For "void *" members, we must use the coming "sized_by" attribute.
Link: https://gcc.gnu.org/pipermail/gcc-patches/2025-May/683136.html [1] Signed-off-by: Kees Cook kees@kernel.org
Cc: Miguel Ojeda ojeda@kernel.org Cc: Nathan Chancellor nathan@kernel.org Cc: Nick Desaulniers nick.desaulniers+lkml@gmail.com Cc: Bill Wendling morbo@google.com Cc: Justin Stitt justinstitt@google.com Cc: Peter Zijlstra peterz@infradead.org Cc: Marco Elver elver@google.com Cc: Przemek Kitszel przemyslaw.kitszel@intel.com Cc: Andrew Morton akpm@linux-foundation.org Cc: Masahiro Yamada masahiroy@kernel.org Cc: Christophe Leroy christophe.leroy@csgroup.eu Cc: Johannes Weiner hannes@cmpxchg.org Cc: llvm@lists.linux.dev
init/Kconfig | 11 +++++++++++ Makefile | 4 ++++ include/linux/compiler_types.h | 21 ++++++++++++++++++++- include/uapi/linux/stddef.h | 4 ++++ 4 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/init/Kconfig b/init/Kconfig index cab3ad28ca49..54691b086bc6 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -139,6 +139,17 @@ config CC_HAS_COUNTED_BY # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896 default y if CC_IS_GCC && GCC_VERSION >= 150100
+config CC_HAS_COUNTED_BY_PTR_BARE
def_bool $(success,echo 'struct foo { int *ptr __attribute__((__counted_by__(count))); int count; };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)+config CC_HAS_COUNTED_BY_PTR_EXP
def_bool $(success,echo 'struct foo { int *ptr __attribute__((__counted_by__(count))); int count; };' | $(CC) $(CLANG_FLAGS) -fexperimental-late-parse-attributes -x c - -c -o /dev/null -Werror)
depends on !CC_HAS_COUNTED_BY_PTR_BARE
Do these still require an unreleased Clang version? Otherwise a version check will be faster.
+config CC_HAS_COUNTED_BY_PTR
def_bool y
depends on CC_HAS_COUNTED_BY_PTR_BARE || CC_HAS_COUNTED_BY_PTR_EXPconfig CC_HAS_MULTIDIMENSIONAL_NONSTRING def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)
diff --git a/Makefile b/Makefile index d14824792227..1b297dcbb0df 100644 --- a/Makefile +++ b/Makefile @@ -933,6 +933,10 @@ KBUILD_CFLAGS += $(CC_AUTO_VAR_INIT_ZERO_ENABLER) endif endif
+ifdef CONFIG_CC_HAS_COUNTED_BY_PTR_EXP +KBUILD_CFLAGS += -fexperimental-late-parse-attributes +endif
# Explicitly clear padding bits during variable initialization KBUILD_CFLAGS += $(call cc-option,-fzero-init-padding-bits=all)
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 59288a2c1ad2..f197ea03b593 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -353,11 +353,14 @@ struct ftrace_likely_data { #endif
/*
- Runtime track number of flexible array member elements for use by
- CONFIG_FORTIFY_SOURCE and CONFIG_UBSAN_BOUNDS.
- Optional: only supported since gcc >= 15
- Optional: only supported since clang >= 18
- gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896
- clang: https://clang.llvm.org/docs/AttributeReference.html#counted-by-counted-by-or...
- __bdos on clang < 19.1.2 can erroneously return 0:
- https://github.com/llvm/llvm-project/pull/110497
@@ -371,6 +374,22 @@ struct ftrace_likely_data { # define __counted_by(member) #endif
+/*
- Runtime track number of objects pointed to by a pointer member for
- use by CONFIG_FORTIFY_SOURCE and CONFIG_UBSAN_BOUNDS.
- Optional: only supported since gcc >= 16
- Optional: only supported since clang >= 20
- clang: ...
- */
+#ifdef CONFIG_CC_HAS_COUNTED_BY_PTR +# define __counted_by_ptr(member) __attribute__((__counted_by__(member))) +#else +# define __counted_by_ptr(member) +#endif
/*
- Optional: only supported since gcc >= 15
- Optional: not supported by Clang
diff --git a/include/uapi/linux/stddef.h b/include/uapi/linux/stddef.h index 9a28f7d9a334..111b097ec00b 100644 --- a/include/uapi/linux/stddef.h +++ b/include/uapi/linux/stddef.h @@ -72,6 +72,10 @@ #define __counted_by_be(m) #endif
+#ifndef __counted_by_ptr +#define __counted_by_ptr(m) +#endif
#ifdef __KERNEL__ #define __kernel_nonstring __nonstring
#else
2.34.1
 
            On Mon, Oct 20, 2025 at 3:01 PM Kees Cook kees@kernel.org wrote:
Introduce __counted_by_ptr(), which works like __counted_by(), but for pointer struct members:
struct foo { int a, b, c; char *buffer __counted_by_ptr(bytes); short nr_bars; struct bar *bars __counted_by_ptr(nr_bars); size_t bytes; };
Since "counted_by" can only be applied to pointer members in very recent compiler versions, its application ends up needing to be distinct from flexible array "counted_by" annotations, hence a separate macro.
Unfortunately, this annotation cannot be used for "void *" members (since such a member is considered a pointer to an incomplete type, and neither Clang nor GCC developers could be convinced otherwise[1], even in the face of the GNU extension that "void *" has size "1 byte" for pointer arithmetic). For "void *" members, we must use the coming "sized_by" attribute.
I'm pretty sure that "sized_by" is available in Clang right now.
-bw
Link: https://gcc.gnu.org/pipermail/gcc-patches/2025-May/683136.html [1] Signed-off-by: Kees Cook kees@kernel.org
Cc: Miguel Ojeda ojeda@kernel.org Cc: Nathan Chancellor nathan@kernel.org Cc: Nick Desaulniers nick.desaulniers+lkml@gmail.com Cc: Bill Wendling morbo@google.com Cc: Justin Stitt justinstitt@google.com Cc: Peter Zijlstra peterz@infradead.org Cc: Marco Elver elver@google.com Cc: Przemek Kitszel przemyslaw.kitszel@intel.com Cc: Andrew Morton akpm@linux-foundation.org Cc: Masahiro Yamada masahiroy@kernel.org Cc: Christophe Leroy christophe.leroy@csgroup.eu Cc: Johannes Weiner hannes@cmpxchg.org Cc: llvm@lists.linux.dev
init/Kconfig | 11 +++++++++++ Makefile | 4 ++++ include/linux/compiler_types.h | 21 ++++++++++++++++++++- include/uapi/linux/stddef.h | 4 ++++ 4 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/init/Kconfig b/init/Kconfig index cab3ad28ca49..54691b086bc6 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -139,6 +139,17 @@ config CC_HAS_COUNTED_BY # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896 default y if CC_IS_GCC && GCC_VERSION >= 150100
+config CC_HAS_COUNTED_BY_PTR_BARE
def_bool $(success,echo 'struct foo { int *ptr __attribute__((__counted_by__(count))); int count; };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)+config CC_HAS_COUNTED_BY_PTR_EXP
def_bool $(success,echo 'struct foo { int *ptr __attribute__((__counted_by__(count))); int count; };' | $(CC) $(CLANG_FLAGS) -fexperimental-late-parse-attributes -x c - -c -o /dev/null -Werror)
depends on !CC_HAS_COUNTED_BY_PTR_BARE+config CC_HAS_COUNTED_BY_PTR
def_bool y
depends on CC_HAS_COUNTED_BY_PTR_BARE || CC_HAS_COUNTED_BY_PTR_EXPconfig CC_HAS_MULTIDIMENSIONAL_NONSTRING def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)
diff --git a/Makefile b/Makefile index d14824792227..1b297dcbb0df 100644 --- a/Makefile +++ b/Makefile @@ -933,6 +933,10 @@ KBUILD_CFLAGS += $(CC_AUTO_VAR_INIT_ZERO_ENABLER) endif endif
+ifdef CONFIG_CC_HAS_COUNTED_BY_PTR_EXP +KBUILD_CFLAGS += -fexperimental-late-parse-attributes +endif
# Explicitly clear padding bits during variable initialization KBUILD_CFLAGS += $(call cc-option,-fzero-init-padding-bits=all)
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 59288a2c1ad2..f197ea03b593 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -353,11 +353,14 @@ struct ftrace_likely_data { #endif
/*
- Runtime track number of flexible array member elements for use by
- CONFIG_FORTIFY_SOURCE and CONFIG_UBSAN_BOUNDS.
- Optional: only supported since gcc >= 15
- Optional: only supported since clang >= 18
- gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108896
- clang: https://clang.llvm.org/docs/AttributeReference.html#counted-by-counted-by-or...
- __bdos on clang < 19.1.2 can erroneously return 0:
- https://github.com/llvm/llvm-project/pull/110497
@@ -371,6 +374,22 @@ struct ftrace_likely_data { # define __counted_by(member) #endif
+/*
- Runtime track number of objects pointed to by a pointer member for
- use by CONFIG_FORTIFY_SOURCE and CONFIG_UBSAN_BOUNDS.
- Optional: only supported since gcc >= 16
- Optional: only supported since clang >= 20
- clang: ...
- */
+#ifdef CONFIG_CC_HAS_COUNTED_BY_PTR +# define __counted_by_ptr(member) __attribute__((__counted_by__(member))) +#else +# define __counted_by_ptr(member) +#endif
/*
- Optional: only supported since gcc >= 15
- Optional: not supported by Clang
diff --git a/include/uapi/linux/stddef.h b/include/uapi/linux/stddef.h index 9a28f7d9a334..111b097ec00b 100644 --- a/include/uapi/linux/stddef.h +++ b/include/uapi/linux/stddef.h @@ -72,6 +72,10 @@ #define __counted_by_be(m) #endif
+#ifndef __counted_by_ptr +#define __counted_by_ptr(m) +#endif
#ifdef __KERNEL__ #define __kernel_nonstring __nonstring
#else
2.34.1
 
            On Mon, Oct 20, 2025 at 03:53:55PM -0700, Bill Wendling wrote:
On Mon, Oct 20, 2025 at 3:01 PM Kees Cook kees@kernel.org wrote:
Introduce __counted_by_ptr(), which works like __counted_by(), but for pointer struct members:
struct foo { int a, b, c; char *buffer __counted_by_ptr(bytes); short nr_bars; struct bar *bars __counted_by_ptr(nr_bars); size_t bytes; };
Since "counted_by" can only be applied to pointer members in very recent compiler versions, its application ends up needing to be distinct from flexible array "counted_by" annotations, hence a separate macro.
Unfortunately, this annotation cannot be used for "void *" members (since such a member is considered a pointer to an incomplete type, and neither Clang nor GCC developers could be convinced otherwise[1], even in the face of the GNU extension that "void *" has size "1 byte" for pointer arithmetic). For "void *" members, we must use the coming "sized_by" attribute.
I'm pretty sure that "sized_by" is available in Clang right now.
My clang-22 build seems to accept it no problem indeed -- although I've not verified it actually does anything with it.
 
            On Mon, Oct 20, 2025 at 03:01:15PM -0700, Kees Cook wrote:
Introduce __counted_by_ptr(), which works like __counted_by(), but for pointer struct members:
struct foo { int a, b, c; char *buffer __counted_by_ptr(bytes); short nr_bars; struct bar *bars __counted_by_ptr(nr_bars); size_t bytes; };
Since "counted_by" can only be applied to pointer members in very recent compiler versions, its application ends up needing to be distinct from flexible array "counted_by" annotations, hence a separate macro.
Unfortunately, this annotation cannot be used for "void *" members (since such a member is considered a pointer to an incomplete type, and neither Clang nor GCC developers could be convinced otherwise[1], even in the face of the GNU extension that "void *" has size "1 byte" for pointer arithmetic). For "void *" members, we must use the coming "sized_by" attribute.
So why do we need both __counted_by_ptr() and this __sized_by(), won't one be good enough?
Also, given the existing __counted_by() is really only usable with
=19.1.3 and we're now at 22-ish, do we really need two of these?
That is, I'm really hating the idea we need 3 different annotations for what is effectively the same thing and feel we should try *really* hard to make it 1.
 
            On Tue, Oct 21, 2025 at 11:54:47AM +0200, Peter Zijlstra wrote:
On Mon, Oct 20, 2025 at 03:01:15PM -0700, Kees Cook wrote:
Introduce __counted_by_ptr(), which works like __counted_by(), but for pointer struct members:
struct foo { int a, b, c; char *buffer __counted_by_ptr(bytes); short nr_bars; struct bar *bars __counted_by_ptr(nr_bars); size_t bytes; };
Since "counted_by" can only be applied to pointer members in very recent compiler versions, its application ends up needing to be distinct from flexible array "counted_by" annotations, hence a separate macro.
Unfortunately, this annotation cannot be used for "void *" members (since such a member is considered a pointer to an incomplete type, and neither Clang nor GCC developers could be convinced otherwise[1], even in the face of the GNU extension that "void *" has size "1 byte" for pointer arithmetic). For "void *" members, we must use the coming "sized_by" attribute.
So why do we need both __counted_by_ptr() and this __sized_by(), won't one be good enough?
I remain extraordinarily frustrated that counted_by can't be used with "void *". I hit a brick wall on this, though, and don't know how to convince either GCC or Clang devs to fix it. It's so obviously correct to me: "void *" uses a 1 byte iterator for arithmetic... so asking how big a given allocation is should be byte sized!
Let me take another stab at it...
Also, given the existing __counted_by() is really only usable with
=19.1.3 and we're now at 22-ish, do we really need two of these?
That is, I'm really hating the idea we need 3 different annotations for what is effectively the same thing and feel we should try *really* hard to make it 1.
As for avoiding __counted_by_ptr(), we could just raise the minimum Clang and GCC versions to require this, but that means dropping existing coverage (e.g GCC 15 supports only flexible array counted_by).
Maybe we could do a global __counted_by_ptr -> __counted_by replacement once GCC 16 is released?
 
            On Tue, Oct 21, 2025 at 12:24:05PM -0700, Kees Cook wrote:
On Tue, Oct 21, 2025 at 11:54:47AM +0200, Peter Zijlstra wrote:
So why do we need both __counted_by_ptr() and this __sized_by(), won't one be good enough?
I remain extraordinarily frustrated that counted_by can't be used with "void *". I hit a brick wall on this, though, and don't know how to convince either GCC or Clang devs to fix it. It's so obviously correct to me: "void *" uses a 1 byte iterator for arithmetic... so asking how big a given allocation is should be byte sized!
Right, at least for gnu11 language variants this really should work. I mean, disallow the usage for c11 if you're pedantic but for crying out loud, have the GNU extensions be consistent and all that.
Feel free to use my feedback if it would help.
Let me take another stab at it...
Thanks!
As for avoiding __counted_by_ptr(), we could just raise the minimum Clang and GCC versions to require this, but that means dropping existing coverage (e.g GCC 15 supports only flexible array counted_by).
Maybe we could do a global __counted_by_ptr -> __counted_by replacement once GCC 16 is released?
That sounds like a plan! :-)
 
            On Tue, Oct 21, 2025 at 12:24:05PM -0700, Kees Cook wrote:
On Tue, Oct 21, 2025 at 11:54:47AM +0200, Peter Zijlstra wrote:
[...] Unfortunately, this annotation cannot be used for "void *" members (since such a member is considered a pointer to an incomplete type, and neither Clang nor GCC developers could be convinced otherwise[1], even in the face of the GNU extension that "void *" has size "1 byte" for pointer arithmetic). For "void *" members, we must use the coming "sized_by" attribute.
So why do we need both __counted_by_ptr() and this __sized_by(), won't one be good enough?
[...] Let me take another stab at it...
It seems this will be acceptable as long as it is gated by GNU extensions. GCC patch in progress. Clang PR here: https://github.com/llvm/llvm-project/pull/163698
 
            On Wed, Oct 22, 2025 at 05:47:43PM -0700, Kees Cook wrote:
On Tue, Oct 21, 2025 at 12:24:05PM -0700, Kees Cook wrote:
On Tue, Oct 21, 2025 at 11:54:47AM +0200, Peter Zijlstra wrote:
[...] Unfortunately, this annotation cannot be used for "void *" members (since such a member is considered a pointer to an incomplete type, and neither Clang nor GCC developers could be convinced otherwise[1], even in the face of the GNU extension that "void *" has size "1 byte" for pointer arithmetic). For "void *" members, we must use the coming "sized_by" attribute.
So why do we need both __counted_by_ptr() and this __sized_by(), won't one be good enough?
[...] Let me take another stab at it...
It seems this will be acceptable as long as it is gated by GNU extensions.
Excellent!
GCC patch in progress. Clang PR here: https://github.com/llvm/llvm-project/pull/163698
I think you've got your link mixed up, this appears to be arm-kcfi (also good ofc). Either that, or I need copy/paste lessons.
This one?
 
            On October 23, 2025 1:01:23 AM PDT, Peter Zijlstra peterz@infradead.org wrote:
On Wed, Oct 22, 2025 at 05:47:43PM -0700, Kees Cook wrote:
On Tue, Oct 21, 2025 at 12:24:05PM -0700, Kees Cook wrote:
On Tue, Oct 21, 2025 at 11:54:47AM +0200, Peter Zijlstra wrote:
[...] Unfortunately, this annotation cannot be used for "void *" members (since such a member is considered a pointer to an incomplete type, and neither Clang nor GCC developers could be convinced otherwise[1], even in the face of the GNU extension that "void *" has size "1 byte" for pointer arithmetic). For "void *" members, we must use the coming "sized_by" attribute.
So why do we need both __counted_by_ptr() and this __sized_by(), won't one be good enough?
[...] Let me take another stab at it...
It seems this will be acceptable as long as it is gated by GNU extensions.
Excellent!
GCC patch in progress. Clang PR here: https://github.com/llvm/llvm-project/pull/163698
I think you've got your link mixed up, this appears to be arm-kcfi (also good ofc). Either that, or I need copy/paste lessons.
This one?
Whoops, yes, that's the one! Seems I'm the one needing those lessons. ;)
 
            Provide run-time validation of the __counted_by_ptr() annotation via newly added PTR_BOUNDS LKDTM test.
Signed-off-by: Kees Cook kees@kernel.org --- Cc: Arnd Bergmann arnd@arndb.de Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/misc/lkdtm/bugs.c | 90 ++++++++++++++++++++++--- tools/testing/selftests/lkdtm/tests.txt | 2 + 2 files changed, 84 insertions(+), 8 deletions(-)
diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index 376047beea3d..a069a7d686fc 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -414,32 +414,32 @@ static void lkdtm_ARRAY_BOUNDS(void) pr_expected_config(CONFIG_UBSAN_BOUNDS); }
-struct lkdtm_annotated { +struct lkdtm_cb_fam { unsigned long flags; int count; int array[] __counted_by(count); };
-static volatile int fam_count = 4; +static volatile int element_count = 4;
static void lkdtm_FAM_BOUNDS(void) { - struct lkdtm_annotated *inst; + struct lkdtm_cb_fam *inst;
- inst = kzalloc(struct_size(inst, array, fam_count + 1), GFP_KERNEL); + inst = kzalloc(struct_size(inst, array, element_count + 1), GFP_KERNEL); if (!inst) { pr_err("FAIL: could not allocate test struct!\n"); return; }
- inst->count = fam_count; + inst->count = element_count; pr_info("Array access within bounds ...\n"); - inst->array[1] = fam_count; + inst->array[1] = element_count; ignored = inst->array[1];
pr_info("Array access beyond bounds ...\n"); - inst->array[fam_count] = fam_count; - ignored = inst->array[fam_count]; + inst->array[element_count] = element_count; + ignored = inst->array[element_count];
kfree(inst);
@@ -454,6 +454,79 @@ static void lkdtm_FAM_BOUNDS(void) pr_expected_config(CONFIG_UBSAN_BOUNDS); }
+struct lkdtm_extra { + short a, b; + u16 sixteen; + u32 bigger; + u64 biggest; +}; + +struct lkdtm_cb_ptr { + int a, b, c; + int nr_extra; + char *buf __counted_by_ptr(len); + size_t len; + struct lkdtm_extra *extra __counted_by_ptr(nr_extra); +}; + +static noinline void check_ptr_len(struct lkdtm_cb_ptr *p, size_t len) +{ + if (__member_size(p->buf) != len) + pr_err("FAIL: could not determine size of inst->buf: %zu\n", + __member_size(p->buf)); + else + pr_info("good: inst->buf length is %zu\n", len); +} + +static void lkdtm_PTR_BOUNDS(void) +{ + struct lkdtm_cb_ptr *inst; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) { + pr_err("FAIL: could not allocate struct lkdtm_cb_ptr!\n"); + return; + } + + inst->buf = kzalloc(element_count, GFP_KERNEL); + if (!inst->buf) { + pr_err("FAIL: could not allocate inst->buf!\n"); + return; + } + inst->len = element_count; + + /* Double element_count */ + inst->extra = kcalloc(element_count * 2, sizeof(*inst->extra), GFP_KERNEL); + inst->nr_extra = element_count * 2; + + pr_info("Pointer access within bounds ...\n"); + check_ptr_len(inst, 4); + /* All 4 bytes */ + inst->buf[0] = 'A'; + inst->buf[1] = 'B'; + inst->buf[2] = 'C'; + inst->buf[3] = 'D'; + /* Halfway into the array */ + inst->extra[element_count].biggest = 0x1000; + + pr_info("Pointer access beyond bounds ...\n"); + ignored = inst->extra[inst->nr_extra].b; + + kfree(inst->extra); + kfree(inst->buf); + kfree(inst); + + pr_err("FAIL: survived access of invalid pointer member offset!\n"); + + if (!IS_ENABLED(CONFIG_CC_HAS_COUNTED_BY_PTR)) + pr_warn("This is expected since this %s was built with a compiler that does not support __counted_by_ptr\n", + lkdtm_kernel_info); + else if (IS_ENABLED(CONFIG_UBSAN_BOUNDS)) + pr_expected_config(CONFIG_UBSAN_TRAP); + else + pr_expected_config(CONFIG_UBSAN_BOUNDS); +} + static void lkdtm_CORRUPT_LIST_ADD(void) { /* @@ -716,6 +789,7 @@ static struct crashtype crashtypes[] = { CRASHTYPE(OVERFLOW_UNSIGNED), CRASHTYPE(ARRAY_BOUNDS), CRASHTYPE(FAM_BOUNDS), + CRASHTYPE(PTR_BOUNDS), CRASHTYPE(CORRUPT_LIST_ADD), CRASHTYPE(CORRUPT_LIST_DEL), CRASHTYPE(STACK_GUARD_PAGE_LEADING), diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index cff124c1eddd..204d4a669632 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -9,6 +9,8 @@ EXCEPTION #CORRUPT_STACK Crashes entire system on success #CORRUPT_STACK_STRONG Crashes entire system on success ARRAY_BOUNDS call trace:|UBSAN: array-index-out-of-bounds +FAM_BOUNDS call trace:|UBSAN: array-index-out-of-bounds +PTR_BOUNDS call trace:|UBSAN: array-index-out-of-bounds CORRUPT_LIST_ADD list_add corruption CORRUPT_LIST_DEL list_del corruption STACK_GUARD_PAGE_LEADING
 
            Use the __counted_by annotation now available for struct pointer members, __counted_by_ptr(). Move assignments to immediately after allocation.
Signed-off-by: Kees Cook kees@kernel.org --- Cc: Al Viro viro@zeniv.linux.org.uk Cc: Christian Brauner brauner@kernel.org Cc: Jan Kara jack@suse.cz Cc: linux-fsdevel@vger.kernel.org --- fs/coredump.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/fs/coredump.c b/fs/coredump.c index 5c1c381ee380..876f1cdb756f 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -92,7 +92,7 @@ enum coredump_type_t { };
struct core_name { - char *corename; + char *corename __counted_by_ptr(size); int used, size; unsigned int core_pipe_limit; bool core_dumped; @@ -106,15 +106,15 @@ static int expand_corename(struct core_name *cn, int size)
size = kmalloc_size_roundup(size); corename = krealloc(cn->corename, size, GFP_KERNEL); - if (!corename) return -ENOMEM;
+ cn->corename = corename; + cn->size = size; + if (size > core_name_size) /* racy but harmless */ core_name_size = size;
- cn->size = size; - cn->corename = corename; return 0; }
linux-kselftest-mirror@lists.linaro.org



