]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 4.19
authorSasha Levin <sashal@kernel.org>
Sat, 9 Jan 2021 16:03:24 +0000 (11:03 -0500)
committerSasha Levin <sashal@kernel.org>
Sat, 9 Jan 2021 16:03:24 +0000 (11:03 -0500)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-4.19/proc-change-nlink-under-proc_subdir_lock.patch [new file with mode: 0644]
queue-4.19/proc-fix-lookup-in-proc-net-subdirectories-after-set.patch [new file with mode: 0644]
queue-4.19/series

diff --git a/queue-4.19/proc-change-nlink-under-proc_subdir_lock.patch b/queue-4.19/proc-change-nlink-under-proc_subdir_lock.patch
new file mode 100644 (file)
index 0000000..7217c93
--- /dev/null
@@ -0,0 +1,116 @@
+From 0a092b8c9d05d23a34bfd00679575a027b35de4f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 4 Dec 2019 16:49:59 -0800
+Subject: proc: change ->nlink under proc_subdir_lock
+
+From: Alexey Dobriyan <adobriyan@gmail.com>
+
+[ Upstream commit e06689bf57017ac022ccf0f2a5071f760821ce0f ]
+
+Currently gluing PDE into global /proc tree is done under lock, but
+changing ->nlink is not.  Additionally struct proc_dir_entry::nlink is
+not atomic so updates can be lost.
+
+Link: http://lkml.kernel.org/r/20190925202436.GA17388@avx2
+Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/proc/generic.c | 31 +++++++++++++++----------------
+ 1 file changed, 15 insertions(+), 16 deletions(-)
+
+diff --git a/fs/proc/generic.c b/fs/proc/generic.c
+index e39bac94dead0..7820fe524cb03 100644
+--- a/fs/proc/generic.c
++++ b/fs/proc/generic.c
+@@ -137,8 +137,12 @@ static int proc_getattr(const struct path *path, struct kstat *stat,
+ {
+       struct inode *inode = d_inode(path->dentry);
+       struct proc_dir_entry *de = PDE(inode);
+-      if (de && de->nlink)
+-              set_nlink(inode, de->nlink);
++      if (de) {
++              nlink_t nlink = READ_ONCE(de->nlink);
++              if (nlink > 0) {
++                      set_nlink(inode, nlink);
++              }
++      }
+       generic_fillattr(inode, stat);
+       return 0;
+@@ -361,6 +365,7 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
+               write_unlock(&proc_subdir_lock);
+               goto out_free_inum;
+       }
++      dir->nlink++;
+       write_unlock(&proc_subdir_lock);
+       return dp;
+@@ -471,10 +476,7 @@ struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
+               ent->data = data;
+               ent->proc_fops = &proc_dir_operations;
+               ent->proc_iops = &proc_dir_inode_operations;
+-              parent->nlink++;
+               ent = proc_register(parent, ent);
+-              if (!ent)
+-                      parent->nlink--;
+       }
+       return ent;
+ }
+@@ -504,10 +506,7 @@ struct proc_dir_entry *proc_create_mount_point(const char *name)
+               ent->data = NULL;
+               ent->proc_fops = NULL;
+               ent->proc_iops = NULL;
+-              parent->nlink++;
+               ent = proc_register(parent, ent);
+-              if (!ent)
+-                      parent->nlink--;
+       }
+       return ent;
+ }
+@@ -665,8 +664,12 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
+       len = strlen(fn);
+       de = pde_subdir_find(parent, fn, len);
+-      if (de)
++      if (de) {
+               rb_erase(&de->subdir_node, &parent->subdir);
++              if (S_ISDIR(de->mode)) {
++                      parent->nlink--;
++              }
++      }
+       write_unlock(&proc_subdir_lock);
+       if (!de) {
+               WARN(1, "name '%s'\n", name);
+@@ -675,9 +678,6 @@ void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
+       proc_entry_rundown(de);
+-      if (S_ISDIR(de->mode))
+-              parent->nlink--;
+-      de->nlink = 0;
+       WARN(pde_subdir_first(de),
+            "%s: removing non-empty directory '%s/%s', leaking at least '%s'\n",
+            __func__, de->parent->name, de->name, pde_subdir_first(de)->name);
+@@ -713,13 +713,12 @@ int remove_proc_subtree(const char *name, struct proc_dir_entry *parent)
+                       de = next;
+                       continue;
+               }
+-              write_unlock(&proc_subdir_lock);
+-
+-              proc_entry_rundown(de);
+               next = de->parent;
+               if (S_ISDIR(de->mode))
+                       next->nlink--;
+-              de->nlink = 0;
++              write_unlock(&proc_subdir_lock);
++
++              proc_entry_rundown(de);
+               if (de == root)
+                       break;
+               pde_put(de);
+-- 
+2.27.0
+
diff --git a/queue-4.19/proc-fix-lookup-in-proc-net-subdirectories-after-set.patch b/queue-4.19/proc-fix-lookup-in-proc-net-subdirectories-after-set.patch
new file mode 100644 (file)
index 0000000..8ba4235
--- /dev/null
@@ -0,0 +1,173 @@
+From b7fbc151cc829901314130efa12f579be247c3df Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 15 Dec 2020 20:42:39 -0800
+Subject: proc: fix lookup in /proc/net subdirectories after setns(2)
+
+From: Alexey Dobriyan <adobriyan@gmail.com>
+
+[ Upstream commit c6c75deda81344c3a95d1d1f606d5cee109e5d54 ]
+
+Commit 1fde6f21d90f ("proc: fix /proc/net/* after setns(2)") only forced
+revalidation of regular files under /proc/net/
+
+However, /proc/net/ is unusual in the sense of /proc/net/foo handlers
+take netns pointer from parent directory which is old netns.
+
+Steps to reproduce:
+
+       (void)open("/proc/net/sctp/snmp", O_RDONLY);
+       unshare(CLONE_NEWNET);
+
+       int fd = open("/proc/net/sctp/snmp", O_RDONLY);
+       read(fd, &c, 1);
+
+Read will read wrong data from original netns.
+
+Patch forces lookup on every directory under /proc/net .
+
+Link: https://lkml.kernel.org/r/20201205160916.GA109739@localhost.localdomain
+Fixes: 1da4d377f943 ("proc: revalidate misc dentries")
+Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
+Reported-by: "Rantala, Tommi T. (Nokia - FI/Espoo)" <tommi.t.rantala@nokia.com>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/proc/generic.c       | 24 ++++++++++++++++++++++--
+ fs/proc/internal.h      |  7 +++++++
+ fs/proc/proc_net.c      | 16 ----------------
+ include/linux/proc_fs.h |  8 +++++++-
+ 4 files changed, 36 insertions(+), 19 deletions(-)
+
+diff --git a/fs/proc/generic.c b/fs/proc/generic.c
+index 7820fe524cb03..bab10368a04d8 100644
+--- a/fs/proc/generic.c
++++ b/fs/proc/generic.c
+@@ -341,6 +341,16 @@ static const struct file_operations proc_dir_operations = {
+       .iterate_shared         = proc_readdir,
+ };
++static int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
++{
++      return 0;
++}
++
++const struct dentry_operations proc_net_dentry_ops = {
++      .d_revalidate   = proc_net_d_revalidate,
++      .d_delete       = always_delete_dentry,
++};
++
+ /*
+  * proc directories can do almost nothing..
+  */
+@@ -463,8 +473,8 @@ struct proc_dir_entry *proc_symlink(const char *name,
+ }
+ EXPORT_SYMBOL(proc_symlink);
+-struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
+-              struct proc_dir_entry *parent, void *data)
++struct proc_dir_entry *_proc_mkdir(const char *name, umode_t mode,
++              struct proc_dir_entry *parent, void *data, bool force_lookup)
+ {
+       struct proc_dir_entry *ent;
+@@ -476,10 +486,20 @@ struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
+               ent->data = data;
+               ent->proc_fops = &proc_dir_operations;
+               ent->proc_iops = &proc_dir_inode_operations;
++              if (force_lookup) {
++                      pde_force_lookup(ent);
++              }
+               ent = proc_register(parent, ent);
+       }
+       return ent;
+ }
++EXPORT_SYMBOL_GPL(_proc_mkdir);
++
++struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
++              struct proc_dir_entry *parent, void *data)
++{
++      return _proc_mkdir(name, mode, parent, data, false);
++}
+ EXPORT_SYMBOL_GPL(proc_mkdir_data);
+ struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
+diff --git a/fs/proc/internal.h b/fs/proc/internal.h
+index 95b14196f2842..4f14906ef16b5 100644
+--- a/fs/proc/internal.h
++++ b/fs/proc/internal.h
+@@ -305,3 +305,10 @@ extern unsigned long task_statm(struct mm_struct *,
+                               unsigned long *, unsigned long *,
+                               unsigned long *, unsigned long *);
+ extern void task_mem(struct seq_file *, struct mm_struct *);
++
++extern const struct dentry_operations proc_net_dentry_ops;
++static inline void pde_force_lookup(struct proc_dir_entry *pde)
++{
++      /* /proc/net/ entries can be changed under us by setns(CLONE_NEWNET) */
++      pde->proc_dops = &proc_net_dentry_ops;
++}
+diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c
+index a7b12435519e0..096bcc1e7a8a5 100644
+--- a/fs/proc/proc_net.c
++++ b/fs/proc/proc_net.c
+@@ -38,22 +38,6 @@ static struct net *get_proc_net(const struct inode *inode)
+       return maybe_get_net(PDE_NET(PDE(inode)));
+ }
+-static int proc_net_d_revalidate(struct dentry *dentry, unsigned int flags)
+-{
+-      return 0;
+-}
+-
+-static const struct dentry_operations proc_net_dentry_ops = {
+-      .d_revalidate   = proc_net_d_revalidate,
+-      .d_delete       = always_delete_dentry,
+-};
+-
+-static void pde_force_lookup(struct proc_dir_entry *pde)
+-{
+-      /* /proc/net/ entries can be changed under us by setns(CLONE_NEWNET) */
+-      pde->proc_dops = &proc_net_dentry_ops;
+-}
+-
+ static int seq_open_net(struct inode *inode, struct file *file)
+ {
+       unsigned int state_size = PDE(inode)->state_size;
+diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
+index d0e1f1522a78e..5141657a0f7f6 100644
+--- a/include/linux/proc_fs.h
++++ b/include/linux/proc_fs.h
+@@ -21,6 +21,7 @@ extern void proc_flush_task(struct task_struct *);
+ extern struct proc_dir_entry *proc_symlink(const char *,
+               struct proc_dir_entry *, const char *);
++struct proc_dir_entry *_proc_mkdir(const char *, umode_t, struct proc_dir_entry *, void *, bool);
+ extern struct proc_dir_entry *proc_mkdir(const char *, struct proc_dir_entry *);
+ extern struct proc_dir_entry *proc_mkdir_data(const char *, umode_t,
+                                             struct proc_dir_entry *, void *);
+@@ -89,6 +90,11 @@ static inline struct proc_dir_entry *proc_symlink(const char *name,
+ static inline struct proc_dir_entry *proc_mkdir(const char *name,
+       struct proc_dir_entry *parent) {return NULL;}
+ static inline struct proc_dir_entry *proc_create_mount_point(const char *name) { return NULL; }
++static inline struct proc_dir_entry *_proc_mkdir(const char *name, umode_t mode,
++              struct proc_dir_entry *parent, void *data, bool force_lookup)
++{
++      return NULL;
++}
+ static inline struct proc_dir_entry *proc_mkdir_data(const char *name,
+       umode_t mode, struct proc_dir_entry *parent, void *data) { return NULL; }
+ static inline struct proc_dir_entry *proc_mkdir_mode(const char *name,
+@@ -121,7 +127,7 @@ struct net;
+ static inline struct proc_dir_entry *proc_net_mkdir(
+       struct net *net, const char *name, struct proc_dir_entry *parent)
+ {
+-      return proc_mkdir_data(name, 0, parent, net);
++      return _proc_mkdir(name, 0, parent, net, true);
+ }
+ struct ns_common;
+-- 
+2.27.0
+
index a91ef68f5c4bc8a36c26a59a80eb4a3de052b2bd..106cf91ae53cdc300bec7a48f898f07e1e773a81 100644 (file)
@@ -5,3 +5,5 @@ scsi-ide-do-not-set-the-rqf_preempt-flag-for-sense-r.patch
 scsi-scsi_transport_spi-set-rqf_pm-for-domain-valida.patch
 lib-genalloc-fix-the-overflow-when-size-is-too-big.patch
 depmod-handle-the-case-of-sbin-depmod-without-sbin-i.patch
+proc-change-nlink-under-proc_subdir_lock.patch
+proc-fix-lookup-in-proc-net-subdirectories-after-set.patch