|
|
ad8c96 |
From 80c7e812dffa734599aadde93cb8e30b34f0983d Mon Sep 17 00:00:00 2001
|
|
|
ad8c96 |
From: Joe Lawrence <joe.lawrence@redhat.com>
|
|
|
ad8c96 |
Date: Mon, 21 Mar 2022 15:45:03 -0400
|
|
|
ad8c96 |
Subject: [KPATCH CVE-2021-4083] fget: kpatch fixes for CVE-2021-4083
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Kernels:
|
|
|
ad8c96 |
3.10.0-1160.24.1.el7
|
|
|
ad8c96 |
3.10.0-1160.25.1.el7
|
|
|
ad8c96 |
3.10.0-1160.31.1.el7
|
|
|
ad8c96 |
3.10.0-1160.36.2.el7
|
|
|
ad8c96 |
3.10.0-1160.41.1.el7
|
|
|
ad8c96 |
3.10.0-1160.42.2.el7
|
|
|
ad8c96 |
3.10.0-1160.45.1.el7
|
|
|
ad8c96 |
3.10.0-1160.49.1.el7
|
|
|
ad8c96 |
3.10.0-1160.53.1.el7
|
|
|
ad8c96 |
3.10.0-1160.59.1.el7
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Changes since last build:
|
|
|
ad8c96 |
arches: x86_64 ppc64le
|
|
|
ad8c96 |
file.o: changed function: SyS_dup
|
|
|
ad8c96 |
file.o: changed function: dup_fd
|
|
|
ad8c96 |
file.o: changed function: fget
|
|
|
ad8c96 |
file.o: changed function: fget_light
|
|
|
ad8c96 |
file.o: changed function: fget_raw
|
|
|
ad8c96 |
file.o: changed function: fget_raw_light
|
|
|
ad8c96 |
file.o: changed function: put_files_struct
|
|
|
ad8c96 |
file.o: new function: __fget
|
|
|
ad8c96 |
file.o: new function: __fget_light
|
|
|
ad8c96 |
---------------------------
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Kpatch-MR: https://gitlab.com/redhat/prdsc/rhel/src/kpatch/rhel-7/-/merge_requests/34
|
|
|
ad8c96 |
Approved-by: Yannick Cote (@ycote1)
|
|
|
ad8c96 |
Modifications:
|
|
|
ad8c96 |
- include/linux/rcupdate.h, kernel/rcupdate.c: leave exported
|
|
|
ad8c96 |
rcu_my_thread_group_empty() intact
|
|
|
ad8c96 |
- fs/file.c: use fput() instead of fputs_many() since we skipped commit
|
|
|
ad8c96 |
("fs: add fget_many() and fput_many()")
|
|
|
ad8c96 |
- fs/file.c: use fcheck_files() instead of files_lookup_fd_raw() since
|
|
|
ad8c96 |
we are skipping subsequent commit ("fget: clarify and improve
|
|
|
ad8c96 |
__fget_files() implementation") that provides it.
|
|
|
ad8c96 |
- Set __attribute__((optimize("-fno-optimize-sibling-calls"))) for
|
|
|
ad8c96 |
fget() and fget_raw() on ppc64le
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit c2207a235113315ad696b06eb96ccd36d1f5fdeb
|
|
|
ad8c96 |
Author: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
Date: Fri Jan 21 10:22:29 2022 +0100
|
|
|
ad8c96 |
|
|
|
ad8c96 |
introduce __fcheck_files() to fix rcu_dereference_check_fdtable(), kill rcu_my_thread_group_empty()
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2032478
|
|
|
ad8c96 |
Upstream status: Linus
|
|
|
ad8c96 |
Testing: xfstests
|
|
|
ad8c96 |
CVE: CVE-2021-4083
|
|
|
ad8c96 |
Conflicts:
|
|
|
ad8c96 |
- context difference due to backport of later patch
|
|
|
ad8c96 |
- target file difference due to missing backport of rcu source code
|
|
|
ad8c96 |
move
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit a8d4b8345e0ee48b732126d980efaf0dc373e2b0
|
|
|
ad8c96 |
Author: Oleg Nesterov <oleg@redhat.com>
|
|
|
ad8c96 |
Date: Sat Jan 11 19:19:32 2014 +0100
|
|
|
ad8c96 |
|
|
|
ad8c96 |
introduce __fcheck_files() to fix rcu_dereference_check_fdtable(), kill rcu_my_thread_group_empty()
|
|
|
ad8c96 |
|
|
|
ad8c96 |
rcu_dereference_check_fdtable() looks very wrong,
|
|
|
ad8c96 |
|
|
|
ad8c96 |
1. rcu_my_thread_group_empty() was added by 844b9a8707f1 "vfs: fix
|
|
|
ad8c96 |
RCU-lockdep false positive due to /proc" but it doesn't really
|
|
|
ad8c96 |
fix the problem. A CLONE_THREAD (without CLONE_FILES) task can
|
|
|
ad8c96 |
hit the same race with get_files_struct().
|
|
|
ad8c96 |
|
|
|
ad8c96 |
And otoh rcu_my_thread_group_empty() can suppress the correct
|
|
|
ad8c96 |
warning if the caller is the CLONE_FILES (without CLONE_THREAD)
|
|
|
ad8c96 |
task.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
2. files->count == 1 check is not really right too. Even if this
|
|
|
ad8c96 |
files_struct is not shared it is not safe to access it lockless
|
|
|
ad8c96 |
unless the caller is the owner.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Otoh, this check is sub-optimal. files->count == 0 always means
|
|
|
ad8c96 |
it is safe to use it lockless even if files != current->files,
|
|
|
ad8c96 |
but put_files_struct() has to take rcu_read_lock(). See the next
|
|
|
ad8c96 |
patch.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
This patch removes the buggy checks and turns fcheck_files() into
|
|
|
ad8c96 |
__fcheck_files() which uses rcu_dereference_raw(), the "unshared"
|
|
|
ad8c96 |
callers, fget_light() and fget_raw_light(), can use it to avoid
|
|
|
ad8c96 |
the warning from RCU-lockdep.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fcheck_files() is trivially reimplemented as rcu_lockdep_assert()
|
|
|
ad8c96 |
plus __fcheck_files().
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
|
|
|
ad8c96 |
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit ec06bac02991edcfdeb148ab2fe7f3e2d7d3ceaa
|
|
|
ad8c96 |
Author: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
Date: Fri Jan 21 10:22:30 2022 +0100
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fs: factor out common code in fget() and fget_raw()
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2032478
|
|
|
ad8c96 |
Upstream status: Linus
|
|
|
ad8c96 |
Testing: xfstests
|
|
|
ad8c96 |
CVE: CVE-2021-4083
|
|
|
ad8c96 |
Conflicts:
|
|
|
ad8c96 |
- difference due to backport of later patch
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit 1deb46e2562561255c34075825fd00f22a858bb3
|
|
|
ad8c96 |
Author: Oleg Nesterov <oleg@redhat.com>
|
|
|
ad8c96 |
Date: Mon Jan 13 16:48:19 2014 +0100
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fs: factor out common code in fget() and fget_raw()
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Apart from FMODE_PATH check fget() and fget_raw() are identical,
|
|
|
ad8c96 |
shift the code into the new simple helper, __fget(fd, mask). Saves
|
|
|
ad8c96 |
160 bytes.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
|
|
|
ad8c96 |
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit ac43fab520f6836e2a7d3d20dd64d6328233ccbe
|
|
|
ad8c96 |
Author: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
Date: Fri Jan 21 10:22:30 2022 +0100
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fs: factor out common code in fget_light() and fget_raw_light()
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2032478
|
|
|
ad8c96 |
Upstream status: Linus
|
|
|
ad8c96 |
Testing: xfstests
|
|
|
ad8c96 |
CVE: CVE-2021-4083
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit ad46183445043b562856c60b74db664668fb364b
|
|
|
ad8c96 |
Author: Oleg Nesterov <oleg@redhat.com>
|
|
|
ad8c96 |
Date: Mon Jan 13 16:48:40 2014 +0100
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fs: factor out common code in fget_light() and fget_raw_light()
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Apart from FMODE_PATH check fget_light() and fget_raw_light() are
|
|
|
ad8c96 |
identical, shift the code into the new helper, __fget_light(fd, mask).
|
|
|
ad8c96 |
Saves 208 bytes.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
|
|
|
ad8c96 |
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit 9e24c8894f5df488a336f0c848f15a7d2f78d163
|
|
|
ad8c96 |
Author: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
Date: Fri Jan 21 10:22:30 2022 +0100
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fs: __fget_light() can use __fget() in slow path
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2032478
|
|
|
ad8c96 |
Upstream status: Linus
|
|
|
ad8c96 |
Testing: xfstests
|
|
|
ad8c96 |
CVE: CVE-2021-4083
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit e6ff9a9fa4e05c1c03dec63cdc6a87d6dea02755
|
|
|
ad8c96 |
Author: Oleg Nesterov <oleg@redhat.com>
|
|
|
ad8c96 |
Date: Mon Jan 13 16:49:06 2014 +0100
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fs: __fget_light() can use __fget() in slow path
|
|
|
ad8c96 |
|
|
|
ad8c96 |
The slow path in __fget_light() can use __fget() to avoid the
|
|
|
ad8c96 |
code duplication. Saves 232 bytes.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
|
|
|
ad8c96 |
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit d63fb584ae2d7d9a1620e23e59072cb6929f3833
|
|
|
ad8c96 |
Author: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
Date: Fri Jan 21 10:22:30 2022 +0100
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fs/file.c: __fget() and dup2() atomicity rules
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2032478
|
|
|
ad8c96 |
Upstream status: Linus
|
|
|
ad8c96 |
Testing: xfstests
|
|
|
ad8c96 |
CVE: CVE-2021-4083
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit 5ba97d2832f87943c43bb69cb1ef86dbc59df5bc
|
|
|
ad8c96 |
Author: Eric Dumazet <edumazet@google.com>
|
|
|
ad8c96 |
Date: Mon Jun 29 17:10:30 2015 +0200
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fs/file.c: __fget() and dup2() atomicity rules
|
|
|
ad8c96 |
|
|
|
ad8c96 |
__fget() does lockless fetch of pointer from the descriptor
|
|
|
ad8c96 |
table, attempts to grab a reference and treats "it was already
|
|
|
ad8c96 |
zero" as "it's already gone from the table, we just hadn't
|
|
|
ad8c96 |
seen the store, let's fail". Unfortunately, that breaks the
|
|
|
ad8c96 |
atomicity of dup2() - __fget() might see the old pointer,
|
|
|
ad8c96 |
notice that it's been already dropped and treat that as
|
|
|
ad8c96 |
"it's closed". What we should be getting is either the
|
|
|
ad8c96 |
old file or new one, depending whether we come before or after
|
|
|
ad8c96 |
dup2().
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Dmitry had following test failing sometimes :
|
|
|
ad8c96 |
|
|
|
ad8c96 |
int fd;
|
|
|
ad8c96 |
void *Thread(void *x) {
|
|
|
ad8c96 |
char buf;
|
|
|
ad8c96 |
int n = read(fd, &buf, 1);
|
|
|
ad8c96 |
if (n != 1)
|
|
|
ad8c96 |
exit(printf("read failed: n=%d errno=%d\n", n, errno));
|
|
|
ad8c96 |
return 0;
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
|
|
|
ad8c96 |
int main()
|
|
|
ad8c96 |
{
|
|
|
ad8c96 |
fd = open("/dev/urandom", O_RDONLY);
|
|
|
ad8c96 |
int fd2 = open("/dev/urandom", O_RDONLY);
|
|
|
ad8c96 |
if (fd == -1 || fd2 == -1)
|
|
|
ad8c96 |
exit(printf("open failed\n"));
|
|
|
ad8c96 |
pthread_t th;
|
|
|
ad8c96 |
pthread_create(&th, 0, Thread, 0);
|
|
|
ad8c96 |
if (dup2(fd2, fd) == -1)
|
|
|
ad8c96 |
exit(printf("dup2 failed\n"));
|
|
|
ad8c96 |
pthread_join(th, 0);
|
|
|
ad8c96 |
if (close(fd) == -1)
|
|
|
ad8c96 |
exit(printf("close failed\n"));
|
|
|
ad8c96 |
if (close(fd2) == -1)
|
|
|
ad8c96 |
exit(printf("close failed\n"));
|
|
|
ad8c96 |
printf("DONE\n");
|
|
|
ad8c96 |
return 0;
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Eric Dumazet <edumazet@google.com>
|
|
|
ad8c96 |
Reported-by: Dmitry Vyukov <dvyukov@google.com>
|
|
|
ad8c96 |
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit bc04a10c9303dcd9a6305a0452361537257fa0c1
|
|
|
ad8c96 |
Author: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
Date: Fri Jan 21 10:22:31 2022 +0100
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fget: check that the fd still exists after getting a ref to it
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2032478
|
|
|
ad8c96 |
Upstream status: Linus
|
|
|
ad8c96 |
Testing: xfstests
|
|
|
ad8c96 |
CVE: CVE-2021-4083
|
|
|
ad8c96 |
|
|
|
ad8c96 |
commit 054aa8d439b9185d4f5eb9a90282d1ce74772969
|
|
|
ad8c96 |
Author: Linus Torvalds <torvalds@linux-foundation.org>
|
|
|
ad8c96 |
Date: Wed Dec 1 10:06:14 2021 -0800
|
|
|
ad8c96 |
|
|
|
ad8c96 |
fget: check that the fd still exists after getting a ref to it
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Jann Horn points out that there is another possible race wrt Unix domain
|
|
|
ad8c96 |
socket garbage collection, somewhat reminiscent of the one fixed in
|
|
|
ad8c96 |
commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK").
|
|
|
ad8c96 |
|
|
|
ad8c96 |
See the extended comment about the garbage collection requirements added
|
|
|
ad8c96 |
to unix_peek_fds() by that commit for details.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
The race comes from how we can locklessly look up a file descriptor just
|
|
|
ad8c96 |
as it is in the process of being closed, and with the right artificial
|
|
|
ad8c96 |
timing (Jann added a few strategic 'mdelay(500)' calls to do that), the
|
|
|
ad8c96 |
Unix domain socket garbage collector could see the reference count
|
|
|
ad8c96 |
decrement of the close() happen before fget() took its reference to the
|
|
|
ad8c96 |
file and the file was attached onto a new file descriptor.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
This is all (intentionally) correct on the 'struct file *' side, with
|
|
|
ad8c96 |
RCU lookups and lockless reference counting very much part of the
|
|
|
ad8c96 |
design. Getting that reference count out of order isn't a problem per
|
|
|
ad8c96 |
se.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
But the garbage collector can get confused by seeing this situation of
|
|
|
ad8c96 |
having seen a file not having any remaining external references and then
|
|
|
ad8c96 |
seeing it being attached to an fd.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
In commit cbcf01128d0a ("af_unix: fix garbage collect vs MSG_PEEK") the
|
|
|
ad8c96 |
fix was to serialize the file descriptor install with the garbage
|
|
|
ad8c96 |
collector by taking and releasing the unix_gc_lock.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
That's not really an option here, but since this all happens when we are
|
|
|
ad8c96 |
in the process of looking up a file descriptor, we can instead simply
|
|
|
ad8c96 |
just re-check that the file hasn't been closed in the meantime, and just
|
|
|
ad8c96 |
re-do the lookup if we raced with a concurrent close() of the same file
|
|
|
ad8c96 |
descriptor.
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Reported-and-tested-by: Jann Horn <jannh@google.com>
|
|
|
ad8c96 |
Acked-by: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
|
|
|
ad8c96 |
|
|
|
ad8c96 |
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
|
|
|
ad8c96 |
---
|
|
|
ad8c96 |
fs/file.c | 86 +++++++++++++++--------------------------
|
|
|
ad8c96 |
include/linux/fdtable.h | 35 ++++++++++-------
|
|
|
ad8c96 |
2 files changed, 53 insertions(+), 68 deletions(-)
|
|
|
ad8c96 |
|
|
|
ad8c96 |
diff --git a/fs/file.c b/fs/file.c
|
|
|
ad8c96 |
index 44bd634b636a..564d60bf0fda 100644
|
|
|
ad8c96 |
--- a/fs/file.c
|
|
|
ad8c96 |
+++ b/fs/file.c
|
|
|
ad8c96 |
@@ -718,42 +718,43 @@ void do_close_on_exec(struct files_struct *files)
|
|
|
ad8c96 |
spin_unlock(&files->file_lock);
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
|
|
|
ad8c96 |
-struct file *fget(unsigned int fd)
|
|
|
ad8c96 |
+static struct file *__fget(unsigned int fd, fmode_t mask)
|
|
|
ad8c96 |
{
|
|
|
ad8c96 |
- struct file *file;
|
|
|
ad8c96 |
struct files_struct *files = current->files;
|
|
|
ad8c96 |
+ struct file *file;
|
|
|
ad8c96 |
|
|
|
ad8c96 |
rcu_read_lock();
|
|
|
ad8c96 |
+loop:
|
|
|
ad8c96 |
file = fcheck_files(files, fd);
|
|
|
ad8c96 |
if (file) {
|
|
|
ad8c96 |
- /* File object ref couldn't be taken */
|
|
|
ad8c96 |
- if (file->f_mode & FMODE_PATH || !get_file_rcu(file))
|
|
|
ad8c96 |
+ /* File object ref couldn't be taken.
|
|
|
ad8c96 |
+ * dup2() atomicity guarantee is the reason
|
|
|
ad8c96 |
+ * we loop to catch the new file (or NULL pointer)
|
|
|
ad8c96 |
+ */
|
|
|
ad8c96 |
+ if (file->f_mode & mask)
|
|
|
ad8c96 |
file = NULL;
|
|
|
ad8c96 |
+ else if (!get_file_rcu(file))
|
|
|
ad8c96 |
+ goto loop;
|
|
|
ad8c96 |
+ else if (fcheck_files(files, fd) != file) {
|
|
|
ad8c96 |
+ fput(file);
|
|
|
ad8c96 |
+ goto loop;
|
|
|
ad8c96 |
+ }
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
rcu_read_unlock();
|
|
|
ad8c96 |
|
|
|
ad8c96 |
return file;
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
|
|
|
ad8c96 |
+__attribute__((optimize("-fno-optimize-sibling-calls"))) struct file *fget(unsigned int fd)
|
|
|
ad8c96 |
+{
|
|
|
ad8c96 |
+ return __fget(fd, FMODE_PATH);
|
|
|
ad8c96 |
+}
|
|
|
ad8c96 |
EXPORT_SYMBOL(fget);
|
|
|
ad8c96 |
|
|
|
ad8c96 |
-struct file *fget_raw(unsigned int fd)
|
|
|
ad8c96 |
+__attribute__((optimize("-fno-optimize-sibling-calls"))) struct file *fget_raw(unsigned int fd)
|
|
|
ad8c96 |
{
|
|
|
ad8c96 |
- struct file *file;
|
|
|
ad8c96 |
- struct files_struct *files = current->files;
|
|
|
ad8c96 |
-
|
|
|
ad8c96 |
- rcu_read_lock();
|
|
|
ad8c96 |
- file = fcheck_files(files, fd);
|
|
|
ad8c96 |
- if (file) {
|
|
|
ad8c96 |
- /* File object ref couldn't be taken */
|
|
|
ad8c96 |
- if (!atomic_long_inc_not_zero(&file->f_count))
|
|
|
ad8c96 |
- file = NULL;
|
|
|
ad8c96 |
- }
|
|
|
ad8c96 |
- rcu_read_unlock();
|
|
|
ad8c96 |
-
|
|
|
ad8c96 |
- return file;
|
|
|
ad8c96 |
+ return __fget(fd, 0);
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
-
|
|
|
ad8c96 |
EXPORT_SYMBOL(fget_raw);
|
|
|
ad8c96 |
|
|
|
ad8c96 |
/*
|
|
|
ad8c96 |
@@ -772,56 +773,33 @@ EXPORT_SYMBOL(fget_raw);
|
|
|
ad8c96 |
* The fput_needed flag returned by fget_light should be passed to the
|
|
|
ad8c96 |
* corresponding fput_light.
|
|
|
ad8c96 |
*/
|
|
|
ad8c96 |
-struct file *fget_light(unsigned int fd, int *fput_needed)
|
|
|
ad8c96 |
+struct file *__fget_light(unsigned int fd, fmode_t mask, int *fput_needed)
|
|
|
ad8c96 |
{
|
|
|
ad8c96 |
- struct file *file;
|
|
|
ad8c96 |
struct files_struct *files = current->files;
|
|
|
ad8c96 |
+ struct file *file;
|
|
|
ad8c96 |
|
|
|
ad8c96 |
*fput_needed = 0;
|
|
|
ad8c96 |
if (atomic_read(&files->count) == 1) {
|
|
|
ad8c96 |
- file = fcheck_files(files, fd);
|
|
|
ad8c96 |
- if (file && (file->f_mode & FMODE_PATH))
|
|
|
ad8c96 |
+ file = __fcheck_files(files, fd);
|
|
|
ad8c96 |
+ if (file && (file->f_mode & mask))
|
|
|
ad8c96 |
file = NULL;
|
|
|
ad8c96 |
} else {
|
|
|
ad8c96 |
- rcu_read_lock();
|
|
|
ad8c96 |
- file = fcheck_files(files, fd);
|
|
|
ad8c96 |
- if (file) {
|
|
|
ad8c96 |
- if (!(file->f_mode & FMODE_PATH) &&
|
|
|
ad8c96 |
- atomic_long_inc_not_zero(&file->f_count))
|
|
|
ad8c96 |
- *fput_needed = 1;
|
|
|
ad8c96 |
- else
|
|
|
ad8c96 |
- /* Didn't get the reference, someone's freed */
|
|
|
ad8c96 |
- file = NULL;
|
|
|
ad8c96 |
- }
|
|
|
ad8c96 |
- rcu_read_unlock();
|
|
|
ad8c96 |
+ file = __fget(fd, mask);
|
|
|
ad8c96 |
+ if (file)
|
|
|
ad8c96 |
+ *fput_needed = 1;
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
|
|
|
ad8c96 |
return file;
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
+struct file *fget_light(unsigned int fd, int *fput_needed)
|
|
|
ad8c96 |
+{
|
|
|
ad8c96 |
+ return __fget_light(fd, FMODE_PATH, fput_needed);
|
|
|
ad8c96 |
+}
|
|
|
ad8c96 |
EXPORT_SYMBOL(fget_light);
|
|
|
ad8c96 |
|
|
|
ad8c96 |
struct file *fget_raw_light(unsigned int fd, int *fput_needed)
|
|
|
ad8c96 |
{
|
|
|
ad8c96 |
- struct file *file;
|
|
|
ad8c96 |
- struct files_struct *files = current->files;
|
|
|
ad8c96 |
-
|
|
|
ad8c96 |
- *fput_needed = 0;
|
|
|
ad8c96 |
- if (atomic_read(&files->count) == 1) {
|
|
|
ad8c96 |
- file = fcheck_files(files, fd);
|
|
|
ad8c96 |
- } else {
|
|
|
ad8c96 |
- rcu_read_lock();
|
|
|
ad8c96 |
- file = fcheck_files(files, fd);
|
|
|
ad8c96 |
- if (file) {
|
|
|
ad8c96 |
- if (atomic_long_inc_not_zero(&file->f_count))
|
|
|
ad8c96 |
- *fput_needed = 1;
|
|
|
ad8c96 |
- else
|
|
|
ad8c96 |
- /* Didn't get the reference, someone's freed */
|
|
|
ad8c96 |
- file = NULL;
|
|
|
ad8c96 |
- }
|
|
|
ad8c96 |
- rcu_read_unlock();
|
|
|
ad8c96 |
- }
|
|
|
ad8c96 |
-
|
|
|
ad8c96 |
- return file;
|
|
|
ad8c96 |
+ return __fget_light(fd, 0, fput_needed);
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
|
|
|
ad8c96 |
void set_close_on_exec(unsigned int fd, int flag)
|
|
|
ad8c96 |
diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h
|
|
|
ad8c96 |
index 88d74ca9418f..95bcca7c1a0f 100644
|
|
|
ad8c96 |
--- a/include/linux/fdtable.h
|
|
|
ad8c96 |
+++ b/include/linux/fdtable.h
|
|
|
ad8c96 |
@@ -70,29 +70,36 @@ struct files_struct {
|
|
|
ad8c96 |
RH_KABI_EXTEND(wait_queue_head_t resize_wait)
|
|
|
ad8c96 |
};
|
|
|
ad8c96 |
|
|
|
ad8c96 |
-#define rcu_dereference_check_fdtable(files, fdtfd) \
|
|
|
ad8c96 |
- (rcu_dereference_check((fdtfd), \
|
|
|
ad8c96 |
- lockdep_is_held(&(files)->file_lock) || \
|
|
|
ad8c96 |
- atomic_read(&(files)->count) == 1 || \
|
|
|
ad8c96 |
- rcu_my_thread_group_empty()))
|
|
|
ad8c96 |
-
|
|
|
ad8c96 |
-#define files_fdtable(files) \
|
|
|
ad8c96 |
- (rcu_dereference_check_fdtable((files), (files)->fdt))
|
|
|
ad8c96 |
-
|
|
|
ad8c96 |
struct file_operations;
|
|
|
ad8c96 |
struct vfsmount;
|
|
|
ad8c96 |
struct dentry;
|
|
|
ad8c96 |
|
|
|
ad8c96 |
-static inline struct file * fcheck_files(struct files_struct *files, unsigned int fd)
|
|
|
ad8c96 |
+#define rcu_dereference_check_fdtable(files, fdtfd) \
|
|
|
ad8c96 |
+ rcu_dereference_check((fdtfd), lockdep_is_held(&(files)->file_lock))
|
|
|
ad8c96 |
+
|
|
|
ad8c96 |
+#define files_fdtable(files) \
|
|
|
ad8c96 |
+ rcu_dereference_check_fdtable((files), (files)->fdt)
|
|
|
ad8c96 |
+
|
|
|
ad8c96 |
+/*
|
|
|
ad8c96 |
+ * The caller must ensure that fd table isn't shared or hold rcu or file lock
|
|
|
ad8c96 |
+ */
|
|
|
ad8c96 |
+static inline struct file *__fcheck_files(struct files_struct *files, unsigned int fd)
|
|
|
ad8c96 |
{
|
|
|
ad8c96 |
- struct file * file = NULL;
|
|
|
ad8c96 |
- struct fdtable *fdt = files_fdtable(files);
|
|
|
ad8c96 |
+ struct fdtable *fdt = rcu_dereference_raw(files->fdt);
|
|
|
ad8c96 |
|
|
|
ad8c96 |
if (fd < fdt->max_fds) {
|
|
|
ad8c96 |
fd = array_index_nospec(fd, fdt->max_fds);
|
|
|
ad8c96 |
- file = rcu_dereference_check_fdtable(files, fdt->fd[fd]);
|
|
|
ad8c96 |
+ return rcu_dereference_raw(fdt->fd[fd]);
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
- return file;
|
|
|
ad8c96 |
+ return NULL;
|
|
|
ad8c96 |
+}
|
|
|
ad8c96 |
+
|
|
|
ad8c96 |
+static inline struct file *fcheck_files(struct files_struct *files, unsigned int fd)
|
|
|
ad8c96 |
+{
|
|
|
ad8c96 |
+ rcu_lockdep_assert(rcu_read_lock_held() ||
|
|
|
ad8c96 |
+ lockdep_is_held(&files->file_lock),
|
|
|
ad8c96 |
+ "suspicious rcu_dereference_check() usage");
|
|
|
ad8c96 |
+ return __fcheck_files(files, fd);
|
|
|
ad8c96 |
}
|
|
|
ad8c96 |
|
|
|
ad8c96 |
/*
|
|
|
ad8c96 |
--
|
|
|
ad8c96 |
2.26.3
|
|
|
ad8c96 |
|
|
|
ad8c96 |
|