summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-10 08:54:13 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-10 08:54:13 -0800
commit85f24b0ace9aa79142f632fc3ccc730a8d2a4a28 (patch)
tree29cd3fd0b191f3de84af9f5700823cc135f0b824
parentbffce9b427b37e2f54416a695ec5d7f030ba610f (diff)
parent44dd7cfbd1db5199cf7afe03158a578a64b55800 (diff)
Merge tag 'hardening-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull hardening updates from Kees Cook: "Mostly small cleanups and various scattered annotations and flex array warning fixes that we reviewed by unlanded in other trees. Introduces new annotation for expanding counted_by to pointer members, now that compiler behavior between GCC and Clang has been normalized. - Various missed __counted_by annotations (Thorsten Blum) - Various missed -Wflex-array-member-not-at-end fixes (Gustavo A. R. Silva) - Avoid leftover tempfiles for interrupted compile-time FORTIFY tests (Nicolas Schier) - Remove non-existant CONFIG_UBSAN_REPORT_FULL from docs (Stefan Wiehler) - fortify: Use C arithmetic not FIELD_xxx() in FORTIFY_REASON defines (David Laight) - Add __counted_by_ptr attribute, tests, and first user (Bill Wendling, Kees Cook) - Update MAINTAINERS file to make hardening section not include pstore" * tag 'hardening-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: MAINTAINERS: pstore: Remove L: entry nfp: tls: Avoid -Wflex-array-member-not-at-end warnings carl9170: Avoid -Wflex-array-member-not-at-end warning coredump: Use __counted_by_ptr for struct core_name::corename lkdtm/bugs: Add __counted_by_ptr() test PTR_BOUNDS compiler_types.h: Attributes: Add __counted_by_ptr macro fortify: Cleanup temp file also on non-successful exit fortify: Rename temporary file to match ignore pattern fortify: Use C arithmetic not FIELD_xxx() in FORTIFY_REASON defines ecryptfs: Annotate struct ecryptfs_message with __counted_by fs/xattr: Annotate struct simple_xattr with __counted_by crypto: af_alg - Annotate struct af_alg_iv with __counted_by Kconfig.ubsan: Remove CONFIG_UBSAN_REPORT_FULL from documentation drm/nouveau: fifo: Avoid -Wflex-array-member-not-at-end warning
-rw-r--r--MAINTAINERS1
-rw-r--r--Makefile6
-rw-r--r--drivers/gpu/drm/nouveau/nvif/fifo.c5
-rw-r--r--drivers/misc/lkdtm/bugs.c90
-rw-r--r--drivers/net/ethernet/netronome/nfp/crypto/fw.h24
-rw-r--r--drivers/net/ethernet/netronome/nfp/crypto/tls.c8
-rw-r--r--drivers/net/wireless/ath/carl9170/carl9170.h12
-rw-r--r--fs/coredump.c8
-rw-r--r--fs/ecryptfs/ecryptfs_kernel.h2
-rw-r--r--include/linux/compiler_types.h18
-rw-r--r--include/linux/fortify-string.h8
-rw-r--r--include/linux/xattr.h2
-rw-r--r--include/uapi/linux/if_alg.h2
-rw-r--r--include/uapi/linux/stddef.h4
-rw-r--r--init/Kconfig7
-rw-r--r--lib/Kconfig.ubsan2
-rw-r--r--lib/test_fortify/test_fortify.sh4
-rw-r--r--tools/testing/selftests/lkdtm/tests.txt2
18 files changed, 161 insertions, 44 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index fcf39de76838..33bd9742398a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20996,7 +20996,6 @@ PSTORE FILESYSTEM
M: Kees Cook <kees@kernel.org>
R: Tony Luck <tony.luck@intel.com>
R: Guilherme G. Piccoli <gpiccoli@igalia.com>
-L: linux-hardening@vger.kernel.org
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/pstore
F: Documentation/admin-guide/pstore-blk.rst
diff --git a/Makefile b/Makefile
index 1b4b862230c6..6b283d201799 100644
--- a/Makefile
+++ b/Makefile
@@ -952,6 +952,12 @@ KBUILD_CFLAGS += $(CC_AUTO_VAR_INIT_ZERO_ENABLER)
endif
endif
+ifdef CONFIG_CC_IS_CLANG
+ifdef CONFIG_CC_HAS_COUNTED_BY_PTR
+KBUILD_CFLAGS += -fexperimental-late-parse-attributes
+endif
+endif
+
# Explicitly clear padding bits during variable initialization
KBUILD_CFLAGS += $(call cc-option,-fzero-init-padding-bits=all)
diff --git a/drivers/gpu/drm/nouveau/nvif/fifo.c b/drivers/gpu/drm/nouveau/nvif/fifo.c
index a463289962b2..b0ab80995d98 100644
--- a/drivers/gpu/drm/nouveau/nvif/fifo.c
+++ b/drivers/gpu/drm/nouveau/nvif/fifo.c
@@ -25,13 +25,12 @@ static int
nvif_fifo_runlists(struct nvif_device *device)
{
struct nvif_object *object = &device->object;
- struct {
- struct nv_device_info_v1 m;
+ TRAILING_OVERLAP(struct nv_device_info_v1, m, data,
struct {
struct nv_device_info_v1_data runlists;
struct nv_device_info_v1_data runlist[64];
} v;
- } *a;
+ ) *a;
int ret, i;
if (device->runlist)
diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c
index 502059078b45..b2aee36b956d 100644
--- a/drivers/misc/lkdtm/bugs.c
+++ b/drivers/misc/lkdtm/bugs.c
@@ -465,32 +465,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);
@@ -505,6 +505,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)
{
/*
@@ -769,6 +842,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/drivers/net/ethernet/netronome/nfp/crypto/fw.h b/drivers/net/ethernet/netronome/nfp/crypto/fw.h
index dcb67c2b5e5e..1e869599febb 100644
--- a/drivers/net/ethernet/netronome/nfp/crypto/fw.h
+++ b/drivers/net/ethernet/netronome/nfp/crypto/fw.h
@@ -32,16 +32,22 @@ struct nfp_crypto_req_reset {
#define NFP_NET_TLS_VLAN_UNUSED 4095
struct nfp_crypto_req_add_front {
- struct nfp_ccm_hdr hdr;
- __be32 ep_id;
- u8 resv[3];
- u8 opcode;
- u8 key_len;
- __be16 ipver_vlan __packed;
- u8 l4_proto;
+ /* New members MUST be added within the struct_group() macro below. */
+ struct_group_tagged(nfp_crypto_req_add_front_hdr, __hdr,
+ struct nfp_ccm_hdr hdr;
+ __be32 ep_id;
+ u8 resv[3];
+ u8 opcode;
+ u8 key_len;
+ __be16 ipver_vlan __packed;
+ u8 l4_proto;
+ );
#define NFP_NET_TLS_NON_ADDR_KEY_LEN 8
u8 l3_addrs[];
};
+static_assert(offsetof(struct nfp_crypto_req_add_front, l3_addrs) ==
+ sizeof(struct nfp_crypto_req_add_front_hdr),
+ "struct member likely outside of struct_group_tagged()");
struct nfp_crypto_req_add_back {
__be16 src_port;
@@ -55,14 +61,14 @@ struct nfp_crypto_req_add_back {
};
struct nfp_crypto_req_add_v4 {
- struct nfp_crypto_req_add_front front;
+ struct nfp_crypto_req_add_front_hdr front;
__be32 src_ip;
__be32 dst_ip;
struct nfp_crypto_req_add_back back;
};
struct nfp_crypto_req_add_v6 {
- struct nfp_crypto_req_add_front front;
+ struct nfp_crypto_req_add_front_hdr front;
__be32 src_ip[4];
__be32 dst_ip[4];
struct nfp_crypto_req_add_back back;
diff --git a/drivers/net/ethernet/netronome/nfp/crypto/tls.c b/drivers/net/ethernet/netronome/nfp/crypto/tls.c
index f252ecdcd2cd..9983d7aa2b9c 100644
--- a/drivers/net/ethernet/netronome/nfp/crypto/tls.c
+++ b/drivers/net/ethernet/netronome/nfp/crypto/tls.c
@@ -180,7 +180,9 @@ nfp_net_tls_set_ipv4(struct nfp_net *nn, struct nfp_crypto_req_add_v4 *req,
req->front.key_len += sizeof(__be32) * 2;
if (direction == TLS_OFFLOAD_CTX_DIR_TX) {
- nfp_net_tls_assign_conn_id(nn, &req->front);
+ nfp_net_tls_assign_conn_id(nn,
+ container_of(&req->front,
+ struct nfp_crypto_req_add_front, __hdr));
} else {
req->src_ip = inet->inet_daddr;
req->dst_ip = inet->inet_saddr;
@@ -199,7 +201,9 @@ nfp_net_tls_set_ipv6(struct nfp_net *nn, struct nfp_crypto_req_add_v6 *req,
req->front.key_len += sizeof(struct in6_addr) * 2;
if (direction == TLS_OFFLOAD_CTX_DIR_TX) {
- nfp_net_tls_assign_conn_id(nn, &req->front);
+ nfp_net_tls_assign_conn_id(nn,
+ container_of(&req->front,
+ struct nfp_crypto_req_add_front, __hdr));
} else {
memcpy(req->src_ip, &sk->sk_v6_daddr, sizeof(req->src_ip));
memcpy(req->dst_ip, &np->saddr, sizeof(req->dst_ip));
diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
index ba29b4aebe9f..b13685e22a0d 100644
--- a/drivers/net/wireless/ath/carl9170/carl9170.h
+++ b/drivers/net/wireless/ath/carl9170/carl9170.h
@@ -375,11 +375,6 @@ struct ar9170 {
u8 *readbuf;
spinlock_t cmd_lock;
struct completion cmd_wait;
- union {
- __le32 cmd_buf[PAYLOAD_MAX + 1];
- struct carl9170_cmd cmd;
- struct carl9170_rsp rsp;
- };
/* statistics */
unsigned int tx_dropped;
@@ -463,6 +458,13 @@ struct ar9170 {
unsigned int cache_idx;
} rng;
#endif /* CONFIG_CARL9170_HWRNG */
+
+ /* Must be last as it ends in a flexible-array member. */
+ union {
+ __le32 cmd_buf[PAYLOAD_MAX + 1];
+ struct carl9170_cmd cmd;
+ struct carl9170_rsp rsp;
+ };
};
enum carl9170_ps_off_override_reasons {
diff --git a/fs/coredump.c b/fs/coredump.c
index d9597610a6ca..4ce7c80b39c8 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;
}
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 62a2ea7f59ed..f58b12be8267 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -359,7 +359,7 @@ struct ecryptfs_message {
/* Inherits from msg_ctx->index */
u32 index;
u32 data_len;
- u8 data[];
+ u8 data[] __counted_by(data_len);
};
struct ecryptfs_msg_ctx {
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index d3318a3c2577..d095beb904ea 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -369,7 +369,7 @@ struct ftrace_likely_data {
* 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-null-sized-by-sized-by-or-null
*
* __bdos on clang < 19.1.2 can erroneously return 0:
* https://github.com/llvm/llvm-project/pull/110497
@@ -384,6 +384,22 @@ struct ftrace_likely_data {
#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 >= 22
+ *
+ * gcc: https://gcc.gnu.org/pipermail/gcc-patches/2025-April/681727.html
+ * clang: https://clang.llvm.org/docs/AttributeReference.html#counted-by-counted-by-or-null-sized-by-sized-by-or-null
+ */
+#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/linux/fortify-string.h b/include/linux/fortify-string.h
index b3b53f8c1b28..171982e53c9a 100644
--- a/include/linux/fortify-string.h
+++ b/include/linux/fortify-string.h
@@ -2,7 +2,6 @@
#ifndef _LINUX_FORTIFY_STRING_H_
#define _LINUX_FORTIFY_STRING_H_
-#include <linux/bitfield.h>
#include <linux/bug.h>
#include <linux/const.h>
#include <linux/limits.h>
@@ -10,10 +9,9 @@
#define __FORTIFY_INLINE extern __always_inline __gnu_inline __overloadable
#define __RENAME(x) __asm__(#x)
-#define FORTIFY_REASON_DIR(r) FIELD_GET(BIT(0), r)
-#define FORTIFY_REASON_FUNC(r) FIELD_GET(GENMASK(7, 1), r)
-#define FORTIFY_REASON(func, write) (FIELD_PREP(BIT(0), write) | \
- FIELD_PREP(GENMASK(7, 1), func))
+#define FORTIFY_REASON_DIR(r) ((r) & 1)
+#define FORTIFY_REASON_FUNC(r) ((r) >> 1)
+#define FORTIFY_REASON(func, write) ((func) << 1 | (write))
/* Overridden by KUnit tests. */
#ifndef fortify_panic
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index 64e9afe7d647..296b5ee5c979 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -114,7 +114,7 @@ struct simple_xattr {
struct rb_node rb_node;
char *name;
size_t size;
- char value[];
+ char value[] __counted_by(size);
};
void simple_xattrs_init(struct simple_xattrs *xattrs);
diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h
index b35871cbeed7..4f51e198ac2e 100644
--- a/include/uapi/linux/if_alg.h
+++ b/include/uapi/linux/if_alg.h
@@ -42,7 +42,7 @@ struct sockaddr_alg_new {
struct af_alg_iv {
__u32 ivlen;
- __u8 iv[];
+ __u8 iv[] __counted_by(ivlen);
};
/* Socket options */
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
diff --git a/init/Kconfig b/init/Kconfig
index fcc0785dc28c..99941f34abc6 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -143,6 +143,13 @@ 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
+ bool
+ # supported since clang 22
+ default y if CC_IS_CLANG && CLANG_VERSION >= 220000
+ # supported since gcc 16.0.0
+ default y if CC_IS_GCC && GCC_VERSION >= 160000
+
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/lib/Kconfig.ubsan b/lib/Kconfig.ubsan
index 744121178815..1ecaae7064d2 100644
--- a/lib/Kconfig.ubsan
+++ b/lib/Kconfig.ubsan
@@ -104,7 +104,7 @@ config UBSAN_DIV_ZERO
This option enables -fsanitize=integer-divide-by-zero which checks
for integer division by zero. This is effectively redundant with the
kernel's existing exception handling, though it can provide greater
- debugging information under CONFIG_UBSAN_REPORT_FULL.
+ debugging information.
config UBSAN_UNREACHABLE
bool "Perform checking for unreachable code"
diff --git a/lib/test_fortify/test_fortify.sh b/lib/test_fortify/test_fortify.sh
index c2688ab8281d..ad6dd44fa31c 100644
--- a/lib/test_fortify/test_fortify.sh
+++ b/lib/test_fortify/test_fortify.sh
@@ -17,7 +17,7 @@ WANT="__${FILE%%-*}"
# Argument 2: Where to write the build log.
OUT="$1"
shift
-TMP="${OUT}.tmp"
+TMP="${OUT%/*}/.${OUT##*/}.tmp"
# Argument 3: Path to "nm" tool.
NM="$1"
@@ -29,7 +29,7 @@ shift
__cleanup() {
rm -f "$TMP"
}
-trap __cleanup EXIT
+trap __cleanup EXIT HUP INT QUIT TERM
# Function names in warnings are wrapped in backticks under UTF-8 locales.
# Run the commands with LANG=C so that grep output will not change.
diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt
index 67cd53715d93..e62b85b591be 100644
--- a/tools/testing/selftests/lkdtm/tests.txt
+++ b/tools/testing/selftests/lkdtm/tests.txt
@@ -11,6 +11,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