diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-01 16:51:16 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-01 16:51:16 +0100 |
| commit | a8400804d5ba45c7aec07ebafbdf88305b1a301e (patch) | |
| tree | 94e97a657a6011966f8d3de354dcaea183e86d45 | |
| parent | 752643d17b6d6a246b8516232a35113b5df8bbfb (diff) | |
| parent | 760f96b7f54beb8dc6f85d97ac7854fddba86835 (diff) | |
| download | linux-next-a8400804d5ba45c7aec07ebafbdf88305b1a301e.tar.gz linux-next-a8400804d5ba45c7aec07ebafbdf88305b1a301e.zip | |
Merge branch 'for-next/kspp' of https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git
| -rw-r--r-- | arch/um/include/asm/common.lds.S | 1 | ||||
| -rw-r--r-- | drivers/misc/lkdtm/core.c | 16 | ||||
| -rw-r--r-- | fs/signalfd.c | 28 | ||||
| -rw-r--r-- | include/linux/fortify-string.h | 2 | ||||
| -rw-r--r-- | scripts/coccinelle/api/kmalloc_objs.cocci | 161 | ||||
| -rw-r--r-- | scripts/gcc-plugins/randomize_layout_plugin.c | 63 |
6 files changed, 219 insertions, 52 deletions
diff --git a/arch/um/include/asm/common.lds.S b/arch/um/include/asm/common.lds.S index 6585cb93e6eb..7b0c6f8a9d4f 100644 --- a/arch/um/include/asm/common.lds.S +++ b/arch/um/include/asm/common.lds.S @@ -93,6 +93,7 @@ .fini_array : { __fini_array_start = .; *(.fini_array) + *(.fini_array.*) __fini_array_end = .; } diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index ededa32d6744..01bebcb33bd4 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -236,11 +236,11 @@ static ssize_t lkdtm_debugfs_entry(struct file *f, if (count >= PAGE_SIZE) return -EINVAL; - buf = (char *)__get_free_page(GFP_KERNEL); + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; if (copy_from_user(buf, user_buf, count)) { - free_page((unsigned long) buf); + kfree(buf); return -EFAULT; } /* NULL-terminate and remove enter */ @@ -248,7 +248,7 @@ static ssize_t lkdtm_debugfs_entry(struct file *f, strim(buf); crashtype = find_crashtype(buf); - free_page((unsigned long)buf); + kfree(buf); if (!crashtype) return -EINVAL; @@ -271,7 +271,7 @@ static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf, ssize_t out; char *buf; - buf = (char *)__get_free_page(GFP_KERNEL); + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (buf == NULL) return -ENOMEM; @@ -290,7 +290,7 @@ static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf, out = simple_read_from_buffer(user_buf, count, off, buf, n); - free_page((unsigned long) buf); + kfree(buf); return out; } @@ -313,11 +313,11 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf, if (count < 1) return -EINVAL; - buf = (char *)__get_free_page(GFP_KERNEL); + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; if (copy_from_user(buf, user_buf, count)) { - free_page((unsigned long) buf); + kfree(buf); return -EFAULT; } /* NULL-terminate and remove enter */ @@ -325,7 +325,7 @@ static ssize_t direct_entry(struct file *f, const char __user *user_buf, strim(buf); crashtype = find_crashtype(buf); - free_page((unsigned long) buf); + kfree(buf); if (!crashtype) return -EINVAL; diff --git a/fs/signalfd.c b/fs/signalfd.c index dff53745e352..22bc0870a824 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -48,17 +48,30 @@ static int signalfd_release(struct inode *inode, struct file *file) return 0; } +static void refine_sigmask(struct signalfd_ctx *ctx, sigset_t *sigmask) +{ + struct k_sigaction *k = current->sighand->action; + int n; + + *sigmask = ctx->sigmask; + for (n = 1; n <= _NSIG; ++n, ++k) { + if (k->sa.sa_flags & SA_IMMUTABLE) + sigaddset(sigmask, n); + } +} + static __poll_t signalfd_poll(struct file *file, poll_table *wait) { struct signalfd_ctx *ctx = file->private_data; __poll_t events = 0; + sigset_t sigmask; poll_wait(file, ¤t->sighand->signalfd_wqh, wait); spin_lock_irq(¤t->sighand->siglock); - if (next_signal(¤t->pending, &ctx->sigmask) || - next_signal(¤t->signal->shared_pending, - &ctx->sigmask)) + refine_sigmask(ctx, &sigmask); + if (next_signal(¤t->pending, &sigmask) || + next_signal(¤t->signal->shared_pending, &sigmask)) events |= EPOLLIN; spin_unlock_irq(¤t->sighand->siglock); @@ -155,11 +168,13 @@ static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info int nonblock) { enum pid_type type; - ssize_t ret; DECLARE_WAITQUEUE(wait, current); + sigset_t sigmask; + ssize_t ret; spin_lock_irq(¤t->sighand->siglock); - ret = dequeue_signal(&ctx->sigmask, info, &type); + refine_sigmask(ctx, &sigmask); + ret = dequeue_signal(&sigmask, info, &type); switch (ret) { case 0: if (!nonblock) @@ -174,7 +189,7 @@ static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info add_wait_queue(¤t->sighand->signalfd_wqh, &wait); for (;;) { set_current_state(TASK_INTERRUPTIBLE); - ret = dequeue_signal(&ctx->sigmask, info, &type); + ret = dequeue_signal(&sigmask, info, &type); if (ret != 0) break; if (signal_pending(current)) { @@ -184,6 +199,7 @@ static ssize_t signalfd_dequeue(struct signalfd_ctx *ctx, kernel_siginfo_t *info spin_unlock_irq(¤t->sighand->siglock); schedule(); spin_lock_irq(¤t->sighand->siglock); + refine_sigmask(ctx, &sigmask); } spin_unlock_irq(¤t->sighand->siglock); diff --git a/include/linux/fortify-string.h b/include/linux/fortify-string.h index cf841dc71fef..7e7c369e0a6c 100644 --- a/include/linux/fortify-string.h +++ b/include/linux/fortify-string.h @@ -458,10 +458,8 @@ __FORTIFY_INLINE bool fortify_memset_chk(__kernel_size_t size, * __struct_size() vs __member_size() must be captured here to avoid * evaluating argument side-effects further into the macro layers. */ -#ifndef CONFIG_KMSAN #define memset(p, c, s) __fortify_memset_chk(p, c, s, \ __struct_size(p), __member_size(p)) -#endif /* * To make sure the compiler can enforce protection against buffer overflows, diff --git a/scripts/coccinelle/api/kmalloc_objs.cocci b/scripts/coccinelle/api/kmalloc_objs.cocci index e9a415b7b6f4..0a98ddf03eca 100644 --- a/scripts/coccinelle/api/kmalloc_objs.cocci +++ b/scripts/coccinelle/api/kmalloc_objs.cocci @@ -24,27 +24,57 @@ def alloc_array(name): print(f"Unknown transform for {name}", file=sys.stderr) return func -// This excludes anything that is assigning to or from integral types or -// string literals. Everything else gets the sizeof() extracted for the -// kmalloc_obj() type/var argument. sizeof(void *) is also excluded because -// it will need case-by-case double-checking to make sure the right type is +// Allocations sized by a byte-sized type (BYTE_TYPES) or a string literal, +// and allocations assigned to a pointer to a byte-sized type, are byte +// buffers and are left alone. sizeof(void *) is also excluded because it +// will need case-by-case double-checking to make sure the right type is // being assigned. +// +// Allocations sized by a multi-byte integral type (MULTIBYTE_TYPES) are +// converted when they are assigned to a pointer to that same type. +// Otherwise they are left alone: the target may be a pointer to an array +// of that type, such as "s16 (*pairs)[2]", for which the converted +// allocation would have the wrong pointer type. For the same reason, +// arrays of pointers to integral types sized as sizeof(char *) and the +// like are left alone. Allocations of other types assigned to a pointer to +// a multi-byte integral type are left alone too. +// +// Everything else gets the sizeof() extracted for the kmalloc_obj() +// type/var argument. +// +// The first matching alternative below wins, so the exclusions must come +// before the more general conversions. @direct depends on patch && !(file in "tools") && !(file in "samples")@ typedef u8, u16, u32, u64; typedef __u8, __u16, __u32, __u64; typedef uint8_t, uint16_t, uint32_t, uint64_t; +typedef s8, s16, s32, s64; +typedef __s8, __s16, __s32, __s64; +typedef int8_t, int16_t, int32_t, int64_t; typedef uchar, ushort, uint, ulong; typedef __le16, __le32, __le64; typedef __be16, __be32, __be64; typedef wchar_t; -type INTEGRAL = {u8,__u8,uint8_t,char,unsigned char,uchar,wchar_t, - u16,__u16,uint16_t,unsigned short,ushort, - u32,__u32,uint32_t,unsigned int,uint, - u64,__u64,uint64_t,unsigned long,ulong, - __le16,__le32,__le64,__be16,__be32,__be64}; +type BYTE_TYPES = {char,signed char,unsigned char,uchar, + u8,__u8,uint8_t,s8,__s8,int8_t}; +type MULTIBYTE_TYPES = {short,short int,signed short,signed short int, + unsigned short,unsigned short int,ushort, + int,signed,signed int,unsigned,unsigned int,uint, + long,long int,signed long,signed long int, + unsigned long,unsigned long int,ulong, + long long,long long int, + signed long long,signed long long int, + unsigned long long,unsigned long long int, + u16,__u16,uint16_t,s16,__s16,int16_t, + u32,__u32,uint32_t,s32,__s32,int32_t, + u64,__u64,uint64_t,s64,__s64,int64_t, + __le16,__le32,__le64,__be16,__be32,__be64, + wchar_t}; char [] STRING; -INTEGRAL *BYTES; -INTEGRAL **BYTES_PTRS; +BYTE_TYPES *BYTES; +MULTIBYTE_TYPES *MULTIBYTES; +const MULTIBYTE_TYPES *CONST_MULTIBYTES; +MULTIBYTE_TYPES MULTIBYTE; type TYPE; expression VAR; expression GFP; @@ -59,66 +89,133 @@ fresh identifier ALLOC_OBJS = script:python(ALLOC_ARRAY) { alloc_array(ALLOC_ARR @@ ( +// Convert a single object sized by its target: p = kmalloc(sizeof(*p), gfp) - VAR = ALLOC((sizeof(*VAR)), GFP) + VAR = ALLOC_OBJ(*VAR, GFP) | - ALLOC((\(sizeof(STRING)\|sizeof(INTEGRAL)\|sizeof(INTEGRAL *)\)), GFP) +// Exclude byte buffers and integral pointers: kmalloc(sizeof(u8), gfp), +// kmalloc(sizeof("str"), gfp), kmalloc(sizeof(char *), gfp) + ALLOC((\(sizeof(STRING)\|sizeof(BYTE_TYPES)\| + sizeof(BYTE_TYPES *)\|sizeof(MULTIBYTE_TYPES *)\)), GFP) | - BYTES = ALLOC((sizeof(E)), GFP) +// Exclude anything assigned to a byte pointer: +// u8 *buf = kmalloc(sizeof(*hdr), gfp) + BYTES = ALLOC((\(sizeof(E)\|sizeof(TYPE)\)), GFP) | - BYTES = ALLOC((sizeof(TYPE)), GFP) +// Convert a multi-byte type to a pointer to it: +// u32 *p = kmalloc(sizeof(u32), gfp) + \(MULTIBYTES\|CONST_MULTIBYTES\) = +- ALLOC((sizeof(MULTIBYTE_TYPES)), GFP) ++ ALLOC_OBJ(MULTIBYTE_TYPES, GFP) | - BYTES_PTRS = ALLOC((sizeof(E)), GFP) +// Same by expression: u32 *p = kmalloc(sizeof(p[0]), gfp) + \(MULTIBYTES\|CONST_MULTIBYTES\) = +- ALLOC((sizeof(MULTIBYTE)), GFP) ++ ALLOC_OBJ(MULTIBYTE, GFP) | - BYTES_PTRS = ALLOC((sizeof(TYPE)), GFP) +// Exclude other multi-byte sizes, e.g. to pointers to arrays: s16 (*p)[2] = ... + ALLOC((\(sizeof(MULTIBYTE_TYPES)\|sizeof(MULTIBYTE)\)), GFP) | +// Exclude anything else assigned to a multi-byte pointer: +// u32 *p = kmalloc(sizeof(*hdr), gfp) + \(MULTIBYTES\|CONST_MULTIBYTES\) = ALLOC((\(sizeof(E)\|sizeof(TYPE)\)), GFP) +| +// Exclude void pointers, to be checked by hand: kmalloc(sizeof(void *), gfp) ALLOC((sizeof(void *)), GFP) | +// Convert any other expression: p = kmalloc(sizeof(s->item), gfp) - ALLOC((sizeof(E)), GFP) + ALLOC_OBJ(E, GFP) | +// Convert any other type: p = kmalloc(sizeof(struct item), gfp) - ALLOC((sizeof(TYPE)), GFP) + ALLOC_OBJ(TYPE, GFP) | - ALLOC_ARRAY(COUNT, (\(sizeof(STRING)\|sizeof(INTEGRAL)\|sizeof(INTEGRAL *)\)), GFP) -| - BYTES = ALLOC_ARRAY(COUNT, (sizeof(E)), GFP) -| - BYTES = ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP) -| - BYTES_PTRS = ALLOC_ARRAY(COUNT, (sizeof(E)), GFP) +// The same, for arrays allocated as (count, size): +// Exclude byte buffers and integral pointers: kcalloc(n, sizeof(u8), gfp), +// kcalloc(n, sizeof(char *), gfp) + ALLOC_ARRAY(COUNT, (\(sizeof(STRING)\|sizeof(BYTE_TYPES)\| + sizeof(BYTE_TYPES *)\|sizeof(MULTIBYTE_TYPES *)\)), GFP) | - BYTES_PTRS = ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP) +// Exclude arrays assigned to a byte pointer: +// u8 *buf = kcalloc(n, sizeof(*hdr), gfp) + BYTES = ALLOC_ARRAY(COUNT, (\(sizeof(E)\|sizeof(TYPE)\)), GFP) | - ALLOC_ARRAY((\(sizeof(STRING)\|sizeof(INTEGRAL)\|sizeof(INTEGRAL *)\)), COUNT, GFP) +// Convert a multi-byte array to a pointer to it: +// u32 *p = kcalloc(n, sizeof(u32), gfp) + \(MULTIBYTES\|CONST_MULTIBYTES\) = +- ALLOC_ARRAY(COUNT, (sizeof(MULTIBYTE_TYPES)), GFP) ++ ALLOC_OBJS(MULTIBYTE_TYPES, COUNT, GFP) | - BYTES = ALLOC_ARRAY((sizeof(E)), COUNT, GFP) +// Same by expression: u32 *p = kcalloc(n, sizeof(*p), gfp) + \(MULTIBYTES\|CONST_MULTIBYTES\) = +- ALLOC_ARRAY(COUNT, (sizeof(MULTIBYTE)), GFP) ++ ALLOC_OBJS(MULTIBYTE, COUNT, GFP) | - BYTES = ALLOC_ARRAY((sizeof(TYPE)), COUNT, GFP) +// Exclude other multi-byte arrays: +// s16 (*pairs)[2] = kcalloc(n, sizeof(s16), gfp) + ALLOC_ARRAY(COUNT, (\(sizeof(MULTIBYTE_TYPES)\|sizeof(MULTIBYTE)\)), GFP) | - BYTES_PTRS = ALLOC_ARRAY((sizeof(E)), COUNT, GFP) -| - BYTES_PTRS = ALLOC_ARRAY((sizeof(TYPE)), COUNT, GFP) +// Exclude other arrays assigned to a multi-byte pointer: +// u32 *p = kcalloc(n, sizeof(*hdr), gfp) + \(MULTIBYTES\|CONST_MULTIBYTES\) = ALLOC_ARRAY(COUNT, (\(sizeof(E)\|sizeof(TYPE)\)), GFP) | +// Exclude arrays of void pointers: kcalloc(n, sizeof(void *), gfp) ALLOC_ARRAY(COUNT, (sizeof(void *)), GFP) | - ALLOC_ARRAY((sizeof(void *)), COUNT, GFP) -| +// Convert any other expression: p = kcalloc(n, sizeof(*p), gfp) - ALLOC_ARRAY(COUNT, (sizeof(E)), GFP) + ALLOC_OBJS(E, COUNT, GFP) | +// Convert any other type: p = kcalloc(n, sizeof(struct item), gfp) - ALLOC_ARRAY(COUNT, (sizeof(TYPE)), GFP) + ALLOC_OBJS(TYPE, COUNT, GFP) | +// The same, for arrays allocated as (size, count): +// Exclude byte buffers and integral pointers: kcalloc(sizeof(u8), n, gfp), +// kcalloc(sizeof(char *), n, gfp) + ALLOC_ARRAY((\(sizeof(STRING)\|sizeof(BYTE_TYPES)\| + sizeof(BYTE_TYPES *)\|sizeof(MULTIBYTE_TYPES *)\)), COUNT, GFP) +| +// Exclude arrays assigned to a byte pointer: +// u8 *buf = kcalloc(sizeof(*hdr), n, gfp) + BYTES = ALLOC_ARRAY((\(sizeof(E)\|sizeof(TYPE)\)), COUNT, GFP) +| +// Convert a multi-byte array to a pointer to it: +// u32 *p = kcalloc(sizeof(u32), n, gfp) + \(MULTIBYTES\|CONST_MULTIBYTES\) = +- ALLOC_ARRAY((sizeof(MULTIBYTE_TYPES)), COUNT, GFP) ++ ALLOC_OBJS(MULTIBYTE_TYPES, COUNT, GFP) +| +// Same by expression: u32 *p = kcalloc(sizeof(*p), n, gfp) + \(MULTIBYTES\|CONST_MULTIBYTES\) = +- ALLOC_ARRAY((sizeof(MULTIBYTE)), COUNT, GFP) ++ ALLOC_OBJS(MULTIBYTE, COUNT, GFP) +| +// Exclude other multi-byte arrays: +// s16 (*pairs)[2] = kcalloc(sizeof(s16), n, gfp) + ALLOC_ARRAY((\(sizeof(MULTIBYTE_TYPES)\|sizeof(MULTIBYTE)\)), COUNT, GFP) +| +// Exclude other arrays assigned to a multi-byte pointer: +// u32 *p = kcalloc(sizeof(*hdr), n, gfp) + \(MULTIBYTES\|CONST_MULTIBYTES\) = ALLOC_ARRAY((\(sizeof(E)\|sizeof(TYPE)\)), COUNT, GFP) +| +// Exclude arrays of void pointers: kcalloc(sizeof(void *), n, gfp) + ALLOC_ARRAY((sizeof(void *)), COUNT, GFP) +| +// Convert any other expression: p = kcalloc(sizeof(*p), n, gfp) - ALLOC_ARRAY((sizeof(E)), COUNT, GFP) + ALLOC_OBJS(E, COUNT, GFP) | +// Convert any other type: p = kcalloc(sizeof(struct item), n, gfp) - ALLOC_ARRAY((sizeof(TYPE)), COUNT, GFP) + ALLOC_OBJS(TYPE, COUNT, GFP) | +// Convert flexible array structures: p = kmalloc(struct_size(p, data, n), gfp) - ALLOC(struct_size(VAR, FLEX, COUNT), GFP) + ALLOC_FLEX(*VAR, FLEX, COUNT, GFP) | +// Same by type: kmalloc(struct_size_t(struct item, data, n), gfp) - ALLOC(struct_size_t(TYPE, FLEX, COUNT), GFP) + ALLOC_FLEX(TYPE, FLEX, COUNT, GFP) ) diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-plugins/randomize_layout_plugin.c index ff65a4f87f24..1e66f45fe29b 100644 --- a/scripts/gcc-plugins/randomize_layout_plugin.c +++ b/scripts/gcc-plugins/randomize_layout_plugin.c @@ -22,7 +22,7 @@ #define ORIG_TYPE_NAME(node) \ (TYPE_NAME(TYPE_MAIN_VARIANT(node)) != NULL_TREE ? ((const unsigned char *)IDENTIFIER_POINTER(TYPE_NAME(TYPE_MAIN_VARIANT(node)))) : (const unsigned char *)"anonymous") -#define INFORM(loc, msg, ...) inform(loc, "randstruct: " msg, ##__VA_ARGS__) +#define INFORM(loc, msg, ...) warning_at(loc, 0, "randstruct: " msg, ##__VA_ARGS__) #define MISMATCH(loc, how, ...) INFORM(loc, "casting between randomized structure pointer types (" how "): %qT and %qT\n", __VA_ARGS__) __visible int plugin_is_GPL_compatible; @@ -699,6 +699,63 @@ static void handle_local_var_initializers(void) } /* + * Does @container reach a field of type @member_type by a chain of + * by-value members? That is the relationship container_of() expresses -- + * its @member argument may be a dotted path, e.g. + * container_of(inode, struct ceph_inode_info, netfs.inode) -- so a cast + * from @member_type * to @container * is legitimate rather than a + * layout-confusing one. + * + * container_of() used to leave a "void *__mptr" temporary behind, and this + * pass recognised such casts by that name. Commit f9e7a7564834 + * ("container_of: remove local __mptr variable") removed it to stop nested + * container_of() shadowing itself, and the cast now folds to a bare SSA + * copy when the member sits at offset 0, leaving nothing syntactic to key + * on. Match the type relationship instead. + * + * The depth bound keeps this cheap; container_of() paths are one or two + * members deep in practice. + */ +#define CONTAINER_OF_MAX_DEPTH 4 + +static bool is_container_of_cast(const_tree container, const_tree member_type, + int depth) +{ + const_tree field; + + if (container == NULL_TREE || depth > CONTAINER_OF_MAX_DEPTH) + return false; + + if (TREE_CODE(container) != RECORD_TYPE && + TREE_CODE(container) != UNION_TYPE) + return false; + + for (field = TYPE_FIELDS(container); field; field = DECL_CHAIN(field)) { + const_tree field_type; + + if (TREE_CODE(field) != FIELD_DECL) + continue; + + /* + * Only a member at offset 0 can reach here: for any other + * offset container_of()'s subtraction survives folding, the + * cast's rhs stays void *, and the caller skipped it above. + */ + if (!integer_zerop(byte_position(field))) + continue; + + field_type = TYPE_MAIN_VARIANT(TREE_TYPE(field)); + if (field_type == member_type) + return true; + + if (is_container_of_cast(field_type, member_type, depth + 1)) + return true; + } + + return false; +} + +/* * iterate over all statements to find "bad" casts: * those where the address of the start of a structure is cast * to a pointer of a structure of a different type, or a @@ -799,10 +856,8 @@ static unsigned int find_bad_casts_execute(void) #endif MISMATCH(gimple_location(stmt), "op0", ptr_lhs_type, op0_type); } else { - const_tree ssa_name_var = SSA_NAME_VAR(rhs1); /* skip bogus type casts introduced by container_of */ - if (ssa_name_var != NULL_TREE && DECL_NAME(ssa_name_var) && - !strcmp((const char *)DECL_NAME_POINTER(ssa_name_var), "__mptr")) + if (is_container_of_cast(ptr_lhs_type, ptr_rhs_type, 0)) continue; #ifndef __DEBUG_PLUGIN if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_rhs_type))) |
