build_open_flags() has a weird bit in it: /* Must never be set by userspace */ flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC; This didn't used to have the O_CLOEXEC removal in it, but just used to be: /* Must never be set by userspace */ flags &= ~FMODE_NONOTIFY; but this flag should be only from file->f_mode and should have nothing to do with the O_* flags. Further, this check is redundant with: flags &= VALID_OPEN_FLAGS; a few lines above. Fix this by splitting the f_mode flags (FMODE_*) from the f_flags flags (O_*) internally. Fixes: ecf081d1a73b ("vfs: introduce FMODE_NONOTIFY") Signed-off-by: David Howells <dhowells@xxxxxxxxxx> cc: Eric Paris <eparis@xxxxxxxxxx> --- drivers/dma-buf/dma-buf.c | 2 +- drivers/dma-buf/sync_file.c | 2 +- drivers/gpu/drm/drm_syncobj.c | 2 +- drivers/staging/lustre/lustre/mdc/mdc_lib.c | 2 +- drivers/staging/lustre/lustre/mdc/mdc_locks.c | 2 +- drivers/tty/pty.c | 4 ++-- fs/anon_inodes.c | 20 +++++++++++-------- fs/autofs4/dev-ioctl.c | 2 +- fs/cachefiles/rdwr.c | 2 +- fs/eventfd.c | 2 +- fs/eventpoll.c | 2 +- fs/exec.c | 6 ++++-- fs/exportfs/expfs.c | 2 +- fs/fcntl.c | 6 ++---- fs/internal.h | 1 + fs/namei.c | 1 + fs/namespace.c | 2 +- fs/nfs/dir.c | 15 ++++++++------ fs/nfs/nfs4proc.c | 9 +++------ fs/notify/fanotify/fanotify_user.c | 10 ++++++---- fs/notify/inotify/inotify_user.c | 2 +- fs/nsfs.c | 2 +- fs/open.c | 24 +++++++++++++++-------- fs/signalfd.c | 3 ++- fs/timerfd.c | 2 +- fs/xfs/xfs_ioctl.c | 2 +- include/linux/anon_inodes.h | 6 +++--- include/linux/fs.h | 26 ++++++++++++++++++------- include/linux/fsnotify.h | 8 ++++---- include/linux/nfs_fs.h | 3 ++- include/uapi/asm-generic/fcntl.h | 1 - ipc/mqueue.c | 6 ++---- kernel/bpf/syscall.c | 6 +++--- kernel/events/core.c | 2 +- net/unix/af_unix.c | 2 +- security/apparmor/file.c | 2 +- security/keys/big_key.c | 2 +- security/selinux/hooks.c | 2 +- 38 files changed, 109 insertions(+), 86 deletions(-) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index d78d5fc173dc..93445178d5c1 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -436,7 +436,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) dmabuf->resv = resv; file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, - exp_info->flags); + exp_info->flags, 0); if (IS_ERR(file)) { ret = PTR_ERR(file); goto err_dmabuf; diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 35dd06479867..b92125e9be40 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -37,7 +37,7 @@ static struct sync_file *sync_file_alloc(void) return NULL; sync_file->file = anon_inode_getfile("sync_file", &sync_file_fops, - sync_file, 0); + sync_file, 0, 0); if (IS_ERR(sync_file->file)) goto err; diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index d4f4ce484529..10eb9b6d7d6a 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -419,7 +419,7 @@ int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd) file = anon_inode_getfile("syncobj_file", &drm_syncobj_file_fops, - syncobj, 0); + syncobj, 0, 0); if (IS_ERR(file)) { put_unused_fd(fd); return PTR_ERR(file); diff --git a/drivers/staging/lustre/lustre/mdc/mdc_lib.c b/drivers/staging/lustre/lustre/mdc/mdc_lib.c index 46eefdc09e3a..092d5be903cc 100644 --- a/drivers/staging/lustre/lustre/mdc/mdc_lib.c +++ b/drivers/staging/lustre/lustre/mdc/mdc_lib.c @@ -176,7 +176,7 @@ static inline __u64 mds_pack_open_flags(__u64 flags) cr_flags |= MDS_OPEN_SYNC; if (flags & O_DIRECTORY) cr_flags |= MDS_OPEN_DIRECTORY; - if (flags & __FMODE_EXEC) + if (flags & FMODE_EXEC) cr_flags |= MDS_FMODE_EXEC; if (cl_is_lov_delay_create(flags)) cr_flags |= MDS_OPEN_DELAY_CREATE; diff --git a/drivers/staging/lustre/lustre/mdc/mdc_locks.c b/drivers/staging/lustre/lustre/mdc/mdc_locks.c index 695ef44532cf..7520e9dafffc 100644 --- a/drivers/staging/lustre/lustre/mdc/mdc_locks.c +++ b/drivers/staging/lustre/lustre/mdc/mdc_locks.c @@ -257,7 +257,7 @@ mdc_intent_open_pack(struct obd_export *exp, struct lookup_intent *it, } else { if (it->it_flags & (FMODE_WRITE | MDS_OPEN_TRUNC)) mode = LCK_CW; - else if (it->it_flags & __FMODE_EXEC) + else if (it->it_flags & FMODE_EXEC) mode = LCK_PR; else mode = LCK_CR; diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 6c7151edd715..91a0df8dc4a7 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -636,7 +636,7 @@ int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags) } path.dentry = tty->link->driver_data; - filp = dentry_open(&path, flags, current_cred()); + filp = dentry_open(&path, flags, 0, current_cred()); mntput(path.mnt); if (IS_ERR(filp)) { retval = PTR_ERR(filp); @@ -806,7 +806,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) nonseekable_open(inode, filp); /* We refuse fsnotify events on ptmx, since it's a shared resource */ - filp->f_mode |= FMODE_NONOTIFY; + filp->f_mode |= FMODE_DONT_NOTIFY; retval = tty_alloc_file(filp); if (retval) diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index 13c06a7e0b85..2b50a274f885 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c @@ -60,7 +60,8 @@ static struct file_system_type anon_inode_fs_type = { * @name: [in] name of the "class" of the new file * @fops: [in] file operations for the new file * @priv: [in] private data for the new file (will be file's private_data) - * @flags: [in] flags + * @f_flags: [in] O_* flags + * @f_mode: [in] FMODE_* flags * * Creates a new file by hooking it on a single inode. This is useful for files * that do not need to have a full-fledged inode in order to operate correctly. @@ -69,8 +70,8 @@ static struct file_system_type anon_inode_fs_type = { * setup. Returns the newly created file* or an error pointer. */ struct file *anon_inode_getfile(const char *name, - const struct file_operations *fops, - void *priv, int flags) + const struct file_operations *fops, void *priv, + unsigned int f_flags, fmode_t f_mode) { struct qstr this; struct path path; @@ -103,12 +104,12 @@ struct file *anon_inode_getfile(const char *name, d_instantiate(path.dentry, anon_inode_inode); - file = alloc_file(&path, OPEN_FMODE(flags), fops); + file = alloc_file(&path, f_mode | OPEN_FMODE(f_flags), fops); if (IS_ERR(file)) goto err_dput; file->f_mapping = anon_inode_inode->i_mapping; - file->f_flags = flags & (O_ACCMODE | O_NONBLOCK); + file->f_flags = f_flags & (O_ACCMODE | O_NONBLOCK); file->private_data = priv; return file; @@ -129,7 +130,8 @@ EXPORT_SYMBOL_GPL(anon_inode_getfile); * @name: [in] name of the "class" of the new file * @fops: [in] file operations for the new file * @priv: [in] private data for the new file (will be file's private_data) - * @flags: [in] flags + * @f_flags: [in] O_* flags + * @f_mode: [in] FMODE_* flags * * Creates a new file by hooking it on a single inode. This is useful for files * that do not need to have a full-fledged inode in order to operate correctly. @@ -138,17 +140,17 @@ EXPORT_SYMBOL_GPL(anon_inode_getfile); * setup. Returns new descriptor or an error code. */ int anon_inode_getfd(const char *name, const struct file_operations *fops, - void *priv, int flags) + void *priv, unsigned int f_flags, fmode_t f_mode) { int error, fd; struct file *file; - error = get_unused_fd_flags(flags); + error = get_unused_fd_flags(f_flags); if (error < 0) return error; fd = error; - file = anon_inode_getfile(name, fops, priv, flags); + file = anon_inode_getfile(name, fops, priv, f_flags, f_mode); if (IS_ERR(file)) { error = PTR_ERR(file); goto err_put_unused_fd; diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c index 26f6b4f41ce6..8e93d4e07aac 100644 --- a/fs/autofs4/dev-ioctl.c +++ b/fs/autofs4/dev-ioctl.c @@ -258,7 +258,7 @@ static int autofs_dev_ioctl_open_mountpoint(const char *name, dev_t devid) if (err) goto out; - filp = dentry_open(&path, O_RDONLY, current_cred()); + filp = dentry_open(&path, O_RDONLY, 0, current_cred()); path_put(&path); if (IS_ERR(filp)) { err = PTR_ERR(filp); diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c index 5082c8a49686..0d20db389405 100644 --- a/fs/cachefiles/rdwr.c +++ b/fs/cachefiles/rdwr.c @@ -910,7 +910,7 @@ int cachefiles_write_page(struct fscache_storage *op, struct page *page) * own time */ path.mnt = cache->mnt; path.dentry = object->backer; - file = dentry_open(&path, O_RDWR | O_LARGEFILE, cache->cache_cred); + file = dentry_open(&path, O_RDWR | O_LARGEFILE, 0, cache->cache_cred); if (IS_ERR(file)) { ret = PTR_ERR(file); goto error_2; diff --git a/fs/eventfd.c b/fs/eventfd.c index 08d3bd602f73..fb4c5912a982 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -402,7 +402,7 @@ static int do_eventfd(unsigned int count, int flags) ctx->flags = flags; fd = anon_inode_getfd("[eventfd]", &eventfd_fops, ctx, - O_RDWR | (flags & EFD_SHARED_FCNTL_FLAGS)); + O_RDWR | (flags & EFD_SHARED_FCNTL_FLAGS), 0); if (fd < 0) eventfd_free_ctx(ctx); diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 602ca4285b2e..e8052eb1e0dd 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1963,7 +1963,7 @@ static int do_epoll_create(int flags) goto out_free_ep; } file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep, - O_RDWR | (flags & O_CLOEXEC)); + O_RDWR | (flags & O_CLOEXEC), 0); if (IS_ERR(file)) { error = PTR_ERR(file); goto out_free_fd; diff --git a/fs/exec.c b/fs/exec.c index 183059c427b9..7ca13cc0b7f9 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -124,7 +124,8 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) struct filename *tmp = getname(library); int error = PTR_ERR(tmp); static const struct open_flags uselib_flags = { - .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC, + .open_flag = O_LARGEFILE | O_RDONLY, + .f_mode = FMODE_EXEC, .acc_mode = MAY_READ | MAY_EXEC, .intent = LOOKUP_OPEN, .lookup_flags = LOOKUP_FOLLOW, @@ -838,7 +839,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags) struct file *file; int err; struct open_flags open_exec_flags = { - .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC, + .open_flag = O_LARGEFILE | O_RDONLY, + .f_mode = FMODE_EXEC, .acc_mode = MAY_EXEC, .intent = LOOKUP_OPEN, .lookup_flags = LOOKUP_FOLLOW, diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 645158dc33f1..ec8f68277ce7 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -308,7 +308,7 @@ static int get_name(const struct path *path, char *name, struct dentry *child) /* * Open the directory ... */ - file = dentry_open(path, O_RDONLY, cred); + file = dentry_open(path, O_RDONLY, 0, cred); error = PTR_ERR(file); if (IS_ERR(file)) goto out; diff --git a/fs/fcntl.c b/fs/fcntl.c index d737ff082472..60bc5bf2f4cf 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -1028,10 +1028,8 @@ static int __init fcntl_init(void) * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY * is defined as O_NONBLOCK on some platforms and not on others. */ - BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ != - HWEIGHT32( - (VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) | - __FMODE_EXEC | __FMODE_NONOTIFY)); + BUILD_BUG_ON(19 - 1 /* for O_RDONLY being 0 */ != + HWEIGHT32(VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY))); fasync_cache = kmem_cache_create("fasync_cache", sizeof(struct fasync_struct), 0, SLAB_PANIC, NULL); diff --git a/fs/internal.h b/fs/internal.h index f47ede6ace5a..c29552e0522f 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -110,6 +110,7 @@ struct open_flags { int open_flag; umode_t mode; int acc_mode; + fmode_t f_mode; int intent; int lookup_flags; }; diff --git a/fs/namei.c b/fs/namei.c index 819d6ee71b46..5cbd980b4031 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3481,6 +3481,7 @@ static struct file *path_openat(struct nameidata *nd, return file; file->f_flags = op->open_flag; + file->f_mode = op->f_mode; if (unlikely(file->f_flags & __O_TMPFILE)) { error = do_tmpfile(nd, flags, op, file, &opened); diff --git a/fs/namespace.c b/fs/namespace.c index 03ade803b948..dba680aa1ea4 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3309,7 +3309,7 @@ SYSCALL_DEFINE5(fsmount, int, fs_fd, unsigned int, flags, unsigned int, ms_flags /* 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); + file = dentry_open(&newmount, O_PATH, 0, fc->cred); if (IS_ERR(file)) goto err_path; file->f_mode |= FMODE_NEED_UNMOUNT; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 73f8b43d988c..f8eeea255651 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1395,9 +1395,9 @@ const struct dentry_operations nfs4_dentry_operations = { }; EXPORT_SYMBOL_GPL(nfs4_dentry_operations); -static fmode_t flags_to_mode(int flags) +static fmode_t flags_to_mode(int flags, fmode_t f_mode) { - fmode_t res = (__force fmode_t)flags & FMODE_EXEC; + fmode_t res = f_mode & FMODE_EXEC; if ((flags & O_ACCMODE) != O_WRONLY) res |= FMODE_READ; if ((flags & O_ACCMODE) != O_RDONLY) @@ -1407,7 +1407,7 @@ static fmode_t flags_to_mode(int flags) static struct nfs_open_context *create_nfs_open_context(struct dentry *dentry, int open_flags, struct file *filp) { - return alloc_nfs_open_context(dentry, flags_to_mode(open_flags), filp); + return alloc_nfs_open_context(dentry, flags_to_mode(open_flags, filp->f_mode), filp); } static int do_open(struct inode *inode, struct file *filp) @@ -2441,11 +2441,11 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask) return status; } -static int nfs_open_permission_mask(int openflags) +static int nfs_open_permission_mask(fmode_t f_mode, int openflags) { int mask = 0; - if (openflags & __FMODE_EXEC) { + if (f_mode & FMODE_EXEC) { /* ONLY check exec rights */ mask = MAY_EXEC; } else { @@ -2458,9 +2458,10 @@ static int nfs_open_permission_mask(int openflags) return mask; } -int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags) +int nfs_may_open(struct inode *inode, struct rpc_cred *cred, fmode_t f_mode, + int openflags) { - return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags)); + return nfs_do_access(inode, cred, nfs_open_permission_mask(f_mode, openflags)); } EXPORT_SYMBOL_GPL(nfs_may_open); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b71757e85066..6b30118c0507 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1712,7 +1712,8 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) rcu_read_unlock(); nfs_release_seqid(opendata->o_arg.seqid); if (!opendata->is_recover) { - ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode); + ret = nfs_may_open(state->inode, state->owner->so_cred, + fmode, open_mode); if (ret != 0) goto out; } @@ -2414,11 +2415,7 @@ static int nfs4_opendata_access(struct rpc_cred *cred, return 0; mask = 0; - /* - * Use openflags to check for exec, because fmode won't - * always have FMODE_EXEC set when file open for exec. - */ - if (openflags & __FMODE_EXEC) { + if (fmode & FMODE_EXEC) { /* ONLY check for exec rights */ if (S_ISDIR(state->inode->i_mode)) mask = NFS4_ACCESS_LOOKUP; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index ec4d8c59d0e3..a84fb5390e85 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -32,7 +32,7 @@ * * Internal and external open flags are stored together in field f_flags of * struct file. Only external open flags shall be allowed in event_f_flags. - * Internal flags like FMODE_NONOTIFY, FMODE_EXEC, FMODE_NOCMTIME shall be + * Internal flags like FMODE_DONT_NOTIFY, FMODE_EXEC, FMODE_NOCMTIME shall be * excluded. */ #define FANOTIFY_INIT_ALL_EVENT_F_BITS ( \ @@ -92,7 +92,8 @@ static int create_fd(struct fsnotify_group *group, * are NULL; That's fine, just don't call dentry open */ if (event->path.dentry && event->path.mnt) new_file = dentry_open(&event->path, - group->fanotify_data.f_flags | FMODE_NONOTIFY, + group->fanotify_data.f_flags, + FMODE_DONT_NOTIFY, current_cred()); else new_file = ERR_PTR(-EOVERFLOW); @@ -741,7 +742,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) return -EMFILE; } - f_flags = O_RDWR | FMODE_NONOTIFY; + f_flags = O_RDWR; if (flags & FAN_CLOEXEC) f_flags |= O_CLOEXEC; if (flags & FAN_NONBLOCK) @@ -809,7 +810,8 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) group->fanotify_data.audit = true; } - fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); + fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags, + FMODE_DONT_NOTIFY); if (fd < 0) goto out_destroy_group; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index ef32f3657958..b8fd9ade776e 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -667,7 +667,7 @@ static int do_inotify_init(int flags) return PTR_ERR(group); ret = anon_inode_getfd("inotify", &inotify_fops, group, - O_RDONLY | flags); + O_RDONLY | flags, 0); if (ret < 0) fsnotify_destroy_group(group); diff --git a/fs/nsfs.c b/fs/nsfs.c index f069eb6495b0..93886ec2540c 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -174,7 +174,7 @@ int open_related_ns(struct ns_common *ns, return PTR_ERR(err); } - f = dentry_open(&path, O_RDONLY, current_cred()); + f = dentry_open(&path, O_RDONLY, 0, current_cred()); path_put(&path); if (IS_ERR(f)) { put_unused_fd(fd); diff --git a/fs/open.c b/fs/open.c index c5ee7cd60424..79a8a1bd740d 100644 --- a/fs/open.c +++ b/fs/open.c @@ -34,6 +34,13 @@ #include "internal.h" +const u8 acc_mode[O_ACCMODE + 1] = { + [O_RDONLY] = MAY_READ, + [O_WRONLY] = MAY_WRITE, + [O_RDWR] = MAY_READ | MAY_WRITE, + [3] = MAY_READ | MAY_WRITE, +}; + int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, struct file *filp) { @@ -732,9 +739,6 @@ static int do_dentry_open(struct file *f, static const struct file_operations empty_fops = {}; int error; - f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | - FMODE_PREAD | FMODE_PWRITE; - path_get(&f->f_path); f->f_inode = inode; f->f_mapping = inode->i_mapping; @@ -743,11 +747,14 @@ static int do_dentry_open(struct file *f, f->f_wb_err = filemap_sample_wb_err(f->f_mapping); if (unlikely(f->f_flags & O_PATH)) { - f->f_mode = FMODE_PATH; + f->f_mode |= FMODE_PATH; f->f_op = &empty_fops; goto done; } + f->f_mode |= OPEN_FMODE(f->f_flags) | FMODE_LSEEK | + FMODE_PREAD | FMODE_PWRITE; + if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) { error = get_write_access(inode); if (unlikely(error)) @@ -906,8 +913,8 @@ int vfs_open(const struct path *path, struct file *file, return do_dentry_open(file, d_backing_inode(dentry), NULL, cred); } -struct file *dentry_open(const struct path *path, int flags, - const struct cred *cred) +struct file *dentry_open(const struct path *path, unsigned int flags, + fmode_t f_mode, const struct cred *cred) { int error; struct file *f; @@ -941,14 +948,15 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o * them in fcntl(F_GETFD) or similar interfaces. */ flags &= VALID_OPEN_FLAGS; + op->f_mode = 0; if (flags & (O_CREAT | __O_TMPFILE)) op->mode = (mode & S_IALLUGO) | S_IFREG; else op->mode = 0; - /* Must never be set by userspace */ - flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC; + /* Don't leak O_CLOEXEC into ->f_flags */ + flags &= ~O_CLOEXEC; /* * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only diff --git a/fs/signalfd.c b/fs/signalfd.c index d2187a813376..885122c786de 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -287,7 +287,8 @@ static int do_signalfd4(int ufd, sigset_t __user *user_mask, size_t sizemask, * anon_inode_getfd() will install the fd. */ ufd = anon_inode_getfd("[signalfd]", &signalfd_fops, ctx, - O_RDWR | (flags & (O_CLOEXEC | O_NONBLOCK))); + O_RDWR | (flags & (O_CLOEXEC | O_NONBLOCK)), + 0); if (ufd < 0) kfree(ctx); } else { diff --git a/fs/timerfd.c b/fs/timerfd.c index cdad49da3ff7..6de8ea9737d7 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -425,7 +425,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) ctx->moffs = ktime_mono_to_real(0); ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, - O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS)); + O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS), 0); if (ufd < 0) kfree(ctx); diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 89fb1eb80aae..6c3c7ff271df 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -255,7 +255,7 @@ xfs_open_by_handle( path.mnt = parfilp->f_path.mnt; path.dentry = dentry; - filp = dentry_open(&path, hreq->oflags, cred); + filp = dentry_open(&path, hreq->oflags, 0, cred); dput(dentry); if (IS_ERR(filp)) { put_unused_fd(fd); diff --git a/include/linux/anon_inodes.h b/include/linux/anon_inodes.h index d0d7d96261ad..a1a190beb068 100644 --- a/include/linux/anon_inodes.h +++ b/include/linux/anon_inodes.h @@ -12,10 +12,10 @@ struct file_operations; struct file *anon_inode_getfile(const char *name, - const struct file_operations *fops, - void *priv, int flags); + const struct file_operations *fops, void *priv, + unsigned int f_flags, fmode_t f_mode); int anon_inode_getfd(const char *name, const struct file_operations *fops, - void *priv, int flags); + void *priv, unsigned int f_flags, fmode_t f_mode); #endif /* _LINUX_ANON_INODES_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index ba571c18e236..40890e3359f0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -149,7 +149,7 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset, #define FMODE_CAN_WRITE ((__force fmode_t)0x40000) /* File was opened by fanotify and shouldn't generate fanotify events */ -#define FMODE_NONOTIFY ((__force fmode_t)0x4000000) +#define FMODE_DONT_NOTIFY ((__force fmode_t)0x4000000) /* File is capable of returning -EAGAIN if I/O will block */ #define FMODE_NOWAIT ((__force fmode_t)0x8000000) @@ -2413,7 +2413,8 @@ extern struct file *file_open_name(struct filename *, int, umode_t); extern struct file *filp_open(const char *, int, umode_t); extern struct file *file_open_root(struct dentry *, struct vfsmount *, const char *, int, umode_t); -extern struct file * dentry_open(const struct path *, int, const struct cred *); +extern struct file * dentry_open(const struct path *, unsigned int, fmode_t, + const struct cred *); extern int filp_close(struct file *, fl_owner_t id); extern struct filename *getname_flags(const char __user *, int, int *); @@ -3349,12 +3350,23 @@ int proc_nr_inodes(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); int __init get_filesystem_list(char *buf); -#define __FMODE_EXEC ((__force int) FMODE_EXEC) -#define __FMODE_NONOTIFY ((__force int) FMODE_NONOTIFY) +extern const u8 acc_mode[O_ACCMODE + 1]; -#define ACC_MODE(x) ("\004\002\006\006"[(x)&O_ACCMODE]) -#define OPEN_FMODE(flag) ((__force fmode_t)(((flag + 1) & O_ACCMODE) | \ - (flag & __FMODE_NONOTIFY))) +/* + * Turn { O_RDONLY, O_WRONLY, O_RDWD, 3 } into MAY_READ and/or MAY_WRITE + */ +static inline unsigned int ACC_MODE(int x) +{ + return acc_mode[(x) & O_ACCMODE]; +} + +/* + * Turn { O_RDONLY, O_WRONLY, O_RDWD, 3 } into FMODE_READ and/or FMODE_WRITE + */ +static inline fmode_t OPEN_FMODE(unsigned int O_flags) +{ + return (__force fmode_t)((O_flags + 1) & O_ACCMODE); +} static inline bool is_sxid(umode_t mode) { diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index bdaf22582f6e..67c3f9e3f371 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -38,7 +38,7 @@ static inline int fsnotify_perm(struct file *file, int mask) __u32 fsnotify_mask = 0; int ret; - if (file->f_mode & FMODE_NONOTIFY) + if (file->f_mode & FMODE_DONT_NOTIFY) return 0; if (!(mask & (MAY_READ | MAY_OPEN))) return 0; @@ -184,7 +184,7 @@ static inline void fsnotify_access(struct file *file) if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; - if (!(file->f_mode & FMODE_NONOTIFY)) { + if (!(file->f_mode & FMODE_DONT_NOTIFY)) { fsnotify_parent(path, NULL, mask); fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } @@ -202,7 +202,7 @@ static inline void fsnotify_modify(struct file *file) if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; - if (!(file->f_mode & FMODE_NONOTIFY)) { + if (!(file->f_mode & FMODE_DONT_NOTIFY)) { fsnotify_parent(path, NULL, mask); fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } @@ -237,7 +237,7 @@ static inline void fsnotify_close(struct file *file) if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; - if (!(file->f_mode & FMODE_NONOTIFY)) { + if (!(file->f_mode & FMODE_DONT_NOTIFY)) { fsnotify_parent(path, NULL, mask); fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 2f129bbfaae8..a13508c0fe88 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -477,7 +477,8 @@ extern const struct dentry_operations nfs_dentry_operations; extern void nfs_force_lookup_revalidate(struct inode *dir); extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr, struct nfs4_label *label); -extern int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags); +extern int nfs_may_open(struct inode *inode, struct rpc_cred *cred, + fmode_t mode, int openflags); extern void nfs_access_zap_cache(struct inode *inode); /* diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index 9dc0bf0c5a6e..0b1c7e35090c 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -6,7 +6,6 @@ /* * FMODE_EXEC is 0x20 - * FMODE_NONOTIFY is 0x4000000 * These cannot be used by userspace O_* until internal and external open * flags are split. * -Eric Paris diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 934ccdc48a1d..de7548442c94 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -791,8 +791,6 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro, umode_t mode, struct filename *name, struct mq_attr *attr) { - static const int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, - MAY_READ | MAY_WRITE }; int acc; if (d_really_is_negative(dentry)) { @@ -810,7 +808,7 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro, return -EEXIST; if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) return -EINVAL; - acc = oflag2acc[oflag & O_ACCMODE]; + acc = ACC_MODE(oflag); return inode_permission(d_inode(dentry), acc); } @@ -843,7 +841,7 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, 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()); + struct file *file = dentry_open(&path, oflag, 0, current_cred()); if (!IS_ERR(file)) fd_install(fd, file); else diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 016ef9025827..5018d399eed9 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -373,7 +373,7 @@ int bpf_map_new_fd(struct bpf_map *map, int flags) return ret; return anon_inode_getfd("bpf-map", &bpf_map_fops, map, - flags | O_CLOEXEC); + flags | O_CLOEXEC, 0); } int bpf_get_file_flag(int flags) @@ -1068,7 +1068,7 @@ int bpf_prog_new_fd(struct bpf_prog *prog) return ret; return anon_inode_getfd("bpf-prog", &bpf_prog_fops, prog, - O_RDWR | O_CLOEXEC); + O_RDWR | O_CLOEXEC, 0); } static struct bpf_prog *____bpf_prog_get(struct fd f) @@ -1445,7 +1445,7 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr) raw_tp->prog = prog; tp_fd = anon_inode_getfd("bpf-raw-tracepoint", &bpf_raw_tp_fops, raw_tp, - O_CLOEXEC); + O_CLOEXEC, 0); if (tp_fd < 0) { bpf_probe_unregister(raw_tp->btp, prog); err = tp_fd; diff --git a/kernel/events/core.c b/kernel/events/core.c index 67612ce359ad..0e0fcb1f946f 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -10612,7 +10612,7 @@ SYSCALL_DEFINE5(perf_event_open, } event_file = anon_inode_getfile("[perf_event]", &perf_fops, event, - f_flags); + f_flags, 0); if (IS_ERR(event_file)) { err = PTR_ERR(event_file); event_file = NULL; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index e5473c03d667..6813b51d1baf 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2588,7 +2588,7 @@ static int unix_open_file(struct sock *sk) if (fd < 0) goto out; - f = dentry_open(&path, O_PATH, current_cred()); + f = dentry_open(&path, O_PATH, 0, current_cred()); if (IS_ERR(f)) { put_unused_fd(fd); fd = PTR_ERR(f); diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 224b2fef93ca..392398f6254e 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -692,7 +692,7 @@ void aa_inherit_files(const struct cred *cred, struct files_struct *files) if (!n) /* none found? */ goto out; - devnull = dentry_open(&aa_null, O_RDWR, cred); + devnull = dentry_open(&aa_null, O_RDWR, 0, cred); if (IS_ERR(devnull)) devnull = NULL; /* replace all the matching ones with this */ diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 933623784ccd..d29381f22cde 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -374,7 +374,7 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) if (!buf) return -ENOMEM; - file = dentry_open(path, O_RDONLY, current_cred()); + file = dentry_open(path, O_RDONLY, 0, current_cred()); if (IS_ERR(file)) { ret = PTR_ERR(file); goto error; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9c5d60308136..098a541b76e0 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2625,7 +2625,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, if (!n) /* none found? */ return; - devnull = dentry_open(&selinux_null, O_RDWR, cred); + devnull = dentry_open(&selinux_null, O_RDWR, 0, cred); if (IS_ERR(devnull)) devnull = NULL; /* replace all the matching ones with this */