]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 5 Dec 2021 12:54:36 +0000 (13:54 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 5 Dec 2021 12:54:36 +0000 (13:54 +0100)
added patches:
fget-check-that-the-fd-still-exists-after-getting-a-ref-to-it.patch
fs-add-fget_many-and-fput_many.patch

queue-4.4/fget-check-that-the-fd-still-exists-after-getting-a-ref-to-it.patch [new file with mode: 0644]
queue-4.4/fs-add-fget_many-and-fput_many.patch [new file with mode: 0644]
queue-4.4/series

diff --git a/queue-4.4/fget-check-that-the-fd-still-exists-after-getting-a-ref-to-it.patch b/queue-4.4/fget-check-that-the-fd-still-exists-after-getting-a-ref-to-it.patch
new file mode 100644 (file)
index 0000000..e869edf
--- /dev/null
@@ -0,0 +1,63 @@
+From 054aa8d439b9185d4f5eb9a90282d1ce74772969 Mon Sep 17 00:00:00 2001
+From: Linus Torvalds <torvalds@linux-foundation.org>
+Date: Wed, 1 Dec 2021 10:06:14 -0800
+Subject: fget: check that the fd still exists after getting a ref to it
+
+From: Linus Torvalds <torvalds@linux-foundation.org>
+
+commit 054aa8d439b9185d4f5eb9a90282d1ce74772969 upstream.
+
+Jann Horn points out that there is another possible race wrt Unix domain
+socket garbage collection, somewhat reminiscent of the one fixed in
+commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK").
+
+See the extended comment about the garbage collection requirements added
+to unix_peek_fds() by that commit for details.
+
+The race comes from how we can locklessly look up a file descriptor just
+as it is in the process of being closed, and with the right artificial
+timing (Jann added a few strategic 'mdelay(500)' calls to do that), the
+Unix domain socket garbage collector could see the reference count
+decrement of the close() happen before fget() took its reference to the
+file and the file was attached onto a new file descriptor.
+
+This is all (intentionally) correct on the 'struct file *' side, with
+RCU lookups and lockless reference counting very much part of the
+design.  Getting that reference count out of order isn't a problem per
+se.
+
+But the garbage collector can get confused by seeing this situation of
+having seen a file not having any remaining external references and then
+seeing it being attached to an fd.
+
+In commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK") the
+fix was to serialize the file descriptor install with the garbage
+collector by taking and releasing the unix_gc_lock.
+
+That's not really an option here, but since this all happens when we are
+in the process of looking up a file descriptor, we can instead simply
+just re-check that the file hasn't been closed in the meantime, and just
+re-do the lookup if we raced with a concurrent close() of the same file
+descriptor.
+
+Reported-and-tested-by: Jann Horn <jannh@google.com>
+Acked-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/file.c |    4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/fs/file.c
++++ b/fs/file.c
+@@ -708,6 +708,10 @@ loop:
+                       file = NULL;
+               else if (!get_file_rcu_many(file, refs))
+                       goto loop;
++              else if (__fcheck_files(files, fd) != file) {
++                      fput_many(file, refs);
++                      goto loop;
++              }
+       }
+       rcu_read_unlock();
diff --git a/queue-4.4/fs-add-fget_many-and-fput_many.patch b/queue-4.4/fs-add-fget_many-and-fput_many.patch
new file mode 100644 (file)
index 0000000..c2a8bcd
--- /dev/null
@@ -0,0 +1,138 @@
+From 091141a42e15fe47ada737f3996b317072afcefb Mon Sep 17 00:00:00 2001
+From: Jens Axboe <axboe@kernel.dk>
+Date: Wed, 21 Nov 2018 10:32:39 -0700
+Subject: fs: add fget_many() and fput_many()
+
+From: Jens Axboe <axboe@kernel.dk>
+
+commit 091141a42e15fe47ada737f3996b317072afcefb upstream.
+
+Some uses cases repeatedly get and put references to the same file, but
+the only exposed interface is doing these one at the time. As each of
+these entail an atomic inc or dec on a shared structure, that cost can
+add up.
+
+Add fget_many(), which works just like fget(), except it takes an
+argument for how many references to get on the file. Ditto fput_many(),
+which can drop an arbitrary number of references to a file.
+
+Reviewed-by: Hannes Reinecke <hare@suse.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/file.c            |   15 ++++++++++-----
+ fs/file_table.c      |    9 +++++++--
+ include/linux/file.h |    2 ++
+ include/linux/fs.h   |    4 +++-
+ 4 files changed, 22 insertions(+), 8 deletions(-)
+
+--- a/fs/file.c
++++ b/fs/file.c
+@@ -691,7 +691,7 @@ void do_close_on_exec(struct files_struc
+       spin_unlock(&files->file_lock);
+ }
+-static struct file *__fget(unsigned int fd, fmode_t mask)
++static struct file *__fget(unsigned int fd, fmode_t mask, unsigned int refs)
+ {
+       struct files_struct *files = current->files;
+       struct file *file;
+@@ -706,7 +706,7 @@ loop:
+                */
+               if (file->f_mode & mask)
+                       file = NULL;
+-              else if (!get_file_rcu(file))
++              else if (!get_file_rcu_many(file, refs))
+                       goto loop;
+       }
+       rcu_read_unlock();
+@@ -714,15 +714,20 @@ loop:
+       return file;
+ }
++struct file *fget_many(unsigned int fd, unsigned int refs)
++{
++      return __fget(fd, FMODE_PATH, refs);
++}
++
+ struct file *fget(unsigned int fd)
+ {
+-      return __fget(fd, FMODE_PATH);
++      return __fget(fd, FMODE_PATH, 1);
+ }
+ EXPORT_SYMBOL(fget);
+ struct file *fget_raw(unsigned int fd)
+ {
+-      return __fget(fd, 0);
++      return __fget(fd, 0, 1);
+ }
+ EXPORT_SYMBOL(fget_raw);
+@@ -753,7 +758,7 @@ static unsigned long __fget_light(unsign
+                       return 0;
+               return (unsigned long)file;
+       } else {
+-              file = __fget(fd, mask);
++              file = __fget(fd, mask, 1);
+               if (!file)
+                       return 0;
+               return FDPUT_FPUT | (unsigned long)file;
+--- a/fs/file_table.c
++++ b/fs/file_table.c
+@@ -261,9 +261,9 @@ void flush_delayed_fput(void)
+ static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput);
+-void fput(struct file *file)
++void fput_many(struct file *file, unsigned int refs)
+ {
+-      if (atomic_long_dec_and_test(&file->f_count)) {
++      if (atomic_long_sub_and_test(refs, &file->f_count)) {
+               struct task_struct *task = current;
+               if (likely(!in_interrupt() && !(task->flags & PF_KTHREAD))) {
+@@ -282,6 +282,11 @@ void fput(struct file *file)
+       }
+ }
++void fput(struct file *file)
++{
++      fput_many(file, 1);
++}
++
+ /*
+  * synchronous analog of fput(); for kernel threads that might be needed
+  * in some umount() (and thus can't use flush_delayed_fput() without
+--- a/include/linux/file.h
++++ b/include/linux/file.h
+@@ -12,6 +12,7 @@
+ struct file;
+ extern void fput(struct file *);
++extern void fput_many(struct file *, unsigned int);
+ struct file_operations;
+ struct vfsmount;
+@@ -40,6 +41,7 @@ static inline void fdput(struct fd fd)
+ }
+ extern struct file *fget(unsigned int fd);
++extern struct file *fget_many(unsigned int fd, unsigned int refs);
+ extern struct file *fget_raw(unsigned int fd);
+ extern unsigned long __fdget(unsigned int fd);
+ extern unsigned long __fdget_raw(unsigned int fd);
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -923,7 +923,9 @@ static inline struct file *get_file(stru
+       atomic_long_inc(&f->f_count);
+       return f;
+ }
+-#define get_file_rcu(x) atomic_long_inc_not_zero(&(x)->f_count)
++#define get_file_rcu_many(x, cnt)     \
++      atomic_long_add_unless(&(x)->f_count, (cnt), 0)
++#define get_file_rcu(x) get_file_rcu_many((x), 1)
+ #define fput_atomic(x)        atomic_long_add_unless(&(x)->f_count, -1, 1)
+ #define file_count(x) atomic_long_read(&(x)->f_count)
index dbd4cf39e1a2fd86c2f90a5200e8602045920af0..70cc12836b560ddb43922bcc03eacf430df1d81e 100644 (file)
@@ -41,3 +41,5 @@ net-ethernet-dec-tulip-de4x5-fix-possible-array-over.patch
 kprobes-limit-max-data_size-of-the-kretprobe-instances.patch
 sata_fsl-fix-uaf-in-sata_fsl_port_stop-when-rmmod-sata_fsl.patch
 sata_fsl-fix-warning-in-remove_proc_entry-when-rmmod-sata_fsl.patch
+fs-add-fget_many-and-fput_many.patch
+fget-check-that-the-fd-still-exists-after-getting-a-ref-to-it.patch