Signed-off-by: Christian Brauner --- include/linux/cleanup.h | 7 +++++ include/linux/file.h | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h index 19c7e475d3a4..b8bd2f15f91f 100644 --- a/include/linux/cleanup.h +++ b/include/linux/cleanup.h @@ -261,6 +261,10 @@ const volatile void * __must_check_fn(const volatile void *val) * CLASS(name, var)(args...): * declare the variable @var as an instance of the named class * + * CLASS_INIT(name, var, init_expr): + * declare the variable @var as an instance of the named class with + * custom initialization expression. + * * Ex. * * DEFINE_CLASS(fdget, struct fd, fdput(_T), fdget(fd), int fd) @@ -290,6 +294,9 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ class_##_name##_t var __cleanup(class_##_name##_destructor) = \ class_##_name##_constructor +#define CLASS_INIT(_name, _var, _init_expr) \ + class_##_name##_t _var __cleanup(class_##_name##_destructor) = (_init_expr) + #define __scoped_class(_name, var, _label, args...) \ for (CLASS(_name, var)(args); ; ({ goto _label; })) \ if (0) { \ diff --git a/include/linux/file.h b/include/linux/file.h index af1768d934a0..ec90bbf9eb40 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -127,4 +127,79 @@ extern void __fput_sync(struct file *); extern unsigned int sysctl_nr_open_min, sysctl_nr_open_max; +/* + * class_fd_prepare_t: Combined fd + file allocation cleanup class. + * + * Allocates an fd and a file together. On error paths, automatically cleans + * up whichever resource was successfully allocated. Allows flexible file + * allocation with different functions per usage. + */ +typedef struct { + int fd; + struct file *file; +} class_fd_prepare_t; + +#define fd_prepare_fd(_T) ((_T).fd) +#define fd_prepare_file(_T) ((_T).file) + +static inline void class_fd_prepare_destructor(class_fd_prepare_t *_T) +{ + if (unlikely(_T->fd >= 0)) { + put_unused_fd(_T->fd); + if (unlikely(!IS_ERR_OR_NULL(_T->file))) + fput(_T->file); + } +} + +static inline int class_fd_prepare_lock_err(class_fd_prepare_t *_T) +{ + if (IS_ERR(_T->file)) + return PTR_ERR(_T->file); + if (unlikely(!_T->file)) + return -ENOMEM; + return 0; +} + +/* + * __FD_PREPARE_INIT(fd_flags, file_init_expr): + * Helper to initialize fd_prepare class. + * @fd_flags: flags for get_unused_fd_flags() + * @file_init_expr: expression that returns struct file * + * + * Returns a struct fd_prepare with fd and file set. + * If fd allocation fails, file will be set to ERR_PTR with the error code. + * If fd succeeds but file_init_expr fails, file will contain the ERR_PTR. + * The file pointer is the single source of truth for error checking. + */ +#define __FD_PREPARE_INIT(_fd_flags, _file_init_owned) \ + ({ \ + class_fd_prepare_t _fd_prepare = { \ + .fd = get_unused_fd_flags((_fd_flags)), \ + }; \ + if (likely(_fd_prepare.fd >= 0)) \ + _fd_prepare.file = (_file_init_owned); \ + else \ + _fd_prepare.file = ERR_PTR(_fd_prepare.fd); \ + _fd_prepare; \ + }) + +/* + * FD_PREPARE(var, fd_flags, file_init_owned): + * Declares and initializes an fd_prepare variable with automatic cleanup. + * No separate scope required - cleanup happens when variable goes out of scope. + * + * @_var: name of struct fd_prepare variable to define + * @_fd_flags: flags for get_unused_fd_flags() + * @_file_init_owned: struct file to take ownership of (can be expression) + */ +#define FD_PREPARE(_var, _fd_flags, _file_init_owned) \ + CLASS_INIT(fd_prepare, _var, __FD_PREPARE_INIT(_fd_flags, _file_init_owned)) + +#define fd_publish(_fd_prepare) \ + ({ \ + class_fd_prepare_t *__p = &(_fd_prepare); \ + fd_install(__p->fd, __p->file); \ + take_fd(__p->fd); \ + }) + #endif /* __LINUX_FILE_H */ -- 2.47.3 Signed-off-by: Christian Brauner --- fs/anon_inodes.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index 180a458fc4f7..5b15547ca693 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c @@ -280,27 +280,14 @@ static int __anon_inode_getfd(const char *name, const struct inode *context_inode, bool make_inode) { - int error, fd; - struct file *file; + int error; - error = get_unused_fd_flags(flags); - if (error < 0) + FD_PREPARE(fdf, flags, __anon_inode_getfile(name, fops, priv, flags, + context_inode, make_inode)); + error = ACQUIRE_ERR(fd_prepare, &fdf); + if (error) return error; - fd = error; - - file = __anon_inode_getfile(name, fops, priv, flags, context_inode, - make_inode); - if (IS_ERR(file)) { - error = PTR_ERR(file); - goto err_put_unused_fd; - } - fd_install(fd, file); - - return fd; - -err_put_unused_fd: - put_unused_fd(fd); - return error; + return fd_publish(fdf); } /** -- 2.47.3 Signed-off-by: Christian Brauner --- fs/eventfd.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/fs/eventfd.c b/fs/eventfd.c index af42b2c7d235..089eff8894ea 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -378,9 +378,8 @@ EXPORT_SYMBOL_GPL(eventfd_ctx_fileget); static int do_eventfd(unsigned int count, int flags) { - struct eventfd_ctx *ctx; - struct file *file; - int fd; + struct eventfd_ctx *ctx __free(kfree) = NULL; + int err; /* Check the EFD_* constants for consistency. */ BUILD_BUG_ON(EFD_CLOEXEC != O_CLOEXEC); @@ -398,26 +397,20 @@ static int do_eventfd(unsigned int count, int flags) init_waitqueue_head(&ctx->wqh); ctx->count = count; ctx->flags = flags; - ctx->id = ida_alloc(&eventfd_ida, GFP_KERNEL); flags &= EFD_SHARED_FCNTL_FLAGS; flags |= O_RDWR; - fd = get_unused_fd_flags(flags); - if (fd < 0) - goto err; - - file = anon_inode_getfile_fmode("[eventfd]", &eventfd_fops, - ctx, flags, FMODE_NOWAIT); - if (IS_ERR(file)) { - put_unused_fd(fd); - fd = PTR_ERR(file); - goto err; - } - fd_install(fd, file); - return fd; -err: - eventfd_free_ctx(ctx); - return fd; + + FD_PREPARE(fdf, flags, + anon_inode_getfile_fmode("[eventfd]", &eventfd_fops, ctx, + flags, FMODE_NOWAIT)); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) + return err; + + ctx->id = ida_alloc(&eventfd_ida, GFP_KERNEL); + retain_and_null_ptr(ctx); + return fd_publish(fdf); } SYSCALL_DEFINE2(eventfd2, unsigned int, count, int, flags) -- 2.47.3 Signed-off-by: Christian Brauner --- fs/fhandle.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/fs/fhandle.c b/fs/fhandle.c index 052f9c9368fb..e3d6253b1ebd 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c @@ -404,32 +404,33 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh, return retval; } +static struct file *file_open_handle(struct path *path, int open_flag) +{ + const struct export_operations *eops; + + eops = path->mnt->mnt_sb->s_export_op; + if (eops->open) + return eops->open(path, open_flag); + + return file_open_root(path, "", open_flag, 0); +} + static long do_handle_open(int mountdirfd, struct file_handle __user *ufh, int open_flag) { long retval = 0; struct path path __free(path_put) = {}; - struct file *file; - const struct export_operations *eops; retval = handle_to_path(mountdirfd, ufh, &path, open_flag); if (retval) return retval; - CLASS(get_unused_fd, fd)(open_flag); - if (fd < 0) - return fd; - - eops = path.mnt->mnt_sb->s_export_op; - if (eops->open) - file = eops->open(&path, open_flag); - else - file = file_open_root(&path, "", open_flag, 0); - if (IS_ERR(file)) - return PTR_ERR(file); + FD_PREPARE(fdf, open_flag, file_open_handle(&path, open_flag)); + retval = ACQUIRE_ERR(fd_prepare, &fdf); + if (retval) + return retval; - fd_install(fd, file); - return take_fd(fd); + return fd_publish(fdf); } /** -- 2.47.3 Signed-off-by: Christian Brauner --- fs/namespace.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index d82910f33dc4..cff0fdc27fda 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3103,19 +3103,14 @@ static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned SYSCALL_DEFINE3(open_tree, int, dfd, const char __user *, filename, unsigned, flags) { - int fd; - struct file *file __free(fput) = NULL; - - file = vfs_open_tree(dfd, filename, flags); - if (IS_ERR(file)) - return PTR_ERR(file); + int ret; - fd = get_unused_fd_flags(flags & O_CLOEXEC); - if (fd < 0) - return fd; + FD_PREPARE(fdf, flags, vfs_open_tree(dfd, filename, flags)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; - fd_install(fd, no_free_ptr(file)); - return fd; + return fd_publish(fdf); } /* -- 2.47.3 Signed-off-by: Christian Brauner --- fs/namespace.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index cff0fdc27fda..74157dd471ee 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -5030,19 +5030,19 @@ SYSCALL_DEFINE5(open_tree_attr, int, dfd, const char __user *, filename, unsigned, flags, struct mount_attr __user *, uattr, size_t, usize) { - struct file __free(fput) *file = NULL; - int fd; + int ret; if (!uattr && usize) return -EINVAL; - file = vfs_open_tree(dfd, filename, flags); - if (IS_ERR(file)) - return PTR_ERR(file); + FD_PREPARE(fdf, flags, vfs_open_tree(dfd, filename, flags)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; if (uattr) { - int ret; struct mount_kattr kattr = {}; + struct file *file = fd_prepare_file(fdf); if (flags & OPEN_TREE_CLONE) kattr.kflags = MOUNT_KATTR_IDMAP_REPLACE; @@ -5058,12 +5058,7 @@ SYSCALL_DEFINE5(open_tree_attr, int, dfd, const char __user *, filename, return ret; } - fd = get_unused_fd_flags(flags & O_CLOEXEC); - if (fd < 0) - return fd; - - fd_install(fd, no_free_ptr(file)); - return fd; + return fd_publish(fdf); } int show_path(struct seq_file *m, struct dentry *root) -- 2.47.3 Signed-off-by: Christian Brauner --- fs/namespace.c | 56 ++++++++++++++++++++++---------------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 74157dd471ee..8716aedada2c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -4280,8 +4280,7 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, { struct mnt_namespace *ns; struct fs_context *fc; - struct file *file; - struct path newmount; + struct path newmount __free(path_put) = {}; struct mount *mnt; unsigned int mnt_flags = 0; long ret; @@ -4319,33 +4318,32 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, fc = fd_file(f)->private_data; - ret = mutex_lock_interruptible(&fc->uapi_mutex); - if (ret < 0) + ACQUIRE(mutex_intr, uapi_mutex)(&fc->uapi_mutex); + ret = ACQUIRE_ERR(mutex_intr, &uapi_mutex); + if (ret) return ret; /* There must be a valid superblock or we can't mount it */ ret = -EINVAL; if (!fc->root) - goto err_unlock; + return ret; ret = -EPERM; if (mount_too_revealing(fc->root->d_sb, &mnt_flags)) { errorfcp(fc, "VFS", "Mount too revealing"); - goto err_unlock; + return ret; } ret = -EBUSY; if (fc->phase != FS_CONTEXT_AWAITING_MOUNT) - goto err_unlock; + return ret; if (fc->sb_flags & SB_MANDLOCK) warn_mandlock(); newmount.mnt = vfs_create_mount(fc); - if (IS_ERR(newmount.mnt)) { - ret = PTR_ERR(newmount.mnt); - goto err_unlock; - } + if (IS_ERR(newmount.mnt)) + return PTR_ERR(newmount.mnt); newmount.dentry = dget(fc->root); newmount.mnt->mnt_flags = mnt_flags; @@ -4357,38 +4355,28 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, vfs_clean_context(fc); ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true); - if (IS_ERR(ns)) { - ret = PTR_ERR(ns); - goto err_path; - } + if (IS_ERR(ns)) + return PTR_ERR(ns); mnt = real_mount(newmount.mnt); ns->root = mnt; ns->nr_mounts = 1; mnt_add_to_ns(ns, mnt); mntget(newmount.mnt); - /* Attach to an apparent O_PATH fd with a note that we need to unmount - * it, not just simply put it. - */ - file = dentry_open(&newmount, O_PATH, fc->cred); - if (IS_ERR(file)) { + FD_PREPARE(fdf, (flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0, + dentry_open(&newmount, O_PATH, fc->cred)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) { dissolve_on_fput(newmount.mnt); - ret = PTR_ERR(file); - goto err_path; + return ret; } - file->f_mode |= FMODE_NEED_UNMOUNT; - - ret = get_unused_fd_flags((flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0); - if (ret >= 0) - fd_install(ret, file); - else - fput(file); -err_path: - path_put(&newmount); -err_unlock: - mutex_unlock(&fc->uapi_mutex); - return ret; + /* + * Attach to an apparent O_PATH fd with a note that we + * need to unmount it, not just simply put it. + */ + fd_prepare_file(fdf)->f_mode |= FMODE_NEED_UNMOUNT; + return fd_publish(fdf); } static inline int vfs_move_mount(const struct path *from_path, -- 2.47.3 Signed-off-by: Christian Brauner --- fs/notify/fanotify/fanotify_user.c | 63 +++++++++++++++----------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 1dadda82cae5..80166aded1da 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1597,16 +1597,20 @@ static struct hlist_head *fanotify_alloc_merge_hash(void) return hash; } +DEFINE_CLASS(fsnotify_group, + struct fsnotify_group *, + if (_T) fsnotify_destroy_group(_T), + fsnotify_alloc_group(ops, flags), + const struct fsnotify_ops *ops, int flags) + /* fanotify syscalls */ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) { struct user_namespace *user_ns = current_user_ns(); - struct fsnotify_group *group; int f_flags, fd; unsigned int fid_mode = flags & FANOTIFY_FID_BITS; unsigned int class = flags & FANOTIFY_CLASS_BITS; unsigned int internal_flags = 0; - struct file *file; pr_debug("%s: flags=%x event_f_flags=%x\n", __func__, flags, event_f_flags); @@ -1690,36 +1694,29 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) if (flags & FAN_NONBLOCK) f_flags |= O_NONBLOCK; - /* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */ - group = fsnotify_alloc_group(&fanotify_fsnotify_ops, + CLASS(fsnotify_group, group)(&fanotify_fsnotify_ops, FSNOTIFY_GROUP_USER); - if (IS_ERR(group)) { + /* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */ + if (IS_ERR(group)) return PTR_ERR(group); - } /* Enforce groups limits per user in all containing user ns */ group->fanotify_data.ucounts = inc_ucount(user_ns, current_euid(), UCOUNT_FANOTIFY_GROUPS); - if (!group->fanotify_data.ucounts) { - fd = -EMFILE; - goto out_destroy_group; - } + if (!group->fanotify_data.ucounts) + return -EMFILE; group->fanotify_data.flags = flags | internal_flags; group->memcg = get_mem_cgroup_from_mm(current->mm); group->user_ns = get_user_ns(user_ns); group->fanotify_data.merge_hash = fanotify_alloc_merge_hash(); - if (!group->fanotify_data.merge_hash) { - fd = -ENOMEM; - goto out_destroy_group; - } + if (!group->fanotify_data.merge_hash) + return -ENOMEM; group->overflow_event = fanotify_alloc_overflow_event(); - if (unlikely(!group->overflow_event)) { - fd = -ENOMEM; - goto out_destroy_group; - } + if (unlikely(!group->overflow_event)) + return -ENOMEM; if (force_o_largefile()) event_f_flags |= O_LARGEFILE; @@ -1738,8 +1735,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) group->priority = FSNOTIFY_PRIO_PRE_CONTENT; break; default: - fd = -EINVAL; - goto out_destroy_group; + return -EINVAL; } BUILD_BUG_ON(!(FANOTIFY_ADMIN_INIT_FLAGS & FAN_UNLIMITED_QUEUE)); @@ -1750,28 +1746,19 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) } if (flags & FAN_ENABLE_AUDIT) { - fd = -EPERM; if (!capable(CAP_AUDIT_WRITE)) - goto out_destroy_group; + return -EPERM; } - fd = get_unused_fd_flags(f_flags); - if (fd < 0) - goto out_destroy_group; + FD_PREPARE(fdf, f_flags, + anon_inode_getfile_fmode("[fanotify]", &fanotify_fops, group, + f_flags, FMODE_NONOTIFY)); + fd = ACQUIRE_ERR(fd_prepare, &fdf); + if (fd) + return fd; - file = anon_inode_getfile_fmode("[fanotify]", &fanotify_fops, group, - f_flags, FMODE_NONOTIFY); - if (IS_ERR(file)) { - put_unused_fd(fd); - fd = PTR_ERR(file); - goto out_destroy_group; - } - fd_install(fd, file); - return fd; - -out_destroy_group: - fsnotify_destroy_group(group); - return fd; + retain_and_null_ptr(group); + return fd_publish(fdf); } static int fanotify_test_fsid(struct dentry *dentry, unsigned int flags, -- 2.47.3 Signed-off-by: Christian Brauner --- fs/nsfs.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/fs/nsfs.c b/fs/nsfs.c index 79b026a36fb6..61102cc63e1d 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -108,7 +108,6 @@ int ns_get_path(struct path *path, struct task_struct *task, int open_namespace(struct ns_common *ns) { struct path path __free(path_put) = {}; - struct file *f; int err; /* call first to consume reference */ @@ -116,16 +115,11 @@ int open_namespace(struct ns_common *ns) if (err < 0) return err; - CLASS(get_unused_fd, fd)(O_CLOEXEC); - if (fd < 0) - return fd; - - f = dentry_open(&path, O_RDONLY, current_cred()); - if (IS_ERR(f)) - return PTR_ERR(f); - - fd_install(fd, f); - return take_fd(fd); + FD_PREPARE(fdf, O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred())); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) + return err; + return fd_publish(fdf); } int open_related_ns(struct ns_common *ns, -- 2.47.3 Signed-off-by: Christian Brauner --- fs/nsfs.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/fs/nsfs.c b/fs/nsfs.c index 61102cc63e1d..d4f115c4b11d 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -324,28 +324,19 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl, if (ret) return ret; - CLASS(get_unused_fd, fd)(O_CLOEXEC); - if (fd < 0) - return fd; - - f = dentry_open(&path, O_RDONLY, current_cred()); - if (IS_ERR(f)) - return PTR_ERR(f); - - if (uinfo) { - /* - * If @uinfo is passed return all information about the - * mount namespace as well. - */ - ret = copy_ns_info_to_user(to_mnt_ns(ns), uinfo, usize, &kinfo); - if (ret) - return ret; - } - - /* Transfer reference of @f to caller's fdtable. */ - fd_install(fd, no_free_ptr(f)); - /* File descriptor is live so hand it off to the caller. */ - return take_fd(fd); + FD_PREPARE(fdf, O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred())); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; + /* + * If @uinfo is passed return all information about the + * mount namespace as well. + */ + ret = copy_ns_info_to_user(to_mnt_ns(ns), uinfo, usize, &kinfo); + if (ret) + return ret; + ret = fd_publish(fdf); + break; } default: ret = -ENOTTY; -- 2.47.3 Signed-off-by: Christian Brauner --- fs/autofs/dev-ioctl.c | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/fs/autofs/dev-ioctl.c b/fs/autofs/dev-ioctl.c index d8dd150cbd74..11a706af13af 100644 --- a/fs/autofs/dev-ioctl.c +++ b/fs/autofs/dev-ioctl.c @@ -231,32 +231,18 @@ static int test_by_type(const struct path *path, void *p) */ static int autofs_dev_ioctl_open_mountpoint(const char *name, dev_t devid) { - int err, fd; - - fd = get_unused_fd_flags(O_CLOEXEC); - if (likely(fd >= 0)) { - struct file *filp; - struct path path; - - err = find_autofs_mount(name, &path, test_by_dev, &devid); - if (err) - goto out; - - filp = dentry_open(&path, O_RDONLY, current_cred()); - path_put(&path); - if (IS_ERR(filp)) { - err = PTR_ERR(filp); - goto out; - } - - fd_install(fd, filp); - } + struct path path __free(path_put) = {}; + int err; - return fd; + err = find_autofs_mount(name, &path, test_by_dev, &devid); + if (err) + return err; -out: - put_unused_fd(fd); - return err; + FD_PREPARE(fdf, O_CLOEXEC, dentry_open(&path, O_RDONLY, current_cred())); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) + return err; + return fd_publish(fdf); } /* Open a file descriptor on an autofs mount point */ -- 2.47.3 Signed-off-by: Christian Brauner --- fs/eventpoll.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index ee7c4b683ec3..f694252d9614 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -2165,9 +2165,8 @@ static void clear_tfile_check_list(void) */ static int do_epoll_create(int flags) { - int error, fd; - struct eventpoll *ep = NULL; - struct file *file; + int error; + struct eventpoll *ep; /* Check the EPOLL_* constant for consistency. */ BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC); @@ -2184,26 +2183,16 @@ static int do_epoll_create(int flags) * Creates all the items needed to setup an eventpoll file. That is, * a file structure and a free file descriptor. */ - fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC)); - if (fd < 0) { - error = fd; - goto out_free_ep; - } - file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep, - O_RDWR | (flags & O_CLOEXEC)); - if (IS_ERR(file)) { - error = PTR_ERR(file); - goto out_free_fd; + FD_PREPARE(fdf, O_RDWR | (flags & O_CLOEXEC), + anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep, + O_RDWR | (flags & O_CLOEXEC))); + error = ACQUIRE_ERR(fd_prepare, &fdf); + if (error) { + ep_clear_and_put(ep); + return error; } - ep->file = file; - fd_install(fd, file); - return fd; - -out_free_fd: - put_unused_fd(fd); -out_free_ep: - ep_clear_and_put(ep); - return error; + ep->file = fd_prepare_file(fdf); + return fd_publish(fdf); } SYSCALL_DEFINE1(epoll_create1, int, flags) -- 2.47.3 Signed-off-by: Christian Brauner --- fs/open.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/fs/open.c b/fs/open.c index 3d64372ecc67..5b86cea07a89 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1421,8 +1421,8 @@ static int do_sys_openat2(int dfd, const char __user *filename, struct open_how *how) { struct open_flags op; - struct filename *tmp; - int err, fd; + struct filename *tmp __free(putname); + int err; err = build_open_flags(how, &op); if (unlikely(err)) @@ -1432,18 +1432,11 @@ static int do_sys_openat2(int dfd, const char __user *filename, if (IS_ERR(tmp)) return PTR_ERR(tmp); - fd = get_unused_fd_flags(how->flags); - if (likely(fd >= 0)) { - struct file *f = do_filp_open(dfd, tmp, &op); - if (IS_ERR(f)) { - put_unused_fd(fd); - fd = PTR_ERR(f); - } else { - fd_install(fd, f); - } - } - putname(tmp); - return fd; + FD_PREPARE(fdf, how->flags, do_filp_open(dfd, tmp, &op)); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) + return err; + return fd_publish(fdf); } int do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) -- 2.47.3 Signed-off-by: Christian Brauner --- fs/signalfd.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/fs/signalfd.c b/fs/signalfd.c index d469782f97f4..8f1ee3c85f8d 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -250,8 +250,6 @@ static const struct file_operations signalfd_fops = { static int do_signalfd4(int ufd, sigset_t *mask, int flags) { - struct signalfd_ctx *ctx; - /* Check the SFD_* constants for consistency. */ BUILD_BUG_ON(SFD_CLOEXEC != O_CLOEXEC); BUILD_BUG_ON(SFD_NONBLOCK != O_NONBLOCK); @@ -263,7 +261,8 @@ static int do_signalfd4(int ufd, sigset_t *mask, int flags) signotset(mask); if (ufd == -1) { - struct file *file; + int err; + struct signalfd_ctx *ctx __free(kfree) = NULL; ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -271,22 +270,18 @@ static int do_signalfd4(int ufd, sigset_t *mask, int flags) ctx->sigmask = *mask; - ufd = get_unused_fd_flags(flags & O_CLOEXEC); - if (ufd < 0) { - kfree(ctx); - return ufd; - } + FD_PREPARE(fdf, flags & O_CLOEXEC, + anon_inode_getfile_fmode("[signalfd]", &signalfd_fops, ctx, + O_RDWR | (flags & O_NONBLOCK), FMODE_NOWAIT)); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) + return err; - file = anon_inode_getfile_fmode("[signalfd]", &signalfd_fops, - ctx, O_RDWR | (flags & O_NONBLOCK), - FMODE_NOWAIT); - if (IS_ERR(file)) { - put_unused_fd(ufd); - kfree(ctx); - return PTR_ERR(file); - } - fd_install(ufd, file); + retain_and_null_ptr(ctx); + return fd_publish(fdf); } else { + struct signalfd_ctx *ctx; + CLASS(fd, f)(ufd); if (fd_empty(f)) return -EBADF; -- 2.47.3 Signed-off-by: Christian Brauner --- fs/timerfd.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/fs/timerfd.c b/fs/timerfd.c index c68f28d9c426..2ae31663cc63 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -393,9 +393,8 @@ static const struct file_operations timerfd_fops = { SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) { - int ufd; - struct timerfd_ctx *ctx; - struct file *file; + struct timerfd_ctx *ctx __free(kfree) = NULL; + int ret; /* Check the TFD_* constants for consistency. */ BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC); @@ -432,23 +431,16 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) ctx->moffs = ktime_mono_to_real(0); - ufd = get_unused_fd_flags(flags & TFD_SHARED_FCNTL_FLAGS); - if (ufd < 0) { - kfree(ctx); - return ufd; - } - - file = anon_inode_getfile_fmode("[timerfd]", &timerfd_fops, ctx, - O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS), - FMODE_NOWAIT); - if (IS_ERR(file)) { - put_unused_fd(ufd); - kfree(ctx); - return PTR_ERR(file); - } + FD_PREPARE(fdf, flags & TFD_SHARED_FCNTL_FLAGS, + anon_inode_getfile_fmode("[timerfd]", &timerfd_fops, ctx, + O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS), + FMODE_NOWAIT)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; - fd_install(ufd, file); - return ufd; + retain_and_null_ptr(ctx); + return fd_publish(fdf); } static int do_timerfd_settime(int ufd, int flags, -- 2.47.3 Signed-off-by: Christian Brauner --- fs/userfaultfd.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index 54c6cc7fe9c6..6388a525cdb1 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c @@ -2111,9 +2111,8 @@ static void init_once_userfaultfd_ctx(void *mem) static int new_userfaultfd(int flags) { - struct userfaultfd_ctx *ctx; - struct file *file; - int fd; + int ret; + struct userfaultfd_ctx *ctx __free(kfree) = NULL; VM_WARN_ON_ONCE(!current->mm); @@ -2135,26 +2134,19 @@ static int new_userfaultfd(int flags) atomic_set(&ctx->mmap_changing, 0); ctx->mm = current->mm; - fd = get_unused_fd_flags(flags & UFFD_SHARED_FCNTL_FLAGS); - if (fd < 0) - goto err_out; + FD_PREPARE(fdf, flags & UFFD_SHARED_FCNTL_FLAGS, + anon_inode_create_getfile("[userfaultfd]", &userfaultfd_fops, ctx, + O_RDONLY | (flags & UFFD_SHARED_FCNTL_FLAGS), + NULL)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; - /* Create a new inode so that the LSM can block the creation. */ - file = anon_inode_create_getfile("[userfaultfd]", &userfaultfd_fops, ctx, - O_RDONLY | (flags & UFFD_SHARED_FCNTL_FLAGS), NULL); - if (IS_ERR(file)) { - put_unused_fd(fd); - fd = PTR_ERR(file); - goto err_out; - } /* prevent the mm struct to be freed */ mmgrab(ctx->mm); - file->f_mode |= FMODE_NOWAIT; - fd_install(fd, file); - return fd; -err_out: - kmem_cache_free(userfaultfd_ctx_cachep, ctx); - return fd; + fd_prepare_file(fdf)->f_mode |= FMODE_NOWAIT; + retain_and_null_ptr(ctx); + return fd_publish(fdf); } static inline bool userfaultfd_syscall_allowed(int flags) -- 2.47.3 Signed-off-by: Christian Brauner --- fs/xfs/xfs_handle.c | 53 +++++++++++++++++------------------------------------ 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/fs/xfs/xfs_handle.c b/fs/xfs/xfs_handle.c index f19fce557354..3c1895f7009d 100644 --- a/fs/xfs/xfs_handle.c +++ b/fs/xfs/xfs_handle.c @@ -234,13 +234,11 @@ xfs_open_by_handle( { const struct cred *cred = current_cred(); int error; - int fd; int permflag; - struct file *filp; struct inode *inode; struct dentry *dentry; fmode_t fmode; - struct path path; + struct path path __free(path_put) = {}; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -252,8 +250,7 @@ xfs_open_by_handle( /* Restrict xfs_open_by_handle to directories & regular files. */ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) { - error = -EPERM; - goto out_dput; + return -EPERM; } #if BITS_PER_LONG != 32 @@ -263,48 +260,32 @@ xfs_open_by_handle( permflag = hreq->oflags; fmode = OPEN_FMODE(permflag); if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) && - (fmode & FMODE_WRITE) && IS_APPEND(inode)) { - error = -EPERM; - goto out_dput; - } + (fmode & FMODE_WRITE) && IS_APPEND(inode)) + return -EPERM; - if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) { - error = -EPERM; - goto out_dput; - } + if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) + return -EPERM; /* Can't write directories. */ - if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) { - error = -EISDIR; - goto out_dput; - } - - fd = get_unused_fd_flags(0); - if (fd < 0) { - error = fd; - goto out_dput; - } + if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) + return -EISDIR; - path.mnt = parfilp->f_path.mnt; + path.mnt = mntget(parfilp->f_path.mnt); path.dentry = dentry; - filp = dentry_open(&path, hreq->oflags, cred); - dput(dentry); - if (IS_ERR(filp)) { - put_unused_fd(fd); - return PTR_ERR(filp); - } + + FD_PREPARE(fdf, 0, dentry_open(&path, hreq->oflags, cred)); + error = ACQUIRE_ERR(fd_prepare, &fdf); + if (error) + return error; if (S_ISREG(inode->i_mode)) { + struct file *filp = fd_prepare_file(fdf); + filp->f_flags |= O_NOATIME; filp->f_mode |= FMODE_NOCMTIME; } - fd_install(fd, filp); - return fd; - - out_dput: - dput(dentry); - return error; + return fd_publish(fdf); } int -- 2.47.3 Signed-off-by: Christian Brauner --- drivers/dma-buf/dma-buf.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 2bcf9ceca997..e46d8719d61b 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -768,18 +768,16 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_export, "DMA_BUF"); */ int dma_buf_fd(struct dma_buf *dmabuf, int flags) { - int fd; + int ret; if (!dmabuf || !dmabuf->file) return -EINVAL; - fd = get_unused_fd_flags(flags); - if (fd < 0) - return fd; - - fd_install(fd, dmabuf->file); - - return fd; + FD_PREPARE(fdf, flags, dmabuf->file); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; + return fd_publish(fdf); } EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF"); -- 2.47.3 Signed-off-by: Christian Brauner --- net/unix/af_unix.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 68c94f49f7b5..80dd12b6b441 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -3277,8 +3277,7 @@ EXPORT_SYMBOL_GPL(unix_outq_len); static int unix_open_file(struct sock *sk) { - struct file *f; - int fd; + int ret; if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return -EPERM; @@ -3289,18 +3288,11 @@ static int unix_open_file(struct sock *sk) if (!unix_sk(sk)->path.dentry) return -ENOENT; - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) - return fd; - - f = dentry_open(&unix_sk(sk)->path, O_PATH, current_cred()); - if (IS_ERR(f)) { - put_unused_fd(fd); - return PTR_ERR(f); - } - - fd_install(fd, f); - return fd; + FD_PREPARE(fdf, O_CLOEXEC, dentry_open(&unix_sk(sk)->path, O_PATH, current_cred())); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; + return fd_publish(fdf); } static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -- 2.47.3 Signed-off-by: Christian Brauner --- drivers/dma-buf/sync_file.c | 54 ++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 747e377fb954..842db95efddb 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -213,56 +213,40 @@ static __poll_t sync_file_poll(struct file *file, poll_table *wait) static long sync_file_ioctl_merge(struct sync_file *sync_file, unsigned long arg) { - int fd = get_unused_fd_flags(O_CLOEXEC); - int err; struct sync_file *fence2, *fence3; struct sync_merge_data data; + int err; - if (fd < 0) - return fd; - - if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { - err = -EFAULT; - goto err_put_fd; - } + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; - if (data.flags || data.pad) { - err = -EINVAL; - goto err_put_fd; - } + if (data.flags || data.pad) + return -EINVAL; fence2 = sync_file_fdget(data.fd2); - if (!fence2) { - err = -ENOENT; - goto err_put_fd; - } + if (!fence2) + return -ENOENT; data.name[sizeof(data.name) - 1] = '\0'; fence3 = sync_file_merge(data.name, sync_file, fence2); if (!fence3) { - err = -ENOMEM; - goto err_put_fence2; - } - - data.fence = fd; - if (copy_to_user((void __user *)arg, &data, sizeof(data))) { - err = -EFAULT; - goto err_put_fence3; + fput(fence2->file); + return -ENOMEM; } - fd_install(fd, fence3->file); + FD_PREPARE(fdf, O_CLOEXEC, fence3->file); fput(fence2->file); - return 0; - -err_put_fence3: - fput(fence3->file); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) { + fput(fence3->file); + return err; + } -err_put_fence2: - fput(fence2->file); + data.fence = fd_prepare_fd(fdf); + if (copy_to_user((void __user *)arg, &data, sizeof(data))) + return -EFAULT; -err_put_fd: - put_unused_fd(fd); - return err; + return fd_publish(fdf); } static int sync_fill_fence_info(struct dma_fence *fence, -- 2.47.3 Signed-off-by: Christian Brauner --- fs/exec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 4298e7e08d5d..b19bdcd6fd1e 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1280,12 +1280,12 @@ int begin_new_exec(struct linux_binprm * bprm) /* Pass the opened binary to the interpreter. */ if (bprm->have_execfd) { - retval = get_unused_fd_flags(0); - if (retval < 0) + FD_PREPARE(fdf, 0, bprm->executable); + retval = ACQUIRE_ERR(fd_prepare, &fdf); + if (retval) goto out_unlock; - fd_install(retval, bprm->executable); bprm->executable = NULL; - bprm->execfd = retval; + bprm->execfd = fd_publish(fdf); } return 0; -- 2.47.3 Signed-off-by: Christian Brauner --- ipc/mqueue.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 093551fe66a7..e9890736ba19 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -899,7 +899,7 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, struct dentry *root = mnt->mnt_root; struct filename *name; struct path path; - int fd, error; + int ret; int ro; audit_mq_open(oflag, mode, attr); @@ -908,38 +908,30 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, if (IS_ERR(name)) return PTR_ERR(name); - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) - goto out_putname; - ro = mnt_want_write(mnt); /* we'll drop it in any case */ inode_lock(d_inode(root)); path.dentry = lookup_noperm(&QSTR(name->name), root); if (IS_ERR(path.dentry)) { - error = PTR_ERR(path.dentry); - goto out_putfd; + ret = PTR_ERR(path.dentry); + goto out_unlock; } path.mnt = mntget(mnt); - error = prepare_open(path.dentry, oflag, ro, mode, name, attr); - if (!error) { - struct file *file = dentry_open(&path, oflag, current_cred()); - if (!IS_ERR(file)) - fd_install(fd, file); - else - error = PTR_ERR(file); + ret = prepare_open(path.dentry, oflag, ro, mode, name, attr); + if (!ret) { + FD_PREPARE(fdf, O_CLOEXEC, + dentry_open(&path, oflag, current_cred())); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (!ret) + ret = fd_publish(fdf); } path_put(&path); -out_putfd: - if (error) { - put_unused_fd(fd); - fd = error; - } + +out_unlock: inode_unlock(d_inode(root)); if (!ro) mnt_drop_write(mnt); -out_putname: putname(name); - return fd; + return ret; } SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, -- 2.47.3 Signed-off-by: Christian Brauner --- kernel/bpf/bpf_iter.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/kernel/bpf/bpf_iter.c b/kernel/bpf/bpf_iter.c index 6ac35430c573..60f9ca339a6b 100644 --- a/kernel/bpf/bpf_iter.c +++ b/kernel/bpf/bpf_iter.c @@ -634,37 +634,25 @@ static int prepare_seq_file(struct file *file, struct bpf_iter_link *link) int bpf_iter_new_fd(struct bpf_link *link) { struct bpf_iter_link *iter_link; - struct file *file; unsigned int flags; - int err, fd; + int err; if (link->ops != &bpf_iter_link_lops) return -EINVAL; flags = O_RDONLY | O_CLOEXEC; - fd = get_unused_fd_flags(flags); - if (fd < 0) - return fd; - - file = anon_inode_getfile("bpf_iter", &bpf_iter_fops, NULL, flags); - if (IS_ERR(file)) { - err = PTR_ERR(file); - goto free_fd; - } - iter_link = container_of(link, struct bpf_iter_link, link); - err = prepare_seq_file(file, iter_link); + FD_PREPARE(fdf, flags, anon_inode_getfile("bpf_iter", &bpf_iter_fops, NULL, flags)); + err = ACQUIRE_ERR(fd_prepare, &fdf); if (err) - goto free_file; + return err; - fd_install(fd, file); - return fd; + iter_link = container_of(link, struct bpf_iter_link, link); + err = prepare_seq_file(fd_prepare_file(fdf), iter_link); + if (err) + return err; /* Automatic cleanup handles fput */ -free_file: - fput(file); -free_fd: - put_unused_fd(fd); - return err; + return fd_publish(fdf); } struct bpf_prog *bpf_iter_get_info(struct bpf_iter_meta *meta, bool in_stop) -- 2.47.3 Signed-off-by: Christian Brauner --- kernel/bpf/token.c | 48 ++++++++++++++++-------------------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/kernel/bpf/token.c b/kernel/bpf/token.c index 0bbe412f854e..100afbe17dfa 100644 --- a/kernel/bpf/token.c +++ b/kernel/bpf/token.c @@ -110,16 +110,15 @@ const struct file_operations bpf_token_fops = { int bpf_token_create(union bpf_attr *attr) { + struct bpf_token *token __free(kfree) = NULL; struct bpf_mount_opts *mnt_opts; - struct bpf_token *token = NULL; struct user_namespace *userns; struct inode *inode; - struct file *file; CLASS(fd, f)(attr->token_create.bpffs_fd); struct path path; struct super_block *sb; umode_t mode; - int err, fd; + int err; if (fd_empty(f)) return -EBADF; @@ -166,23 +165,21 @@ int bpf_token_create(union bpf_attr *attr) inode->i_fop = &bpf_token_fops; clear_nlink(inode); /* make sure it is unlinked */ - file = alloc_file_pseudo(inode, path.mnt, BPF_TOKEN_INODE_NAME, O_RDWR, &bpf_token_fops); - if (IS_ERR(file)) { - iput(inode); - return PTR_ERR(file); - } + FD_PREPARE(fdf, O_CLOEXEC, + alloc_file_pseudo(inode, path.mnt, BPF_TOKEN_INODE_NAME, + O_RDWR, &bpf_token_fops)); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) + return err; token = kzalloc(sizeof(*token), GFP_USER); - if (!token) { - err = -ENOMEM; - goto out_file; - } + if (!token) + return -ENOMEM; atomic64_set(&token->refcnt, 1); - /* remember bpffs owning userns for future ns_capable() checks */ - token->userns = get_user_ns(userns); - + /* remember bpffs owning userns for future ns_capable() checks. */ + token->userns = userns; token->allowed_cmds = mnt_opts->delegate_cmds; token->allowed_maps = mnt_opts->delegate_maps; token->allowed_progs = mnt_opts->delegate_progs; @@ -190,24 +187,11 @@ int bpf_token_create(union bpf_attr *attr) err = security_bpf_token_create(token, attr, &path); if (err) - goto out_token; - - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) { - err = fd; - goto out_token; - } - - file->private_data = token; - fd_install(fd, file); - - return fd; + return err; -out_token: - bpf_token_free(token); -out_file: - fput(file); - return err; + get_user_ns(token->userns); + fd_prepare_file(fdf)->private_data = no_free_ptr(token); + return fd_publish(fdf); } int bpf_token_get_info_by_fd(struct bpf_token *token, -- 2.47.3 Signed-off-by: Christian Brauner --- mm/memfd.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/mm/memfd.c b/mm/memfd.c index 1d109c1acf21..4267ab8e9099 100644 --- a/mm/memfd.c +++ b/mm/memfd.c @@ -470,9 +470,9 @@ SYSCALL_DEFINE2(memfd_create, const char __user *, uname, unsigned int, flags) { - struct file *file; - int fd, error; - char *name; + char *name __free(kfree) = NULL; + unsigned int fd_flags; + int error; error = sanitize_flags(&flags); if (error < 0) @@ -482,25 +482,11 @@ SYSCALL_DEFINE2(memfd_create, if (IS_ERR(name)) return PTR_ERR(name); - fd = get_unused_fd_flags((flags & MFD_CLOEXEC) ? O_CLOEXEC : 0); - if (fd < 0) { - error = fd; - goto err_free_name; - } - - file = alloc_file(name, flags); - if (IS_ERR(file)) { - error = PTR_ERR(file); - goto err_free_fd; - } - - fd_install(fd, file); - kfree(name); - return fd; + fd_flags = (flags & MFD_CLOEXEC) ? O_CLOEXEC : 0; + FD_PREPARE(fdf, fd_flags, alloc_file(name, flags)); + error = ACQUIRE_ERR(fd_prepare, &fdf); + if (error) + return error; -err_free_fd: - put_unused_fd(fd); -err_free_name: - kfree(name); - return error; + return fd_publish(fdf); } -- 2.47.3 Signed-off-by: Christian Brauner --- mm/secretmem.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/mm/secretmem.c b/mm/secretmem.c index 60137305bc20..b3cd9a33f68e 100644 --- a/mm/secretmem.c +++ b/mm/secretmem.c @@ -224,8 +224,7 @@ static struct file *secretmem_file_create(unsigned long flags) SYSCALL_DEFINE1(memfd_secret, unsigned int, flags) { - struct file *file; - int fd, err; + int err; /* make sure local flags do not confict with global fcntl.h */ BUILD_BUG_ON(SECRETMEM_FLAGS_MASK & O_CLOEXEC); @@ -238,22 +237,12 @@ SYSCALL_DEFINE1(memfd_secret, unsigned int, flags) if (atomic_read(&secretmem_users) < 0) return -ENFILE; - fd = get_unused_fd_flags(flags & O_CLOEXEC); - if (fd < 0) - return fd; - - file = secretmem_file_create(flags); - if (IS_ERR(file)) { - err = PTR_ERR(file); - goto err_put_fd; - } - - fd_install(fd, file); - return fd; + FD_PREPARE(fdf, flags & O_CLOEXEC, secretmem_file_create(flags)); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) + return err; -err_put_fd: - put_unused_fd(fd); - return err; + return fd_publish(fdf); } static int secretmem_init_fs_context(struct fs_context *fc) -- 2.47.3 Signed-off-by: Christian Brauner --- net/handshake/netlink.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index 7e46d130dce2..db4f35c31bc1 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -93,7 +93,7 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info) struct handshake_net *hn = handshake_pernet(net); struct handshake_req *req = NULL; struct socket *sock; - int class, fd, err; + int class, err; err = -EOPNOTSUPP; if (!hn) @@ -106,26 +106,22 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info) err = -EAGAIN; req = handshake_req_next(hn, class); - if (!req) - goto out_status; - - sock = req->hr_sk->sk_socket; - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) { - err = fd; - goto out_complete; - } + if (req) { + sock = req->hr_sk->sk_socket; - err = req->hr_proto->hp_accept(req, info, fd); - if (err) { - put_unused_fd(fd); - goto out_complete; - } + FD_PREPARE(fdf, O_CLOEXEC, sock->file); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) + goto out_complete; - fd_install(fd, get_file(sock->file)); + get_file(sock->file); /* FD_PREPARE() consumes a reference. */ + err = req->hr_proto->hp_accept(req, info, fd_prepare_fd(fdf)); + if (err) + goto out_complete; /* Automatic cleanup handles fput */ - trace_handshake_cmd_accept(net, req, req->hr_sk, fd); - return 0; + trace_handshake_cmd_accept(net, req, req->hr_sk, fd_prepare_fd(fdf)); + return fd_publish(fdf); + } out_complete: handshake_complete(req, -EIO, NULL); -- 2.47.3 Signed-off-by: Christian Brauner --- net/kcm/kcmsock.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index b4f01cb07561..dace6fada46d 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -1560,25 +1560,17 @@ static int kcm_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) } case SIOCKCMCLONE: { struct kcm_clone info; - struct file *file; - info.fd = get_unused_fd_flags(0); - if (unlikely(info.fd < 0)) - return info.fd; + FD_PREPARE(fdf, 0, kcm_clone(sock)); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) + return err; - file = kcm_clone(sock); - if (IS_ERR(file)) { - put_unused_fd(info.fd); - return PTR_ERR(file); - } - if (copy_to_user((void __user *)arg, &info, - sizeof(info))) { - put_unused_fd(info.fd); - fput(file); + info.fd = fd_prepare_fd(fdf); + if (copy_to_user((void __user *)arg, &info, sizeof(info))) return -EFAULT; - } - fd_install(info.fd, file); - err = 0; + + err = fd_publish(fdf); break; } default: -- 2.47.3 Signed-off-by: Christian Brauner --- net/sctp/socket.c | 89 ++++++++++++++++--------------------------------------- 1 file changed, 25 insertions(+), 64 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index ed8293a34240..03f0317c5c38 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5664,47 +5664,46 @@ static int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, return err; } -static int sctp_getsockopt_peeloff_common(struct sock *sk, sctp_peeloff_arg_t *peeloff, - struct file **newfile, unsigned flags) +static int sctp_getsockopt_peeloff_common(struct sock *sk, + sctp_peeloff_arg_t *peeloff, int len, + char __user *optval, + int __user *optlen, unsigned flags) { struct socket *newsock; int retval; retval = sctp_do_peeloff(sk, peeloff->associd, &newsock); if (retval < 0) - goto out; + return retval; - /* Map the socket to an unused fd that can be returned to the user. */ - retval = get_unused_fd_flags(flags & SOCK_CLOEXEC); - if (retval < 0) { + FD_PREPARE(fdf, flags & SOCK_CLOEXEC, sock_alloc_file(newsock, 0, NULL)); + retval = ACQUIRE_ERR(fd_prepare, &fdf); + if (retval) { sock_release(newsock); - goto out; - } - - *newfile = sock_alloc_file(newsock, 0, NULL); - if (IS_ERR(*newfile)) { - put_unused_fd(retval); - retval = PTR_ERR(*newfile); - *newfile = NULL; return retval; } pr_debug("%s: sk:%p, newsk:%p, sd:%d\n", __func__, sk, newsock->sk, - retval); - - peeloff->sd = retval; + fd_prepare_fd(fdf)); if (flags & SOCK_NONBLOCK) - (*newfile)->f_flags |= O_NONBLOCK; -out: - return retval; + fd_prepare_file(fdf)->f_flags |= O_NONBLOCK; + + /* Return the fd mapped to the new socket. */ + if (put_user(len, optlen)) + return -EFAULT; + + if (copy_to_user(optval, &peeloff, len)) + return -EFAULT; + + peeloff->sd = fd_prepare_fd(fdf); + return fd_publish(fdf); } -static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval, int __user *optlen) +static int sctp_getsockopt_peeloff(struct sock *sk, int len, + char __user *optval, int __user *optlen) { sctp_peeloff_arg_t peeloff; - struct file *newfile = NULL; - int retval = 0; if (len < sizeof(sctp_peeloff_arg_t)) return -EINVAL; @@ -5712,33 +5711,13 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval if (copy_from_user(&peeloff, optval, len)) return -EFAULT; - retval = sctp_getsockopt_peeloff_common(sk, &peeloff, &newfile, 0); - if (retval < 0) - goto out; - - /* Return the fd mapped to the new socket. */ - if (put_user(len, optlen)) { - fput(newfile); - put_unused_fd(retval); - return -EFAULT; - } - - if (copy_to_user(optval, &peeloff, len)) { - fput(newfile); - put_unused_fd(retval); - return -EFAULT; - } - fd_install(retval, newfile); -out: - return retval; + return sctp_getsockopt_peeloff_common(sk, &peeloff, len, optval, optlen, 0); } static int sctp_getsockopt_peeloff_flags(struct sock *sk, int len, char __user *optval, int __user *optlen) { sctp_peeloff_flags_arg_t peeloff; - struct file *newfile = NULL; - int retval = 0; if (len < sizeof(sctp_peeloff_flags_arg_t)) return -EINVAL; @@ -5746,26 +5725,8 @@ static int sctp_getsockopt_peeloff_flags(struct sock *sk, int len, if (copy_from_user(&peeloff, optval, len)) return -EFAULT; - retval = sctp_getsockopt_peeloff_common(sk, &peeloff.p_arg, - &newfile, peeloff.flags); - if (retval < 0) - goto out; - - /* Return the fd mapped to the new socket. */ - if (put_user(len, optlen)) { - fput(newfile); - put_unused_fd(retval); - return -EFAULT; - } - - if (copy_to_user(optval, &peeloff, len)) { - fput(newfile); - put_unused_fd(retval); - return -EFAULT; - } - fd_install(retval, newfile); -out: - return retval; + return sctp_getsockopt_peeloff_common(sk, &peeloff.p_arg, len, optval, + optlen, peeloff.flags); } /* 7.1.13 Peer Address Parameters (SCTP_PEER_ADDR_PARAMS) -- 2.47.3 Signed-off-by: Christian Brauner --- net/socket.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/net/socket.c b/net/socket.c index e8892b218708..af72b10ffe49 100644 --- a/net/socket.c +++ b/net/socket.c @@ -503,21 +503,16 @@ EXPORT_SYMBOL(sock_alloc_file); static int sock_map_fd(struct socket *sock, int flags) { - struct file *newfile; - int fd = get_unused_fd_flags(flags); - if (unlikely(fd < 0)) { - sock_release(sock); - return fd; - } + int err; - newfile = sock_alloc_file(sock, flags, NULL); - if (!IS_ERR(newfile)) { - fd_install(fd, newfile); - return fd; + FD_PREPARE(fdf, flags, sock_alloc_file(sock, flags, NULL)); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) { + sock_release(sock); + return err; } - put_unused_fd(fd); - return PTR_ERR(newfile); + return fd_publish(fdf); } /** -- 2.47.3 Signed-off-by: Christian Brauner --- net/socket.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/net/socket.c b/net/socket.c index af72b10ffe49..13617083f95f 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2007,8 +2007,7 @@ static int __sys_accept4_file(struct file *file, struct sockaddr __user *upeer_s int __user *upeer_addrlen, int flags) { struct proto_accept_arg arg = { }; - struct file *newfile; - int newfd; + int err; if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; @@ -2016,18 +2015,12 @@ static int __sys_accept4_file(struct file *file, struct sockaddr __user *upeer_s if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK; - newfd = get_unused_fd_flags(flags); - if (unlikely(newfd < 0)) - return newfd; + FD_PREPARE(fdf, flags, do_accept(file, &arg, upeer_sockaddr, upeer_addrlen, flags)); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) + return err; - newfile = do_accept(file, &arg, upeer_sockaddr, upeer_addrlen, - flags); - if (IS_ERR(newfile)) { - put_unused_fd(newfd); - return PTR_ERR(newfile); - } - fd_install(newfd, newfile); - return newfd; + return fd_publish(fdf); } /* -- 2.47.3 Signed-off-by: Christian Brauner --- arch/powerpc/platforms/cell/spufs/inode.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 7ec60290abe6..413076bc7e3f 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -268,21 +268,13 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, static int spufs_context_open(const struct path *path) { int ret; - struct file *filp; - ret = get_unused_fd_flags(0); - if (ret < 0) + FD_PREPARE(fdf, 0, dentry_open(path, O_RDONLY, current_cred())); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) return ret; - - filp = dentry_open(path, O_RDONLY, current_cred()); - if (IS_ERR(filp)) { - put_unused_fd(ret); - return PTR_ERR(filp); - } - - filp->f_op = &spufs_context_fops; - fd_install(ret, filp); - return ret; + fd_prepare_file(fdf)->f_op = &spufs_context_fops; + return fd_publish(fdf); } static struct spu_context * -- 2.47.3 Fixes a UAF for src_info as well. Signed-off-by: Christian Brauner --- arch/powerpc/platforms/pseries/papr-hvpipe.c | 37 +++++++--------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c index 21a2f447c43f..e752f3404af6 100644 --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c @@ -479,10 +479,8 @@ static const struct file_operations papr_hvpipe_handle_ops = { static int papr_hvpipe_dev_create_handle(u32 srcID) { - struct hvpipe_source_info *src_info; - struct file *file; + struct hvpipe_source_info *src_info __free(kfree) = NULL; long err; - int fd; spin_lock(&hvpipe_src_list_lock); /* @@ -506,20 +504,14 @@ static int papr_hvpipe_dev_create_handle(u32 srcID) src_info->tsk = current; init_waitqueue_head(&src_info->recv_wqh); - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - err = fd; + FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC, + anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops, + (void *)src_info, O_RDWR)); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) goto free_buf; - } - - file = anon_inode_getfile("[papr-hvpipe]", - &papr_hvpipe_handle_ops, (void *)src_info, - O_RDWR); - if (IS_ERR(file)) { - err = PTR_ERR(file); - goto free_fd; - } + retain_and_null_ptr(src_info); spin_lock(&hvpipe_src_list_lock); /* * If two processes are executing ioctl() for the same @@ -528,22 +520,11 @@ static int papr_hvpipe_dev_create_handle(u32 srcID) */ if (hvpipe_find_source(srcID)) { spin_unlock(&hvpipe_src_list_lock); - err = -EALREADY; - goto free_file; + return -EALREADY; } list_add(&src_info->list, &hvpipe_src_list); spin_unlock(&hvpipe_src_list_lock); - - fd_install(fd, file); - return fd; - -free_file: - fput(file); -free_fd: - put_unused_fd(fd); -free_buf: - kfree(src_info); - return err; + return fd_publish(fdf); } /* -- 2.47.3 Signed-off-by: Christian Brauner --- arch/powerpc/platforms/cell/spufs/inode.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 413076bc7e3f..1ce66b8ffed9 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -501,25 +501,17 @@ static const struct file_operations spufs_gang_fops = { static int spufs_gang_open(const struct path *path) { int ret; - struct file *filp; - - ret = get_unused_fd_flags(0); - if (ret < 0) - return ret; /* * get references for dget and mntget, will be released * in error path of *_open(). */ - filp = dentry_open(path, O_RDONLY, current_cred()); - if (IS_ERR(filp)) { - put_unused_fd(ret); - return PTR_ERR(filp); - } - - filp->f_op = &spufs_gang_fops; - fd_install(ret, filp); - return ret; + FD_PREPARE(fdf, 0, dentry_open(path, O_RDONLY, current_cred())); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; + fd_prepare_file(fdf)->f_op = &spufs_gang_fops; + return fd_publish(fdf); } static int spufs_create_gang(struct inode *inode, -- 2.47.3 Signed-off-by: Christian Brauner --- .../powerpc/platforms/pseries/papr-platform-dump.c | 39 ++++++++-------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/arch/powerpc/platforms/pseries/papr-platform-dump.c b/arch/powerpc/platforms/pseries/papr-platform-dump.c index f8d55eccdb6b..df9e837f2bd6 100644 --- a/arch/powerpc/platforms/pseries/papr-platform-dump.c +++ b/arch/powerpc/platforms/pseries/papr-platform-dump.c @@ -301,11 +301,9 @@ static const struct file_operations papr_platform_dump_handle_ops = { */ static long papr_platform_dump_create_handle(u64 dump_tag) { - struct ibm_platform_dump_params *params; + struct ibm_platform_dump_params *params, *tmp; u64 param_dump_tag; - struct file *file; - long err; - int fd; + int err; /* * Return failure if the user space is already opened FD for @@ -334,34 +332,23 @@ static long papr_platform_dump_create_handle(u64 dump_tag) params->dump_tag_lo = (u32)(dump_tag & 0x00000000ffffffffULL); params->status = RTAS_IBM_PLATFORM_DUMP_START; - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - err = fd; - goto free_area; - } - - file = anon_inode_getfile_fmode("[papr-platform-dump]", - &papr_platform_dump_handle_ops, - (void *)params, O_RDONLY, - FMODE_LSEEK | FMODE_PREAD); - if (IS_ERR(file)) { - err = PTR_ERR(file); - goto put_fd; + FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC, + anon_inode_getfile_fmode("[papr-platform-dump]", + &papr_platform_dump_handle_ops, + (void *)params, O_RDONLY, + FMODE_LSEEK | FMODE_PREAD)); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) { + rtas_work_area_free(params->work_area); + kfree(params); + return err } - fd_install(fd, file); - list_add(¶ms->list, &platform_dump_list); pr_info("%s (%d) initiated platform dump for dump tag %llu\n", current->comm, current->pid, dump_tag); - return fd; -put_fd: - put_unused_fd(fd); -free_area: - rtas_work_area_free(params->work_area); - kfree(params); - return err; + return fd_publish(fdf); } /* -- 2.47.3 Signed-off-by: Christian Brauner --- arch/powerpc/platforms/pseries/papr-rtas-common.c | 32 +++++++---------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/arch/powerpc/platforms/pseries/papr-rtas-common.c b/arch/powerpc/platforms/pseries/papr-rtas-common.c index 33c606e3378a..ebe628c69d46 100644 --- a/arch/powerpc/platforms/pseries/papr-rtas-common.c +++ b/arch/powerpc/platforms/pseries/papr-rtas-common.c @@ -205,35 +205,21 @@ long papr_rtas_setup_file_interface(struct papr_rtas_sequence *seq, char *name) { const struct papr_rtas_blob *blob; - struct file *file; - long ret; - int fd; + int ret; blob = papr_rtas_retrieve(seq); if (IS_ERR(blob)) return PTR_ERR(blob); - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto free_blob; - } - - file = anon_inode_getfile_fmode(name, fops, (void *)blob, - O_RDONLY, FMODE_LSEEK | FMODE_PREAD); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto put_fd; + FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC, + anon_inode_getfile_fmode(name, fops, (void *)blob, O_RDONLY, + FMODE_LSEEK | FMODE_PREAD)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) { + papr_rtas_blob_free(blob); + return ret; } - - fd_install(fd, file); - return fd; - -put_fd: - put_unused_fd(fd); -free_blob: - papr_rtas_blob_free(blob); - return ret; + return fd_publish(fdf); } /* -- 2.47.3 Signed-off-by: Christian Brauner --- drivers/dma-buf/sw_sync.c | 42 +++++++++++++++--------------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c index 3c20f1d31cf5..7ff0d0e7dbc7 100644 --- a/drivers/dma-buf/sw_sync.c +++ b/drivers/dma-buf/sw_sync.c @@ -343,47 +343,35 @@ static int sw_sync_debugfs_release(struct inode *inode, struct file *file) static long sw_sync_ioctl_create_fence(struct sync_timeline *obj, unsigned long arg) { - int fd = get_unused_fd_flags(O_CLOEXEC); - int err; struct sync_pt *pt; struct sync_file *sync_file; struct sw_sync_create_fence_data data; + int err; - if (fd < 0) - return fd; - - if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { - err = -EFAULT; - goto err; - } + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; pt = sync_pt_create(obj, data.value); - if (!pt) { - err = -ENOMEM; - goto err; - } + if (!pt) + return -ENOMEM; sync_file = sync_file_create(&pt->base); dma_fence_put(&pt->base); - if (!sync_file) { - err = -ENOMEM; - goto err; - } + if (!sync_file) + return -ENOMEM; - data.fence = fd; - if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + FD_PREPARE(fdf, O_CLOEXEC, sync_file->file); + err = ACQUIRE_ERR(fd_prepare, &fdf); + if (err) { fput(sync_file->file); - err = -EFAULT; - goto err; + return err; } - fd_install(fd, sync_file->file); - - return 0; + data.fence = fd_prepare_fd(fdf); + if (copy_to_user((void __user *)arg, &data, sizeof(data))) + return -EFAULT; -err: - put_unused_fd(fd); - return err; + return fd_publish(fdf); } static long sw_sync_ioctl_inc(struct sync_timeline *obj, unsigned long arg) -- 2.47.3 Signed-off-by: Christian Brauner --- drivers/gpio/gpiolib-cdev.c | 61 ++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 175836467f21..195bee9018fb 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -298,12 +298,35 @@ static const struct file_operations linehandle_fileops = { #endif }; +static int linehandle_fd_create(struct gpio_device *gdev, + struct linehandle_state *lh, + struct gpiohandle_request *handlereq, + void __user *ip) +{ + int ret; + + FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC, + anon_inode_getfile("gpio-linehandle", &linehandle_fileops, + lh, O_RDONLY | O_CLOEXEC)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; + + handlereq->fd = fd_prepare_fd(fdf); + if (copy_to_user(ip, handlereq, sizeof(handlereq))) + return -EFAULT; + + dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", lh->num_descs); + + fd_publish(fdf); + return 0; +} + static int linehandle_create(struct gpio_device *gdev, void __user *ip) { struct gpiohandle_request handlereq; struct linehandle_state *lh; - struct file *file; - int fd, i, ret; + int i, ret; u32 lflags; if (copy_from_user(&handlereq, ip, sizeof(handlereq))) @@ -377,41 +400,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) offset); } - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = fd; + ret = linehandle_fd_create(gdev, lh, &handlereq, ip); + if (ret) goto out_free_lh; - } - - file = anon_inode_getfile("gpio-linehandle", - &linehandle_fileops, - lh, - O_RDONLY | O_CLOEXEC); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto out_put_unused_fd; - } - - handlereq.fd = fd; - if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { - /* - * fput() will trigger the release() callback, so do not go onto - * the regular error cleanup path here. - */ - fput(file); - put_unused_fd(fd); - return -EFAULT; - } - - fd_install(fd, file); - - dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", - lh->num_descs); - return 0; -out_put_unused_fd: - put_unused_fd(fd); out_free_lh: linehandle_free(lh); return ret; -- 2.47.3 Signed-off-by: Christian Brauner --- drivers/hv/mshv_root_main.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index e3b2bd417c46..575c23560bb2 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1938,30 +1938,16 @@ mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev) goto delete_partition; ret = mshv_init_async_handler(partition); - if (ret) - goto remove_partition; - - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto remove_partition; - } - - file = anon_inode_getfile("mshv_partition", &mshv_partition_fops, - partition, O_RDWR); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto put_fd; + if (!ret) { + FD_PREPARE(fdf, O_CLOEXEC, + anon_inode_getfile("mshv_partition", + &mshv_partition_fops, partition, O_RDWR)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (!ret) + return fd_publish(fdf); } - - fd_install(fd, file); - - return fd; - -put_fd: - put_unused_fd(fd); -remove_partition: remove_partition(partition); + delete_partition: hv_call_delete_partition(partition->pt_id); cleanup_irq_srcu: -- 2.47.3 Signed-off-by: Christian Brauner --- drivers/media/mc/mc-request.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/media/mc/mc-request.c b/drivers/media/mc/mc-request.c index f66f728b1b43..bf039ab7be93 100644 --- a/drivers/media/mc/mc-request.c +++ b/drivers/media/mc/mc-request.c @@ -282,8 +282,6 @@ EXPORT_SYMBOL_GPL(media_request_get_by_fd); int media_request_alloc(struct media_device *mdev, int *alloc_fd) { struct media_request *req; - struct file *filp; - int fd; int ret; /* Either both are NULL or both are non-NULL */ @@ -297,19 +295,6 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) if (!req) return -ENOMEM; - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto err_free_req; - } - - filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC); - if (IS_ERR(filp)) { - ret = PTR_ERR(filp); - goto err_put_fd; - } - - filp->private_data = req; req->mdev = mdev; req->state = MEDIA_REQUEST_STATE_IDLE; req->num_incomplete_objects = 0; @@ -320,19 +305,23 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd) req->updating_count = 0; req->access_count = 0; - *alloc_fd = fd; + FD_PREPARE(fdf, O_CLOEXEC, + anon_inode_getfile("request", &request_fops, NULL, + O_CLOEXEC)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + goto err_free_req; + + fd_prepare_file(fdf)->private_data = req; + + *alloc_fd = fd_publish(fdf); snprintf(req->debug_str, sizeof(req->debug_str), "%u:%d", - atomic_inc_return(&mdev->request_id), fd); + atomic_inc_return(&mdev->request_id), *alloc_fd); dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str); - fd_install(fd, filp); - return 0; -err_put_fd: - put_unused_fd(fd); - err_free_req: if (mdev->ops->req_free) mdev->ops->req_free(req); -- 2.47.3 Signed-off-by: Christian Brauner --- drivers/misc/ntsync.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c index 999026a1ae04..b7db1628cf26 100644 --- a/drivers/misc/ntsync.c +++ b/drivers/misc/ntsync.c @@ -721,21 +721,15 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev, static int ntsync_obj_get_fd(struct ntsync_obj *obj) { - struct file *file; - int fd; - - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) - return fd; - file = anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR); - if (IS_ERR(file)) { - put_unused_fd(fd); - return PTR_ERR(file); - } - obj->file = file; - fd_install(fd, file); + int ret; - return fd; + FD_PREPARE(fdf, O_CLOEXEC, + anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; + obj->file = fd_prepare_file(fdf); + return fd_publish(fdf); } static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp) -- 2.47.3 Signed-off-by: Christian Brauner --- drivers/tty/pty.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 8bb1a01fef2a..f9dd2209cc7b 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -601,42 +601,26 @@ static struct cdev ptmx_cdev; */ int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags) { - int fd; - struct file *filp; - int retval = -EINVAL; + int ret; struct path path; if (tty->driver != ptm_driver) return -EIO; - fd = get_unused_fd_flags(flags); - if (fd < 0) { - retval = fd; - goto err; - } - /* Compute the slave's path */ path.mnt = devpts_mntget(master, tty->driver_data); - if (IS_ERR(path.mnt)) { - retval = PTR_ERR(path.mnt); - goto err_put; - } + if (IS_ERR(path.mnt)) + return PTR_ERR(path.mnt); path.dentry = tty->link->driver_data; - filp = dentry_open(&path, flags, current_cred()); - mntput(path.mnt); - if (IS_ERR(filp)) { - retval = PTR_ERR(filp); - goto err_put; + FD_PREPARE(fdf, flags, dentry_open(&path, flags, current_cred())); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) { + mntput(path.mnt); + return ret; } - fd_install(fd, filp); - return fd; - -err_put: - put_unused_fd(fd); -err: - return retval; + return fd_publish(fdf); } static int pty_unix98_ioctl(struct tty_struct *tty, -- 2.47.3 Signed-off-by: Christian Brauner --- drivers/vfio/group.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/drivers/vfio/group.c b/drivers/vfio/group.c index c376a6279de0..7f706991b867 100644 --- a/drivers/vfio/group.c +++ b/drivers/vfio/group.c @@ -299,9 +299,7 @@ static int vfio_group_ioctl_get_device_fd(struct vfio_group *group, char __user *arg) { struct vfio_device *device; - struct file *filep; char *buf; - int fdno; int ret; buf = strndup_user(arg, PAGE_SIZE); @@ -313,26 +311,13 @@ static int vfio_group_ioctl_get_device_fd(struct vfio_group *group, if (IS_ERR(device)) return PTR_ERR(device); - fdno = get_unused_fd_flags(O_CLOEXEC); - if (fdno < 0) { - ret = fdno; - goto err_put_device; - } - - filep = vfio_device_open_file(device); - if (IS_ERR(filep)) { - ret = PTR_ERR(filep); - goto err_put_fdno; + FD_PREPARE(fdf, O_CLOEXEC, vfio_device_open_file(device)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) { + vfio_device_put_registration(device); + return ret; } - - fd_install(fdno, filep); - return fdno; - -err_put_fdno: - put_unused_fd(fdno); -err_put_device: - vfio_device_put_registration(device); - return ret; + return fd_publish(fdf); } static int vfio_group_ioctl_get_status(struct vfio_group *group, -- 2.47.3 Signed-off-by: Christian Brauner --- fs/file.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/fs/file.c b/fs/file.c index 28743b742e3c..0613ca112baf 100644 --- a/fs/file.c +++ b/fs/file.c @@ -1357,28 +1357,26 @@ int replace_fd(unsigned fd, struct file *file, unsigned flags) */ int receive_fd(struct file *file, int __user *ufd, unsigned int o_flags) { - int new_fd; int error; error = security_file_receive(file); if (error) return error; - new_fd = get_unused_fd_flags(o_flags); - if (new_fd < 0) - return new_fd; + FD_PREPARE(fdf, o_flags, file); + error = ACQUIRE_ERR(fd_prepare, &fdf); + if (error) + return error; + get_file(file); if (ufd) { - error = put_user(new_fd, ufd); - if (error) { - put_unused_fd(new_fd); + error = put_user(fd_prepare_fd(fdf), ufd); + if (error) return error; - } } - fd_install(new_fd, get_file(file)); - __receive_sock(file); - return new_fd; + __receive_sock(fd_prepare_file(fdf)); + return fd_publish(fdf); } EXPORT_SYMBOL_GPL(receive_fd); -- 2.47.3 Signed-off-by: Christian Brauner --- io_uring/mock_file.c | 46 +++++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/io_uring/mock_file.c b/io_uring/mock_file.c index 45d3735b2708..5200a3ed0735 100644 --- a/io_uring/mock_file.c +++ b/io_uring/mock_file.c @@ -211,10 +211,10 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag const struct file_operations *fops = &io_mock_fops; const struct io_uring_sqe *sqe = cmd->sqe; struct io_uring_mock_create mc, __user *uarg; - struct io_mock_file *mf = NULL; - struct file *file = NULL; + struct file *file; + struct io_mock_file *mf __free(kfree) = NULL; size_t uarg_size; - int fd = -1, ret; + int ret; /* * It's a testing only driver that allows exercising edge cases @@ -246,10 +246,6 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag if (!mf) return -ENOMEM; - ret = fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC); - if (fd < 0) - goto fail; - init_waitqueue_head(&mf->poll_wq); mf->size = mc.file_size; mf->rw_delay_ns = mc.rw_delay_ns; @@ -258,33 +254,25 @@ static int io_create_mock_file(struct io_uring_cmd *cmd, unsigned int issue_flag mf->pollable = true; } - file = anon_inode_create_getfile("[io_uring_mock]", fops, - mf, O_RDWR | O_CLOEXEC, NULL); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto fail; - } + FD_PREPARE(fdf, O_RDWR | O_CLOEXEC, + anon_inode_create_getfile("[io_uring_mock]", fops, mf, + O_RDWR | O_CLOEXEC, NULL)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; - file->f_mode |= FMODE_READ | FMODE_CAN_READ | - FMODE_WRITE | FMODE_CAN_WRITE | - FMODE_LSEEK; + file = fd_prepare_file(fdf); + file->f_mode |= FMODE_READ | FMODE_CAN_READ | FMODE_WRITE | + FMODE_CAN_WRITE | FMODE_LSEEK; if (mc.flags & IORING_MOCK_CREATE_F_SUPPORT_NOWAIT) file->f_mode |= FMODE_NOWAIT; - mc.out_fd = fd; - if (copy_to_user(uarg, &mc, uarg_size)) { - fput(file); - ret = -EFAULT; - goto fail; - } + mc.out_fd = fd_prepare_fd(fdf); + if (copy_to_user(uarg, &mc, uarg_size)) + return -EFAULT; - fd_install(fd, file); - return 0; -fail: - if (fd >= 0) - put_unused_fd(fd); - kfree(mf); - return ret; + retain_and_null_ptr(mf); + return fd_publish(fdf); } static int io_probe_mock(struct io_uring_cmd *cmd) -- 2.47.3 Signed-off-by: Christian Brauner --- virt/kvm/guest_memfd.c | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index fbca8c0972da..ca0135dbefa8 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -493,28 +493,22 @@ bool __weak kvm_arch_supports_gmem_init_shared(struct kvm *kvm) static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags) { const char *anon_name = "[kvm-gmem]"; - struct kvm_gmem *gmem; + struct kvm_gmem *gmem __free(kfree) = NULL; struct inode *inode; struct file *file; - int fd, err; - - fd = get_unused_fd_flags(0); - if (fd < 0) - return fd; + int ret; gmem = kzalloc(sizeof(*gmem), GFP_KERNEL); - if (!gmem) { - err = -ENOMEM; - goto err_fd; - } + if (!gmem) + return -ENOMEM; - file = anon_inode_create_getfile(anon_name, &kvm_gmem_fops, gmem, - O_RDWR, NULL); - if (IS_ERR(file)) { - err = PTR_ERR(file); - goto err_gmem; - } + FD_PREPARE(fdf, 0, anon_inode_create_getfile(anon_name, &kvm_gmem_fops, + gmem, O_RDWR, NULL)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; + file = fd_prepare_file(fdf); file->f_flags |= O_LARGEFILE; inode = file->f_inode; @@ -535,14 +529,9 @@ static int __kvm_gmem_create(struct kvm *kvm, loff_t size, u64 flags) xa_init(&gmem->bindings); list_add(&gmem->entry, &inode->i_mapping->i_private_list); - fd_install(fd, file); - return fd; - -err_gmem: - kfree(gmem); -err_fd: - put_unused_fd(fd); - return err; + /* Ownership of gmem transferred to file */ + retain_and_null_ptr(gmem); + return fd_publish(fdf); } int kvm_gmem_create(struct kvm *kvm, struct kvm_create_guest_memfd *args) -- 2.47.3 Signed-off-by: Christian Brauner --- virt/kvm/kvm_main.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index b7a0ae2a7b20..f8c5ed12d42d 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -4313,27 +4313,21 @@ static const struct file_operations kvm_vcpu_stats_fops = { static int kvm_vcpu_ioctl_get_stats_fd(struct kvm_vcpu *vcpu) { - int fd; - struct file *file; + int ret; char name[15 + ITOA_MAX_LEN + 1]; snprintf(name, sizeof(name), "kvm-vcpu-stats:%d", vcpu->vcpu_id); - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) - return fd; - - file = anon_inode_getfile_fmode(name, &kvm_vcpu_stats_fops, vcpu, - O_RDONLY, FMODE_PREAD); - if (IS_ERR(file)) { - put_unused_fd(fd); - return PTR_ERR(file); - } + FD_PREPARE(fdf, O_CLOEXEC, anon_inode_getfile_fmode(name, + &kvm_vcpu_stats_fops, + vcpu, O_RDONLY, + FMODE_PREAD)); + ret = ACQUIRE_ERR(fd_prepare, &fdf); + if (ret) + return ret; kvm_get_kvm(vcpu->kvm); - fd_install(fd, file); - - return fd; + return fd_publish(fdf); } #ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY -- 2.47.3