summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-07 14:48:32 +0100
committerMark Brown <broonie@kernel.org>2026-09-07 14:48:32 +0100
commit9a6ec108829214ff53fb03b45526ffdffe12efc6 (patch)
tree298f4c23a175818bb3a485add1316f034abc7a59
parent2675fdf0fe9b4bcf61db278fd976caed87c77da0 (diff)
parent7cada81883e820152b5cd3da038be10238b79738 (diff)
downloadlinux-next-9a6ec108829214ff53fb03b45526ffdffe12efc6.tar.gz
linux-next-9a6ec108829214ff53fb03b45526ffdffe12efc6.zip
Merge branch 'for-next/kspp' of https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git
-rw-r--r--drivers/misc/lkdtm/core.c16
-rw-r--r--fs/signalfd.c28
-rw-r--r--include/linux/fortify-string.h2
-rw-r--r--scripts/gcc-plugins/randomize_layout_plugin.c63
4 files changed, 89 insertions, 20 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;
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, &current->sighand->signalfd_wqh, wait);
spin_lock_irq(&current->sighand->siglock);
- if (next_signal(&current->pending, &ctx->sigmask) ||
- next_signal(&current->signal->shared_pending,
- &ctx->sigmask))
+ refine_sigmask(ctx, &sigmask);
+ if (next_signal(&current->pending, &sigmask) ||
+ next_signal(&current->signal->shared_pending, &sigmask))
events |= EPOLLIN;
spin_unlock_irq(&current->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(&current->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(&current->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(&current->sighand->siglock);
schedule();
spin_lock_irq(&current->sighand->siglock);
+ refine_sigmask(ctx, &sigmask);
}
spin_unlock_irq(&current->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/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)))