From: Jens Axboe axboe@kernel.dk
mainline inclusion from mainline-v5.12-rc1 commit 53dec2ea74f2ef360e8455439be96a780baa6097 category: bugfix bugzilla: 188963, https://gitee.com/src-openeuler/kernel/issues/I7GUAN CVE: NA
--------------------------------
Assumes current->files->file_lock is already held on invocation. Helps the caller check the file before removing the fd, if it needs to.
Signed-off-by: Jens Axboe axboe@kernel.dk
Conflicts: fs/file.c fs/internal.h
Signed-off-by: Li Nan linan122@huawei.com Reviewed-by: Yang Erkun yangerkun@huawei.com Reviewed-by: Wang Weiyang wangweiyang2@huawei.com Signed-off-by: Yongqiang Liu liuyongqiang13@huawei.com --- fs/file.c | 36 +++++++++++++++++++++++++----------- fs/internal.h | 1 + 2 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/fs/file.c b/fs/file.c index 7ada34a76742..b5df6680301c 100644 --- a/fs/file.c +++ b/fs/file.c @@ -20,6 +20,8 @@ #include <linux/rcupdate.h> #include <linux/filescontrol.h>
+#include "internal.h" + unsigned int sysctl_nr_open __read_mostly = 1024*1024; unsigned int sysctl_nr_open_min = BITS_PER_LONG; /* our min() is unusable in constant expressions ;-/ */ @@ -696,36 +698,48 @@ int __close_fd(struct files_struct *files, unsigned fd) EXPORT_SYMBOL(__close_fd); /* for ksys_close() */
/* - * variant of close_fd that gets a ref on the file for later fput. - * The caller must ensure that filp_close() called on the file, and then - * an fput(). + * See close_fd_get_file() below, this variant assumes current->files->file_lock + * is held. */ -int close_fd_get_file(unsigned int fd, struct file **res) +int __close_fd_get_file(unsigned int fd, struct file **res) { struct files_struct *files = current->files; struct file *file; struct fdtable *fdt;
- spin_lock(&files->file_lock); fdt = files_fdtable(files); if (fd >= fdt->max_fds) - goto out_unlock; + goto out_err; file = fdt->fd[fd]; if (!file) - goto out_unlock; + goto out_err; rcu_assign_pointer(fdt->fd[fd], NULL); __put_unused_fd(files, fd); - spin_unlock(&files->file_lock); get_file(file); *res = file; return 0; - -out_unlock: - spin_unlock(&files->file_lock); +out_err: *res = NULL; return -ENOENT; }
+/* + * variant of close_fd that gets a ref on the file for later fput. + * The caller must ensure that filp_close() called on the file, and then + * an fput(). + */ +int close_fd_get_file(unsigned int fd, struct file **res) +{ + struct files_struct *files = current->files; + int ret; + + spin_lock(&files->file_lock); + ret = __close_fd_get_file(fd, res); + spin_unlock(&files->file_lock); + + return ret; +} + void do_close_on_exec(struct files_struct *files) { unsigned i; diff --git a/fs/internal.h b/fs/internal.h index e63939e64439..a17f76e7651e 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -120,6 +120,7 @@ extern struct file *do_filp_open(int dfd, struct filename *pathname, extern struct file *do_file_open_root(struct dentry *, struct vfsmount *, const char *, const struct open_flags *); extern int build_open_flags(int flags, umode_t mode, struct open_flags *op); +extern int __close_fd_get_file(unsigned int fd, struct file **res);
long do_sys_ftruncate(unsigned int fd, loff_t length, int small); long do_faccessat(int dfd, const char __user *filename, int mode);