From eecec19d9e705cb670a387d18973feeffd412970 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 1 Dec 2017 17:26:05 -0500 Subject: mqueue: switch to vfs_mkobj(), quit abusing ->d_fsdata Signed-off-by: Al Viro --- ipc/mqueue.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'ipc') diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 9649ecd..e011ccd 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -416,11 +416,11 @@ static void mqueue_evict_inode(struct inode *inode) put_ipc_ns(ipc_ns); } -static int mqueue_create(struct inode *dir, struct dentry *dentry, - umode_t mode, bool excl) +static int mqueue_create_attr(struct dentry *dentry, umode_t mode, void *arg) { + struct inode *dir = dentry->d_parent->d_inode; struct inode *inode; - struct mq_attr *attr = dentry->d_fsdata; + struct mq_attr *attr = arg; int error; struct ipc_namespace *ipc_ns; @@ -461,6 +461,12 @@ out_unlock: return error; } +static int mqueue_create(struct inode *dir, struct dentry *dentry, + umode_t mode, bool excl) +{ + return mqueue_create_attr(dentry, mode, NULL); +} + static int mqueue_unlink(struct inode *dir, struct dentry *dentry) { struct inode *inode = d_inode(dentry); @@ -732,8 +738,6 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir, ret = mq_attr_ok(ipc_ns, attr); if (ret) return ERR_PTR(ret); - /* store for use during create */ - path->dentry->d_fsdata = attr; } else { struct mq_attr def_attr; @@ -746,9 +750,8 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir, return ERR_PTR(ret); } - mode &= ~current_umask(); - ret = vfs_create(dir, path->dentry, mode, true); - path->dentry->d_fsdata = NULL; + ret = vfs_mkobj(path->dentry, mode & ~current_umask(), + mqueue_create_attr, attr); if (ret) return ERR_PTR(ret); return dentry_open(path, oflag, cred); -- cgit v1.1 From af4a5372e4166e8f13cb5cd36d936a0f56bc8418 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 1 Dec 2017 17:34:22 -0500 Subject: move dentry_open() calls up into do_mq_open() Signed-off-by: Al Viro --- ipc/mqueue.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) (limited to 'ipc') diff --git a/ipc/mqueue.c b/ipc/mqueue.c index e011ccd..fee6755 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -727,17 +727,16 @@ static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr) /* * Invoked when creating a new queue via sys_mq_open */ -static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir, +static int do_create(struct ipc_namespace *ipc_ns, struct inode *dir, struct path *path, int oflag, umode_t mode, struct mq_attr *attr) { - const struct cred *cred = current_cred(); int ret; if (attr) { ret = mq_attr_ok(ipc_ns, attr); if (ret) - return ERR_PTR(ret); + return ret; } else { struct mq_attr def_attr; @@ -747,28 +746,23 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir, ipc_ns->mq_msgsize_default); ret = mq_attr_ok(ipc_ns, &def_attr); if (ret) - return ERR_PTR(ret); + return ret; } - ret = vfs_mkobj(path->dentry, mode & ~current_umask(), + return vfs_mkobj(path->dentry, mode & ~current_umask(), mqueue_create_attr, attr); - if (ret) - return ERR_PTR(ret); - return dentry_open(path, oflag, cred); } /* Opens existing queue */ -static struct file *do_open(struct path *path, int oflag) +static int do_open(struct path *path, int oflag) { static const int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, MAY_READ | MAY_WRITE }; int acc; if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) - return ERR_PTR(-EINVAL); + return -EINVAL; acc = oflag2acc[oflag & O_ACCMODE]; - if (inode_permission(d_inode(path->dentry), acc)) - return ERR_PTR(-EACCES); - return dentry_open(path, oflag, current_cred()); + return inode_permission(d_inode(path->dentry), acc); } static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, @@ -805,28 +799,30 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, if (oflag & O_CREAT) { if (d_really_is_positive(path.dentry)) { /* entry already exists */ audit_inode(name, path.dentry, 0); - if (oflag & O_EXCL) { + if (oflag & O_EXCL) error = -EEXIST; - goto out; - } - filp = do_open(&path, oflag); + else + error = do_open(&path, oflag); } else { if (ro) { error = ro; - goto out; + } else { + audit_inode_parent_hidden(name, root); + error = do_create(ipc_ns, d_inode(root), &path, + oflag, mode, attr); } - audit_inode_parent_hidden(name, root); - filp = do_create(ipc_ns, d_inode(root), &path, - oflag, mode, attr); } } else { if (d_really_is_negative(path.dentry)) { error = -ENOENT; - goto out; + } else { + audit_inode(name, path.dentry, 0); + error = do_open(&path, oflag); } - audit_inode(name, path.dentry, 0); - filp = do_open(&path, oflag); } + if (error) + goto out; + filp = dentry_open(&path, oflag, current_cred()); if (!IS_ERR(filp)) fd_install(fd, filp); -- cgit v1.1 From 05c1b29038acb05a52dc113ae2ab646f9a92bbfd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 1 Dec 2017 17:43:43 -0500 Subject: mqueue: fold mq_attr_ok() into mqueue_get_inode() Signed-off-by: Al Viro --- ipc/mqueue.c | 71 ++++++++++++++++++------------------------------------------ 1 file changed, 21 insertions(+), 50 deletions(-) (limited to 'ipc') diff --git a/ipc/mqueue.c b/ipc/mqueue.c index fee6755..a248352 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -270,13 +270,30 @@ static struct inode *mqueue_get_inode(struct super_block *sb, * that means the min(mq_maxmsg, max_priorities) * struct * posix_msg_tree_node. */ + + ret = -EINVAL; + if (info->attr.mq_maxmsg <= 0 || info->attr.mq_msgsize <= 0) + goto out_inode; + if (capable(CAP_SYS_RESOURCE)) { + if (info->attr.mq_maxmsg > HARD_MSGMAX || + info->attr.mq_msgsize > HARD_MSGSIZEMAX) + goto out_inode; + } else { + if (info->attr.mq_maxmsg > ipc_ns->mq_msg_max || + info->attr.mq_msgsize > ipc_ns->mq_msgsize_max) + goto out_inode; + } + ret = -EOVERFLOW; + /* check for overflow */ + if (info->attr.mq_msgsize > ULONG_MAX/info->attr.mq_maxmsg) + goto out_inode; mq_treesize = info->attr.mq_maxmsg * sizeof(struct msg_msg) + min_t(unsigned int, info->attr.mq_maxmsg, MQ_PRIO_MAX) * sizeof(struct posix_msg_tree_node); - - mq_bytes = mq_treesize + (info->attr.mq_maxmsg * - info->attr.mq_msgsize); - + mq_bytes = info->attr.mq_maxmsg * info->attr.mq_msgsize; + if (mq_bytes + mq_treesize < mq_bytes) + goto out_inode; + mq_bytes += mq_treesize; spin_lock(&mq_lock); if (u->mq_bytes + mq_bytes < u->mq_bytes || u->mq_bytes + mq_bytes > rlimit(RLIMIT_MSGQUEUE)) { @@ -696,34 +713,6 @@ static void remove_notification(struct mqueue_inode_info *info) info->notify_user_ns = NULL; } -static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr) -{ - int mq_treesize; - unsigned long total_size; - - if (attr->mq_maxmsg <= 0 || attr->mq_msgsize <= 0) - return -EINVAL; - if (capable(CAP_SYS_RESOURCE)) { - if (attr->mq_maxmsg > HARD_MSGMAX || - attr->mq_msgsize > HARD_MSGSIZEMAX) - return -EINVAL; - } else { - if (attr->mq_maxmsg > ipc_ns->mq_msg_max || - attr->mq_msgsize > ipc_ns->mq_msgsize_max) - return -EINVAL; - } - /* check for overflow */ - if (attr->mq_msgsize > ULONG_MAX/attr->mq_maxmsg) - return -EOVERFLOW; - mq_treesize = attr->mq_maxmsg * sizeof(struct msg_msg) + - min_t(unsigned int, attr->mq_maxmsg, MQ_PRIO_MAX) * - sizeof(struct posix_msg_tree_node); - total_size = attr->mq_maxmsg * attr->mq_msgsize; - if (total_size + mq_treesize < total_size) - return -EOVERFLOW; - return 0; -} - /* * Invoked when creating a new queue via sys_mq_open */ @@ -731,24 +720,6 @@ static int do_create(struct ipc_namespace *ipc_ns, struct inode *dir, struct path *path, int oflag, umode_t mode, struct mq_attr *attr) { - int ret; - - if (attr) { - ret = mq_attr_ok(ipc_ns, attr); - if (ret) - return ret; - } else { - struct mq_attr def_attr; - - def_attr.mq_maxmsg = min(ipc_ns->mq_msg_max, - ipc_ns->mq_msg_default); - def_attr.mq_msgsize = min(ipc_ns->mq_msgsize_max, - ipc_ns->mq_msgsize_default); - ret = mq_attr_ok(ipc_ns, &def_attr); - if (ret) - return ret; - } - return vfs_mkobj(path->dentry, mode & ~current_umask(), mqueue_create_attr, attr); } -- cgit v1.1 From 066cc813e94a344ae4482af310d324739955c3b5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 1 Dec 2017 17:51:39 -0500 Subject: do_mq_open(): move all work prior to dentry_open() into a helper Signed-off-by: Al Viro --- ipc/mqueue.c | 77 ++++++++++++++++++++++++------------------------------------ 1 file changed, 31 insertions(+), 46 deletions(-) (limited to 'ipc') diff --git a/ipc/mqueue.c b/ipc/mqueue.c index a248352..e8a872e 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -713,34 +713,44 @@ static void remove_notification(struct mqueue_inode_info *info) info->notify_user_ns = NULL; } -/* - * Invoked when creating a new queue via sys_mq_open - */ -static int do_create(struct ipc_namespace *ipc_ns, struct inode *dir, - struct path *path, int oflag, umode_t mode, +static int prepare_open(struct dentry *dentry, int oflag, int ro, + umode_t mode, struct filename *name, struct mq_attr *attr) { - return vfs_mkobj(path->dentry, mode & ~current_umask(), - mqueue_create_attr, attr); -} - -/* Opens existing queue */ -static int do_open(struct path *path, int oflag) -{ static const int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, MAY_READ | MAY_WRITE }; int acc; + + if (oflag & O_CREAT) { + if (d_really_is_positive(dentry)) { /* entry already exists */ + audit_inode(name, dentry, 0); + if (oflag & O_EXCL) + return -EEXIST; + } else { + if (ro) + return ro; + + audit_inode_parent_hidden(name, dentry->d_parent); + return vfs_mkobj(dentry, mode & ~current_umask(), + mqueue_create_attr, attr); + } + } else { + if (d_really_is_negative(dentry)) { + return -ENOENT; + } else { + audit_inode(name, dentry, 0); + } + } if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) return -EINVAL; acc = oflag2acc[oflag & O_ACCMODE]; - return inode_permission(d_inode(path->dentry), acc); + return inode_permission(d_inode(dentry), acc); } static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, struct mq_attr *attr) { struct path path; - struct file *filp; struct filename *name; int fd, error; struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; @@ -767,39 +777,14 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, } path.mnt = mntget(mnt); - if (oflag & O_CREAT) { - if (d_really_is_positive(path.dentry)) { /* entry already exists */ - audit_inode(name, path.dentry, 0); - if (oflag & O_EXCL) - error = -EEXIST; - else - error = do_open(&path, oflag); - } else { - if (ro) { - error = ro; - } else { - audit_inode_parent_hidden(name, root); - error = do_create(ipc_ns, d_inode(root), &path, - oflag, mode, attr); - } - } - } else { - if (d_really_is_negative(path.dentry)) { - error = -ENOENT; - } else { - audit_inode(name, path.dentry, 0); - error = do_open(&path, oflag); - } + 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); } - if (error) - goto out; - filp = dentry_open(&path, oflag, current_cred()); - - if (!IS_ERR(filp)) - fd_install(fd, filp); - else - error = PTR_ERR(filp); -out: path_put(&path); out_putfd: if (error) { -- cgit v1.1 From 9b20d7fc5250f50660c4dd4bea52e54e373b678d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 1 Dec 2017 17:57:02 -0500 Subject: mqueue: clean prepare_open() up Signed-off-by: Al Viro --- ipc/mqueue.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'ipc') diff --git a/ipc/mqueue.c b/ipc/mqueue.c index e8a872e..ce498ff 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -721,26 +721,19 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro, MAY_READ | MAY_WRITE }; int acc; - if (oflag & O_CREAT) { - if (d_really_is_positive(dentry)) { /* entry already exists */ - audit_inode(name, dentry, 0); - if (oflag & O_EXCL) - return -EEXIST; - } else { - if (ro) - return ro; - - audit_inode_parent_hidden(name, dentry->d_parent); - return vfs_mkobj(dentry, mode & ~current_umask(), - mqueue_create_attr, attr); - } - } else { - if (d_really_is_negative(dentry)) { + if (d_really_is_negative(dentry)) { + if (!(oflag & O_CREAT)) return -ENOENT; - } else { - audit_inode(name, dentry, 0); - } + if (ro) + return ro; + audit_inode_parent_hidden(name, dentry->d_parent); + return vfs_mkobj(dentry, mode & ~current_umask(), + mqueue_create_attr, attr); } + /* it already existed */ + audit_inode(name, dentry, 0); + if ((oflag & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) + return -EEXIST; if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) return -EINVAL; acc = oflag2acc[oflag & O_ACCMODE]; -- cgit v1.1 From a713fd7f529e361d42aa9dcc511e3fd3016599fe Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 1 Dec 2017 18:01:09 -0500 Subject: tidy do_mq_open() up a bit Signed-off-by: Al Viro --- ipc/mqueue.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'ipc') diff --git a/ipc/mqueue.c b/ipc/mqueue.c index ce498ff..9f05837 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -743,12 +743,11 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro, static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, struct mq_attr *attr) { - struct path path; + struct vfsmount *mnt = current->nsproxy->ipc_ns->mq_mnt; + struct dentry *root = mnt->mnt_root; struct filename *name; + struct path path; int fd, error; - struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; - struct vfsmount *mnt = ipc_ns->mq_mnt; - struct dentry *root = mnt->mnt_root; int ro; audit_mq_open(oflag, mode, attr); @@ -761,7 +760,6 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, goto out_putname; ro = mnt_want_write(mnt); /* we'll drop it in any case */ - error = 0; inode_lock(d_inode(root)); path.dentry = lookup_one_len(name->name, root, strlen(name->name)); if (IS_ERR(path.dentry)) { @@ -769,7 +767,6 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, goto out_putfd; } 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()); -- cgit v1.1 From 36735a6a2b5e042db1af956ce4bcc13f3ff99e21 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 25 Dec 2017 19:43:35 -0500 Subject: mqueue: switch to on-demand creation of internal mount Instead of doing that upon each ipcns creation, we do that the first time mq_open(2) or mqueue mount is done in an ipcns. What's more, doing that allows to get rid of mount_ns() use - we can go with considerably cheaper mount_nodev(), avoiding the loop over all mqueue superblock instances; ipcns->mq_mnt is used to locate preexisting instance in O(1) time instead of O(instances) mount_ns() would've cost us. Based upon the version by Giuseppe Scrivano ; I've added handling of userland mqueue mounts (original had been broken in that area) and added a switch to mount_nodev(). Signed-off-by: Al Viro --- ipc/mqueue.c | 74 ++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 19 deletions(-) (limited to 'ipc') diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 9f05837..7385791 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -325,8 +325,9 @@ err: static int mqueue_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; - struct ipc_namespace *ns = sb->s_fs_info; + struct ipc_namespace *ns = data; + sb->s_fs_info = ns; sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV; sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; @@ -343,18 +344,44 @@ static int mqueue_fill_super(struct super_block *sb, void *data, int silent) return 0; } +static struct file_system_type mqueue_fs_type; +/* + * Return value is pinned only by reference in ->mq_mnt; it will + * live until ipcns dies. Caller does not need to drop it. + */ +static struct vfsmount *mq_internal_mount(void) +{ + struct ipc_namespace *ns = current->nsproxy->ipc_ns; + struct vfsmount *m = ns->mq_mnt; + if (m) + return m; + m = kern_mount_data(&mqueue_fs_type, ns); + spin_lock(&mq_lock); + if (unlikely(ns->mq_mnt)) { + spin_unlock(&mq_lock); + if (!IS_ERR(m)) + kern_unmount(m); + return ns->mq_mnt; + } + if (!IS_ERR(m)) + ns->mq_mnt = m; + spin_unlock(&mq_lock); + return m; +} + static struct dentry *mqueue_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { - struct ipc_namespace *ns; - if (flags & SB_KERNMOUNT) { - ns = data; - data = NULL; - } else { - ns = current->nsproxy->ipc_ns; - } - return mount_ns(fs_type, flags, data, ns, ns->user_ns, mqueue_fill_super); + struct vfsmount *m; + if (flags & SB_KERNMOUNT) + return mount_nodev(fs_type, flags, data, mqueue_fill_super); + m = mq_internal_mount(); + if (IS_ERR(m)) + return ERR_CAST(m); + atomic_inc(&m->mnt_sb->s_active); + down_write(&m->mnt_sb->s_umount); + return dget(m->mnt_root); } static void init_once(void *foo) @@ -743,13 +770,16 @@ static int prepare_open(struct dentry *dentry, int oflag, int ro, static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, struct mq_attr *attr) { - struct vfsmount *mnt = current->nsproxy->ipc_ns->mq_mnt; - struct dentry *root = mnt->mnt_root; + struct vfsmount *mnt = mq_internal_mount(); + struct dentry *root; struct filename *name; struct path path; int fd, error; int ro; + if (IS_ERR(mnt)) + return PTR_ERR(mnt); + audit_mq_open(oflag, mode, attr); if (IS_ERR(name = getname(u_name))) @@ -760,6 +790,7 @@ static int do_mq_open(const char __user *u_name, int oflag, umode_t mode, goto out_putname; ro = mnt_want_write(mnt); /* we'll drop it in any case */ + root = mnt->mnt_root; inode_lock(d_inode(root)); path.dentry = lookup_one_len(name->name, root, strlen(name->name)); if (IS_ERR(path.dentry)) { @@ -808,6 +839,9 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; struct vfsmount *mnt = ipc_ns->mq_mnt; + if (!mnt) + return -ENOENT; + name = getname(u_name); if (IS_ERR(name)) return PTR_ERR(name); @@ -1534,28 +1568,26 @@ int mq_init_ns(struct ipc_namespace *ns) ns->mq_msgsize_max = DFLT_MSGSIZEMAX; ns->mq_msg_default = DFLT_MSG; ns->mq_msgsize_default = DFLT_MSGSIZE; + ns->mq_mnt = NULL; - ns->mq_mnt = kern_mount_data(&mqueue_fs_type, ns); - if (IS_ERR(ns->mq_mnt)) { - int err = PTR_ERR(ns->mq_mnt); - ns->mq_mnt = NULL; - return err; - } return 0; } void mq_clear_sbinfo(struct ipc_namespace *ns) { - ns->mq_mnt->mnt_sb->s_fs_info = NULL; + if (ns->mq_mnt) + ns->mq_mnt->mnt_sb->s_fs_info = NULL; } void mq_put_mnt(struct ipc_namespace *ns) { - kern_unmount(ns->mq_mnt); + if (ns->mq_mnt) + kern_unmount(ns->mq_mnt); } static int __init init_mqueue_fs(void) { + struct vfsmount *m; int error; mqueue_inode_cachep = kmem_cache_create("mqueue_inode_cache", @@ -1577,6 +1609,10 @@ static int __init init_mqueue_fs(void) if (error) goto out_filesystem; + m = kern_mount_data(&mqueue_fs_type, &init_ipc_ns); + if (IS_ERR(m)) + goto out_filesystem; + init_ipc_ns.mq_mnt = m; return 0; out_filesystem: -- cgit v1.1