From b74dc82a3b289b872eb4ceaa2a3528e9d9b53931 Mon Sep 17 00:00:00 2001 From: Mahad Ibrahim Date: Wed, 22 Jul 2026 23:02:46 +0000 Subject: lkdtm: use kmalloc() instead of __get_free_page lkdtm_debugfs_entry and direct_entry use __get_free_page to allocate a temporary buffer, perform copy_from_user to get the crashtype name, strim() to strip whitespace and find_crashtype to find the corresponding crashtype that is being requested. The lkdtm_debugfs_read uses __get_free_page to allocate a temporary buffer to store all the available crashtypes, and then copy it to userspace. The buffers that are allocated can be allocated with kmalloc as there is nothing special that requires a struct page, or the page allocator. kmalloc() additionally provides a better API that doesn't require ugly casts which obfuscate the code and kfree does not need to know the size of the freed object. Replace use of __get_free_page() with kmalloc(). Signed-off-by: Mahad Ibrahim Acked-by: Mike Rapoport (Microsoft) Link: https://patch.msgid.link/20260722230246.2869-1-mahad.ibrahim.dev@gmail.com Signed-off-by: Kees Cook --- drivers/misc/lkdtm/core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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; -- cgit v1.2.3 From ff936b726c981e362f02755b6417654dc4ef297a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 Jun 2026 16:29:43 +0200 Subject: KMSAN: fix memset() when using fortify-source, again Both kmsan and fortify-source replace the memset function. When both are enabled at the same time, the kmsan version gets used, which triggers a warning about fortify-source being nonfunctional: warning: unsafe memset() usage lacked '__write_overflow' symbol in /home/arnd/arm-soc/lib/test_fortify/write_overflow-memset.c warning: unsafe memset() usage lacked '__write_overflow_field' symbol in /home/arnd/arm-soc/lib/test_fortify/write_overflow_field-memset.c Commit 78a498c3a227 already tried to address this, but this seems to only have worked for memcpy() and memmove() but not memset(), which is still lacking the macro definition when KMSAN is enabled. Remove the incorrect #ifndef check around the memset() macro. Fixes: ff901d80fff6 ("x86: kmsan: use __msan_ string functions where possible.") Fixes: 78a498c3a227 ("x86: fortify: kmsan: fix KMSAN fortify builds") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20260618142951.1739694-1-arnd@kernel.org Signed-off-by: Kees Cook --- include/linux/fortify-string.h | 2 -- 1 file changed, 2 deletions(-) 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, -- cgit v1.2.3 From 37eadcb919b02a742582de781097b455f7b16d76 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 6 Apr 2026 15:37:32 +0200 Subject: signalfd: don't dequeue the forced fatal signals These signals should act like SIGKILL, in that userspace must never dequeue them. But as Kusaram explains, io_uring-driven signalfd_read_iter() called from get_signal() -> task_work_run() paths can do this before get_signal() has a chance to dequeue such a signal and notice SA_IMMUTABLE. Change signalfd_poll() and signalfd_dequeue() to add pending SA_IMMUTABLE signals to ctx->sigmask. TODO: we should probably change force_sig_info_to_task(HANDLER_EXIT) to make fatal_signal_pending() true, or add a fatal_or_forced_signal_pending() helper. Then signalfd_dequeue() could just return -EINTR in this case. This also makes sense for get_signal(), which could prioritize a fatal signal sent by (say) force_sig_seccomp(force_coredump => true), just like it already prioritizes SIGKILL. Cc: stable@kernel.org Reported-by: syzbot+0a4c46806941297fecb9@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0a4c46806941297fecb9 Tested-by: syzbot+0a4c46806941297fecb9@syzkaller.appspotmail.com Link: https://lore.kernel.org/all/69d122fd.050a0220.2dbe29.001c.GAE@google.com/ Suggested-by: Kusaram Devineni Signed-off-by: Oleg Nesterov Reviewed-by: Kees Cook Link: https://patch.msgid.link/adO3HG8bvwRPcmte@redhat.com Signed-off-by: Kees Cook --- fs/signalfd.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) 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); -- cgit v1.2.3 From 3088a585de0eca79aa642ead0763f0642a1a1e52 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 3 Sep 2026 16:24:35 -0700 Subject: randstruct: fix container_of() false positives after __mptr removal Commit f9e7a7564834 ("container_of: remove local __mptr variable") dropped the "void *__mptr" temporary from container_of(). The randstruct GCC plugin's find_bad_casts pass recognized the casts container_of() generates by that variable's name: 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(DECL_NAME_POINTER(ssa_name_var), "__mptr")) continue; With the variable gone the suppression never fires, so every container_of() whose container type is randomized now emits a note: include/linux/container_of.h:23:9: note: randstruct: casting between randomized structure pointer types (ssa): 'struct ocfs2_triggers' and 'struct jbd2_buffer_trigger_type' fs/ocfs2/journal.c:524:16: note: in expansion of macro 'container_of' The pass runs on GIMPLE, after folding, and for a member at offset 0 the whole expression collapses to a bare copy that is indistinguishable from an unsafe cast: to_ocfs2_trigger (struct jbd2_buffer_trigger_type * triggers) { _2 = triggers_1(D); /* void * cast and subtraction gone */ return _2; } Match the type relationship instead. A cast from A * to B * is a container_of() if B reaches a field of type A at offset 0 through a chain of by-value members. The chain matters: container_of()'s member argument may be a dotted path, as in ceph_inode(), which is container_of(inode, struct ceph_inode_info, netfs.inode) and needs two levels. The search is depth-bounded to 4 just in case, since real paths are generally one or two members deep. Requiring the cast happens at offset 0 is done because any other member offset the subtraction survives folding and the cast's rhs is still void *, which the pass already skips a few lines above. A cast between two randomized types with no containment relationship is still reported. Verified with: struct cred *f(struct file *f) { return (struct cred *)f; } which is still flagged with the patch applied. Clang's implementation is unaffected. It checks the cast as written, and both the old and new macros cast from void *, which is always permitted; a genuinely bad cast is rejected there as a hard error rather than a note. Build tested ARCH=x86_64 defconfig with CONFIG_RANDSTRUCT_FULL=y and GCC 14.2.0: randstruct notes 52 before, 0 after. Fixes: f9e7a7564834 ("container_of: remove local __mptr variable") Assisted-by: Claude:claude-opus-5[1m] Link: https://patch.msgid.link/20260903232438.60394-1-kees@kernel.org Signed-off-by: Kees Cook --- scripts/gcc-plugins/randomize_layout_plugin.c | 61 +++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-plugins/randomize_layout_plugin.c index ff65a4f87f24..e2bd9ba08089 100644 --- a/scripts/gcc-plugins/randomize_layout_plugin.c +++ b/scripts/gcc-plugins/randomize_layout_plugin.c @@ -698,6 +698,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 @@ -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))) -- cgit v1.2.3 From 8d799af8b1b6f35fb1879e2e32c0a4d310984c7b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 3 Sep 2026 16:24:36 -0700 Subject: randstruct: report bad casts as warnings rather than notes find_bad_casts() reports a cast between two randomized structure pointer types with inform(), which renders as a "note:". It has done so since the plugin was originally added, while using error() freely for attribute misuse, UAPI structs, and version mismatches. Clang's implementation of the same check has always been stricter: it rejects such a cast as a full error. There is no reason for the GCC side to be effectively silent about the same problem. Build tested ARCH=x86_64 with CONFIG_RANDSTRUCT_FULL=y and GCC 14.2.0: allmodconfig clean, and defconfig clean under three different random seeds. A deliberate bad cast is still reported, now as a warning, at the correct line and column. Link: https://patch.msgid.link/20260903232438.60394-2-kees@kernel.org Signed-off-by: Kees Cook --- scripts/gcc-plugins/randomize_layout_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-plugins/randomize_layout_plugin.c index e2bd9ba08089..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; -- cgit v1.2.3