From: Greg Kroah-Hartman Date: Sun, 5 Dec 2021 12:54:53 +0000 (+0100) Subject: 4.9-stable patches X-Git-Tag: v4.4.294~37 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=978783f3792c19ff92cd0ea15b7d954fef9b38b7;p=thirdparty%2Fkernel%2Fstable-queue.git 4.9-stable patches added patches: fget-check-that-the-fd-still-exists-after-getting-a-ref-to-it.patch fs-add-fget_many-and-fput_many.patch --- diff --git a/queue-4.9/fget-check-that-the-fd-still-exists-after-getting-a-ref-to-it.patch b/queue-4.9/fget-check-that-the-fd-still-exists-after-getting-a-ref-to-it.patch new file mode 100644 index 00000000000..e1ecfcf99ea --- /dev/null +++ b/queue-4.9/fget-check-that-the-fd-still-exists-after-getting-a-ref-to-it.patch @@ -0,0 +1,63 @@ +From 054aa8d439b9185d4f5eb9a90282d1ce74772969 Mon Sep 17 00:00:00 2001 +From: Linus Torvalds +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 + +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 +Acked-by: Miklos Szeredi +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman +--- + fs/file.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/fs/file.c ++++ b/fs/file.c +@@ -709,6 +709,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.9/fs-add-fget_many-and-fput_many.patch b/queue-4.9/fs-add-fget_many-and-fput_many.patch new file mode 100644 index 00000000000..b035a3084ae --- /dev/null +++ b/queue-4.9/fs-add-fget_many-and-fput_many.patch @@ -0,0 +1,138 @@ +From 091141a42e15fe47ada737f3996b317072afcefb Mon Sep 17 00:00:00 2001 +From: Jens Axboe +Date: Wed, 21 Nov 2018 10:32:39 -0700 +Subject: fs: add fget_many() and fput_many() + +From: Jens Axboe + +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 +Reviewed-by: Christoph Hellwig +Signed-off-by: Jens Axboe +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -692,7 +692,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; +@@ -707,7 +707,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(); +@@ -715,15 +715,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); + +@@ -754,7 +759,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 +@@ -939,7 +939,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) + diff --git a/queue-4.9/series b/queue-4.9/series index 2ddcd60d002..fdbba4936f0 100644 --- a/queue-4.9/series +++ b/queue-4.9/series @@ -49,3 +49,5 @@ vrf-reset-ipcb-ip6cb-when-processing-outbound-pkts-in-vrf-dev-xmit.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