]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.1-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 30 Dec 2023 11:23:50 +0000 (11:23 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 30 Dec 2023 11:23:50 +0000 (11:23 +0000)
added patches:
fuse-share-lookup-state-between-submount-and-its-parent.patch
mm-damon-core-make-damon_start-waits-until-kdamond_fn-starts.patch
tracing-kprobes-fix-symbol-counting-logic-by-looking-at-modules-as-well.patch

queue-6.1/fuse-share-lookup-state-between-submount-and-its-parent.patch [new file with mode: 0644]
queue-6.1/mm-damon-core-make-damon_start-waits-until-kdamond_fn-starts.patch [new file with mode: 0644]
queue-6.1/series
queue-6.1/tracing-kprobes-fix-symbol-counting-logic-by-looking-at-modules-as-well.patch [new file with mode: 0644]

diff --git a/queue-6.1/fuse-share-lookup-state-between-submount-and-its-parent.patch b/queue-6.1/fuse-share-lookup-state-between-submount-and-its-parent.patch
new file mode 100644 (file)
index 0000000..85f908b
--- /dev/null
@@ -0,0 +1,222 @@
+From c4d361f66ac91db8fc65061a9671682f61f4ca9d Mon Sep 17 00:00:00 2001
+From: Krister Johansen <kjlx@templeofstupid.com>
+Date: Fri, 3 Nov 2023 10:39:47 -0700
+Subject: fuse: share lookup state between submount and its parent
+
+From: Krister Johansen <kjlx@templeofstupid.com>
+
+commit c4d361f66ac91db8fc65061a9671682f61f4ca9d upstream.
+
+Fuse submounts do not perform a lookup for the nodeid that they inherit
+from their parent.  Instead, the code decrements the nlookup on the
+submount's fuse_inode when it is instantiated, and no forget is
+performed when a submount root is evicted.
+
+Trouble arises when the submount's parent is evicted despite the
+submount itself being in use.  In this author's case, the submount was
+in a container and deatched from the initial mount namespace via a
+MNT_DEATCH operation.  When memory pressure triggered the shrinker, the
+inode from the parent was evicted, which triggered enough forgets to
+render the submount's nodeid invalid.
+
+Since submounts should still function, even if their parent goes away,
+solve this problem by sharing refcounted state between the parent and
+its submount.  When all of the references on this shared state reach
+zero, it's safe to forget the final lookup of the fuse nodeid.
+
+Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
+Cc: stable@vger.kernel.org
+Fixes: 1866d779d5d2 ("fuse: Allow fuse_fill_super_common() for submounts")
+Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/fuse/fuse_i.h |   15 +++++++++++
+ fs/fuse/inode.c  |   75 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
+ 2 files changed, 87 insertions(+), 3 deletions(-)
+
+--- a/fs/fuse/fuse_i.h
++++ b/fs/fuse/fuse_i.h
+@@ -63,6 +63,19 @@ struct fuse_forget_link {
+       struct fuse_forget_link *next;
+ };
++/* Submount lookup tracking */
++struct fuse_submount_lookup {
++      /** Refcount */
++      refcount_t count;
++
++      /** Unique ID, which identifies the inode between userspace
++       * and kernel */
++      u64 nodeid;
++
++      /** The request used for sending the FORGET message */
++      struct fuse_forget_link *forget;
++};
++
+ /** FUSE inode */
+ struct fuse_inode {
+       /** Inode data */
+@@ -155,6 +168,8 @@ struct fuse_inode {
+        */
+       struct fuse_inode_dax *dax;
+ #endif
++      /** Submount specific lookup tracking */
++      struct fuse_submount_lookup *submount_lookup;
+ };
+ /** FUSE inode state bits */
+--- a/fs/fuse/inode.c
++++ b/fs/fuse/inode.c
+@@ -68,6 +68,24 @@ struct fuse_forget_link *fuse_alloc_forg
+       return kzalloc(sizeof(struct fuse_forget_link), GFP_KERNEL_ACCOUNT);
+ }
++static struct fuse_submount_lookup *fuse_alloc_submount_lookup(void)
++{
++      struct fuse_submount_lookup *sl;
++
++      sl = kzalloc(sizeof(struct fuse_submount_lookup), GFP_KERNEL_ACCOUNT);
++      if (!sl)
++              return NULL;
++      sl->forget = fuse_alloc_forget();
++      if (!sl->forget)
++              goto out_free;
++
++      return sl;
++
++out_free:
++      kfree(sl);
++      return NULL;
++}
++
+ static struct inode *fuse_alloc_inode(struct super_block *sb)
+ {
+       struct fuse_inode *fi;
+@@ -83,6 +101,7 @@ static struct inode *fuse_alloc_inode(st
+       fi->attr_version = 0;
+       fi->orig_ino = 0;
+       fi->state = 0;
++      fi->submount_lookup = NULL;
+       mutex_init(&fi->mutex);
+       spin_lock_init(&fi->lock);
+       fi->forget = fuse_alloc_forget();
+@@ -113,6 +132,17 @@ static void fuse_free_inode(struct inode
+       kmem_cache_free(fuse_inode_cachep, fi);
+ }
++static void fuse_cleanup_submount_lookup(struct fuse_conn *fc,
++                                       struct fuse_submount_lookup *sl)
++{
++      if (!refcount_dec_and_test(&sl->count))
++              return;
++
++      fuse_queue_forget(fc, sl->forget, sl->nodeid, 1);
++      sl->forget = NULL;
++      kfree(sl);
++}
++
+ static void fuse_evict_inode(struct inode *inode)
+ {
+       struct fuse_inode *fi = get_fuse_inode(inode);
+@@ -132,6 +162,11 @@ static void fuse_evict_inode(struct inod
+                                         fi->nlookup);
+                       fi->forget = NULL;
+               }
++
++              if (fi->submount_lookup) {
++                      fuse_cleanup_submount_lookup(fc, fi->submount_lookup);
++                      fi->submount_lookup = NULL;
++              }
+       }
+       if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) {
+               WARN_ON(!list_empty(&fi->write_files));
+@@ -311,6 +346,13 @@ void fuse_change_attributes(struct inode
+               fuse_dax_dontcache(inode, attr->flags);
+ }
++static void fuse_init_submount_lookup(struct fuse_submount_lookup *sl,
++                                    u64 nodeid)
++{
++      sl->nodeid = nodeid;
++      refcount_set(&sl->count, 1);
++}
++
+ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
+ {
+       inode->i_mode = attr->mode & S_IFMT;
+@@ -368,12 +410,22 @@ struct inode *fuse_iget(struct super_blo
+        */
+       if (fc->auto_submounts && (attr->flags & FUSE_ATTR_SUBMOUNT) &&
+           S_ISDIR(attr->mode)) {
++              struct fuse_inode *fi;
++
+               inode = new_inode(sb);
+               if (!inode)
+                       return NULL;
+               fuse_init_inode(inode, attr);
+-              get_fuse_inode(inode)->nodeid = nodeid;
++              fi = get_fuse_inode(inode);
++              fi->nodeid = nodeid;
++              fi->submount_lookup = fuse_alloc_submount_lookup();
++              if (!fi->submount_lookup) {
++                      iput(inode);
++                      return NULL;
++              }
++              /* Sets nlookup = 1 on fi->submount_lookup->nlookup */
++              fuse_init_submount_lookup(fi->submount_lookup, nodeid);
+               inode->i_flags |= S_AUTOMOUNT;
+               goto done;
+       }
+@@ -396,11 +448,11 @@ retry:
+               iput(inode);
+               goto retry;
+       }
+-done:
+       fi = get_fuse_inode(inode);
+       spin_lock(&fi->lock);
+       fi->nlookup++;
+       spin_unlock(&fi->lock);
++done:
+       fuse_change_attributes(inode, attr, attr_valid, attr_version);
+       return inode;
+@@ -1439,6 +1491,8 @@ static int fuse_fill_super_submount(stru
+       struct super_block *parent_sb = parent_fi->inode.i_sb;
+       struct fuse_attr root_attr;
+       struct inode *root;
++      struct fuse_submount_lookup *sl;
++      struct fuse_inode *fi;
+       fuse_sb_defaults(sb);
+       fm->sb = sb;
+@@ -1461,12 +1515,27 @@ static int fuse_fill_super_submount(stru
+        * its nlookup should not be incremented.  fuse_iget() does
+        * that, though, so undo it here.
+        */
+-      get_fuse_inode(root)->nlookup--;
++      fi = get_fuse_inode(root);
++      fi->nlookup--;
++
+       sb->s_d_op = &fuse_dentry_operations;
+       sb->s_root = d_make_root(root);
+       if (!sb->s_root)
+               return -ENOMEM;
++      /*
++       * Grab the parent's submount_lookup pointer and take a
++       * reference on the shared nlookup from the parent.  This is to
++       * prevent the last forget for this nodeid from getting
++       * triggered until all users have finished with it.
++       */
++      sl = parent_fi->submount_lookup;
++      WARN_ON(!sl);
++      if (sl) {
++              refcount_inc(&sl->count);
++              fi->submount_lookup = sl;
++      }
++
+       return 0;
+ }
diff --git a/queue-6.1/mm-damon-core-make-damon_start-waits-until-kdamond_fn-starts.patch b/queue-6.1/mm-damon-core-make-damon_start-waits-until-kdamond_fn-starts.patch
new file mode 100644 (file)
index 0000000..fd49ed5
--- /dev/null
@@ -0,0 +1,92 @@
+From 6376a824595607e99d032a39ba3394988b4fce96 Mon Sep 17 00:00:00 2001
+From: SeongJae Park <sj@kernel.org>
+Date: Fri, 8 Dec 2023 17:50:18 +0000
+Subject: mm/damon/core: make damon_start() waits until kdamond_fn() starts
+
+From: SeongJae Park <sj@kernel.org>
+
+commit 6376a824595607e99d032a39ba3394988b4fce96 upstream.
+
+The cleanup tasks of kdamond threads including reset of corresponding
+DAMON context's ->kdamond field and decrease of global nr_running_ctxs
+counter is supposed to be executed by kdamond_fn().  However, commit
+0f91d13366a4 ("mm/damon: simplify stop mechanism") made neither
+damon_start() nor damon_stop() ensure the corresponding kdamond has
+started the execution of kdamond_fn().
+
+As a result, the cleanup can be skipped if damon_stop() is called fast
+enough after the previous damon_start().  Especially the skipped reset
+of ->kdamond could cause a use-after-free.
+
+Fix it by waiting for start of kdamond_fn() execution from
+damon_start().
+
+Link: https://lkml.kernel.org/r/20231208175018.63880-1-sj@kernel.org
+Fixes: 0f91d13366a4 ("mm/damon: simplify stop mechanism")
+Signed-off-by: SeongJae Park <sj@kernel.org>
+Reported-by: Jakub Acs <acsjakub@amazon.de>
+Cc: Changbin Du <changbin.du@intel.com>
+Cc: Jakub Acs <acsjakub@amazon.de>
+Cc: <stable@vger.kernel.org> # 5.15.x
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: SeongJae Park <sj@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/damon.h |    3 +++
+ mm/damon/core.c       |    7 +++++++
+ 2 files changed, 10 insertions(+)
+
+--- a/include/linux/damon.h
++++ b/include/linux/damon.h
+@@ -8,6 +8,7 @@
+ #ifndef _DAMON_H_
+ #define _DAMON_H_
++#include <linux/completion.h>
+ #include <linux/mutex.h>
+ #include <linux/time64.h>
+ #include <linux/types.h>
+@@ -452,6 +453,8 @@ struct damon_ctx {
+ /* private: internal use only */
+       struct timespec64 last_aggregation;
+       struct timespec64 last_ops_update;
++      /* for waiting until the execution of the kdamond_fn is started */
++      struct completion kdamond_started;
+ /* public: */
+       struct task_struct *kdamond;
+--- a/mm/damon/core.c
++++ b/mm/damon/core.c
+@@ -383,6 +383,8 @@ struct damon_ctx *damon_new_ctx(void)
+       if (!ctx)
+               return NULL;
++      init_completion(&ctx->kdamond_started);
++
+       ctx->attrs.sample_interval = 5 * 1000;
+       ctx->attrs.aggr_interval = 100 * 1000;
+       ctx->attrs.ops_update_interval = 60 * 1000 * 1000;
+@@ -519,11 +521,14 @@ static int __damon_start(struct damon_ct
+       mutex_lock(&ctx->kdamond_lock);
+       if (!ctx->kdamond) {
+               err = 0;
++              reinit_completion(&ctx->kdamond_started);
+               ctx->kdamond = kthread_run(kdamond_fn, ctx, "kdamond.%d",
+                               nr_running_ctxs);
+               if (IS_ERR(ctx->kdamond)) {
+                       err = PTR_ERR(ctx->kdamond);
+                       ctx->kdamond = NULL;
++              } else {
++                      wait_for_completion(&ctx->kdamond_started);
+               }
+       }
+       mutex_unlock(&ctx->kdamond_lock);
+@@ -1147,6 +1152,8 @@ static int kdamond_fn(void *data)
+       pr_debug("kdamond (%d) starts\n", current->pid);
++      complete(&ctx->kdamond_started);
++
+       if (ctx->ops.init)
+               ctx->ops.init(ctx);
+       if (ctx->callback.before_start && ctx->callback.before_start(ctx))
index bb0fe3fdcd8d93ea649e288ee7fc0b4e53a3917c..9a246a05c3b71592fdada13c88da42055d9e9f58 100644 (file)
@@ -106,3 +106,6 @@ kvm-arm64-vgic-simplify-kvm_vgic_destroy.patch
 kvm-arm64-vgic-add-a-non-locking-primitive-for-kvm_vgic_vcpu_destroy.patch
 kvm-arm64-vgic-force-vcpu-vgic-teardown-on-vcpu-destroy.patch
 x86-alternatives-sync-core-before-enabling-interrupts.patch
+mm-damon-core-make-damon_start-waits-until-kdamond_fn-starts.patch
+fuse-share-lookup-state-between-submount-and-its-parent.patch
+tracing-kprobes-fix-symbol-counting-logic-by-looking-at-modules-as-well.patch
diff --git a/queue-6.1/tracing-kprobes-fix-symbol-counting-logic-by-looking-at-modules-as-well.patch b/queue-6.1/tracing-kprobes-fix-symbol-counting-logic-by-looking-at-modules-as-well.patch
new file mode 100644 (file)
index 0000000..f9bf639
--- /dev/null
@@ -0,0 +1,68 @@
+From 926fe783c8a64b33997fec405cf1af3e61aed441 Mon Sep 17 00:00:00 2001
+From: Andrii Nakryiko <andrii@kernel.org>
+Date: Fri, 27 Oct 2023 16:31:26 -0700
+Subject: tracing/kprobes: Fix symbol counting logic by looking at modules as well
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+commit 926fe783c8a64b33997fec405cf1af3e61aed441 upstream.
+
+Recent changes to count number of matching symbols when creating
+a kprobe event failed to take into account kernel modules. As such, it
+breaks kprobes on kernel module symbols, by assuming there is no match.
+
+Fix this my calling module_kallsyms_on_each_symbol() in addition to
+kallsyms_on_each_match_symbol() to perform a proper counting.
+
+Link: https://lore.kernel.org/all/20231027233126.2073148-1-andrii@kernel.org/
+
+Cc: Francis Laniel <flaniel@linux.microsoft.com>
+Cc: stable@vger.kernel.org
+Cc: Masami Hiramatsu <mhiramat@kernel.org>
+Cc: Steven Rostedt <rostedt@goodmis.org>
+Fixes: b022f0c7e404 ("tracing/kprobes: Return EADDRNOTAVAIL when func matches several symbols")
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Acked-by: Song Liu <song@kernel.org>
+Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/trace/trace_kprobe.c |   24 ++++++++++++++++++++----
+ 1 file changed, 20 insertions(+), 4 deletions(-)
+
+--- a/kernel/trace/trace_kprobe.c
++++ b/kernel/trace/trace_kprobe.c
+@@ -714,14 +714,30 @@ static int count_symbols(void *data, uns
+       return 0;
+ }
++struct sym_count_ctx {
++      unsigned int count;
++      const char *name;
++};
++
++static int count_mod_symbols(void *data, const char *name, unsigned long unused)
++{
++      struct sym_count_ctx *ctx = data;
++
++      if (strcmp(name, ctx->name) == 0)
++              ctx->count++;
++
++      return 0;
++}
++
+ static unsigned int number_of_same_symbols(char *func_name)
+ {
+-      unsigned int count;
++      struct sym_count_ctx ctx = { .count = 0, .name = func_name };
++
++      kallsyms_on_each_match_symbol(count_symbols, func_name, &ctx.count);
+-      count = 0;
+-      kallsyms_on_each_match_symbol(count_symbols, func_name, &count);
++      module_kallsyms_on_each_symbol(NULL, count_mod_symbols, &ctx);
+-      return count;
++      return ctx.count;
+ }
+ static int __trace_kprobe_create(int argc, const char *argv[])