diff options
-rw-r--r-- | drivers/vfio/vfio.c | 19 | ||||
-rw-r--r-- | fs/eventpoll.c | 2 | ||||
-rw-r--r-- | fs/namei.c | 2 | ||||
-rw-r--r-- | include/linux/kref.h | 18 | ||||
-rw-r--r-- | ipc/mqueue.c | 61 |
5 files changed, 56 insertions, 46 deletions
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 9591e2b509d7..17830c9c7cc6 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c | |||
@@ -264,6 +264,7 @@ static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group) | |||
264 | return group; | 264 | return group; |
265 | } | 265 | } |
266 | 266 | ||
267 | /* called with vfio.group_lock held */ | ||
267 | static void vfio_group_release(struct kref *kref) | 268 | static void vfio_group_release(struct kref *kref) |
268 | { | 269 | { |
269 | struct vfio_group *group = container_of(kref, struct vfio_group, kref); | 270 | struct vfio_group *group = container_of(kref, struct vfio_group, kref); |
@@ -287,13 +288,7 @@ static void vfio_group_release(struct kref *kref) | |||
287 | 288 | ||
288 | static void vfio_group_put(struct vfio_group *group) | 289 | static void vfio_group_put(struct vfio_group *group) |
289 | { | 290 | { |
290 | mutex_lock(&vfio.group_lock); | 291 | kref_put_mutex(&group->kref, vfio_group_release, &vfio.group_lock); |
291 | /* | ||
292 | * Release needs to unlock to unregister the notifier, so only | ||
293 | * unlock if not released. | ||
294 | */ | ||
295 | if (!kref_put(&group->kref, vfio_group_release)) | ||
296 | mutex_unlock(&vfio.group_lock); | ||
297 | } | 292 | } |
298 | 293 | ||
299 | /* Assume group_lock or group reference is held */ | 294 | /* Assume group_lock or group reference is held */ |
@@ -401,7 +396,6 @@ static void vfio_device_release(struct kref *kref) | |||
401 | struct vfio_device, kref); | 396 | struct vfio_device, kref); |
402 | struct vfio_group *group = device->group; | 397 | struct vfio_group *group = device->group; |
403 | 398 | ||
404 | mutex_lock(&group->device_lock); | ||
405 | list_del(&device->group_next); | 399 | list_del(&device->group_next); |
406 | mutex_unlock(&group->device_lock); | 400 | mutex_unlock(&group->device_lock); |
407 | 401 | ||
@@ -416,8 +410,9 @@ static void vfio_device_release(struct kref *kref) | |||
416 | /* Device reference always implies a group reference */ | 410 | /* Device reference always implies a group reference */ |
417 | static void vfio_device_put(struct vfio_device *device) | 411 | static void vfio_device_put(struct vfio_device *device) |
418 | { | 412 | { |
419 | kref_put(&device->kref, vfio_device_release); | 413 | struct vfio_group *group = device->group; |
420 | vfio_group_put(device->group); | 414 | kref_put_mutex(&device->kref, vfio_device_release, &group->device_lock); |
415 | vfio_group_put(group); | ||
421 | } | 416 | } |
422 | 417 | ||
423 | static void vfio_device_get(struct vfio_device *device) | 418 | static void vfio_device_get(struct vfio_device *device) |
@@ -1116,10 +1111,10 @@ static int vfio_group_get_device_fd(struct vfio_group *group, char *buf) | |||
1116 | */ | 1111 | */ |
1117 | filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); | 1112 | filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); |
1118 | 1113 | ||
1119 | fd_install(ret, filep); | ||
1120 | |||
1121 | vfio_device_get(device); | 1114 | vfio_device_get(device); |
1122 | atomic_inc(&group->container_users); | 1115 | atomic_inc(&group->container_users); |
1116 | |||
1117 | fd_install(ret, filep); | ||
1123 | break; | 1118 | break; |
1124 | } | 1119 | } |
1125 | mutex_unlock(&group->device_lock); | 1120 | mutex_unlock(&group->device_lock); |
diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 1c8b55670804..eedec84c1809 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c | |||
@@ -1654,8 +1654,8 @@ SYSCALL_DEFINE1(epoll_create1, int, flags) | |||
1654 | error = PTR_ERR(file); | 1654 | error = PTR_ERR(file); |
1655 | goto out_free_fd; | 1655 | goto out_free_fd; |
1656 | } | 1656 | } |
1657 | fd_install(fd, file); | ||
1658 | ep->file = file; | 1657 | ep->file = file; |
1658 | fd_install(fd, file); | ||
1659 | return fd; | 1659 | return fd; |
1660 | 1660 | ||
1661 | out_free_fd: | 1661 | out_free_fd: |
diff --git a/fs/namei.c b/fs/namei.c index db76b866a097..dd1ed1b8e98e 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -352,6 +352,7 @@ int __inode_permission(struct inode *inode, int mask) | |||
352 | /** | 352 | /** |
353 | * sb_permission - Check superblock-level permissions | 353 | * sb_permission - Check superblock-level permissions |
354 | * @sb: Superblock of inode to check permission on | 354 | * @sb: Superblock of inode to check permission on |
355 | * @inode: Inode to check permission on | ||
355 | * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) | 356 | * @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) |
356 | * | 357 | * |
357 | * Separate out file-system wide checks from inode-specific permission checks. | 358 | * Separate out file-system wide checks from inode-specific permission checks. |
@@ -656,6 +657,7 @@ int sysctl_protected_hardlinks __read_mostly = 1; | |||
656 | /** | 657 | /** |
657 | * may_follow_link - Check symlink following for unsafe situations | 658 | * may_follow_link - Check symlink following for unsafe situations |
658 | * @link: The path of the symlink | 659 | * @link: The path of the symlink |
660 | * @nd: nameidata pathwalk data | ||
659 | * | 661 | * |
660 | * In the case of the sysctl_protected_symlinks sysctl being enabled, | 662 | * In the case of the sysctl_protected_symlinks sysctl being enabled, |
661 | * CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is | 663 | * CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is |
diff --git a/include/linux/kref.h b/include/linux/kref.h index 9c07dcebded7..65af6887872f 100644 --- a/include/linux/kref.h +++ b/include/linux/kref.h | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/bug.h> | 18 | #include <linux/bug.h> |
19 | #include <linux/atomic.h> | 19 | #include <linux/atomic.h> |
20 | #include <linux/kernel.h> | 20 | #include <linux/kernel.h> |
21 | #include <linux/mutex.h> | ||
21 | 22 | ||
22 | struct kref { | 23 | struct kref { |
23 | atomic_t refcount; | 24 | atomic_t refcount; |
@@ -93,4 +94,21 @@ static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref) | |||
93 | { | 94 | { |
94 | return kref_sub(kref, 1, release); | 95 | return kref_sub(kref, 1, release); |
95 | } | 96 | } |
97 | |||
98 | static inline int kref_put_mutex(struct kref *kref, | ||
99 | void (*release)(struct kref *kref), | ||
100 | struct mutex *lock) | ||
101 | { | ||
102 | WARN_ON(release == NULL); | ||
103 | if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) { | ||
104 | mutex_lock(lock); | ||
105 | if (unlikely(!atomic_dec_and_test(&kref->refcount))) { | ||
106 | mutex_unlock(lock); | ||
107 | return 0; | ||
108 | } | ||
109 | release(kref); | ||
110 | return 1; | ||
111 | } | ||
112 | return 0; | ||
113 | } | ||
96 | #endif /* _KREF_H_ */ | 114 | #endif /* _KREF_H_ */ |
diff --git a/ipc/mqueue.c b/ipc/mqueue.c index f8e54f5b9080..9a08acc9e649 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c | |||
@@ -726,7 +726,6 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir, | |||
726 | struct mq_attr *attr) | 726 | struct mq_attr *attr) |
727 | { | 727 | { |
728 | const struct cred *cred = current_cred(); | 728 | const struct cred *cred = current_cred(); |
729 | struct file *result; | ||
730 | int ret; | 729 | int ret; |
731 | 730 | ||
732 | if (attr) { | 731 | if (attr) { |
@@ -748,21 +747,11 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir, | |||
748 | } | 747 | } |
749 | 748 | ||
750 | mode &= ~current_umask(); | 749 | mode &= ~current_umask(); |
751 | ret = mnt_want_write(path->mnt); | ||
752 | if (ret) | ||
753 | return ERR_PTR(ret); | ||
754 | ret = vfs_create(dir, path->dentry, mode, true); | 750 | ret = vfs_create(dir, path->dentry, mode, true); |
755 | path->dentry->d_fsdata = NULL; | 751 | path->dentry->d_fsdata = NULL; |
756 | if (!ret) | 752 | if (ret) |
757 | result = dentry_open(path, oflag, cred); | 753 | return ERR_PTR(ret); |
758 | else | 754 | return dentry_open(path, oflag, cred); |
759 | result = ERR_PTR(ret); | ||
760 | /* | ||
761 | * dentry_open() took a persistent mnt_want_write(), | ||
762 | * so we can now drop this one. | ||
763 | */ | ||
764 | mnt_drop_write(path->mnt); | ||
765 | return result; | ||
766 | } | 755 | } |
767 | 756 | ||
768 | /* Opens existing queue */ | 757 | /* Opens existing queue */ |
@@ -788,7 +777,9 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, | |||
788 | struct mq_attr attr; | 777 | struct mq_attr attr; |
789 | int fd, error; | 778 | int fd, error; |
790 | struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; | 779 | struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; |
791 | struct dentry *root = ipc_ns->mq_mnt->mnt_root; | 780 | struct vfsmount *mnt = ipc_ns->mq_mnt; |
781 | struct dentry *root = mnt->mnt_root; | ||
782 | int ro; | ||
792 | 783 | ||
793 | if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr))) | 784 | if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr))) |
794 | return -EFAULT; | 785 | return -EFAULT; |
@@ -802,6 +793,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, | |||
802 | if (fd < 0) | 793 | if (fd < 0) |
803 | goto out_putname; | 794 | goto out_putname; |
804 | 795 | ||
796 | ro = mnt_want_write(mnt); /* we'll drop it in any case */ | ||
805 | error = 0; | 797 | error = 0; |
806 | mutex_lock(&root->d_inode->i_mutex); | 798 | mutex_lock(&root->d_inode->i_mutex); |
807 | path.dentry = lookup_one_len(name, root, strlen(name)); | 799 | path.dentry = lookup_one_len(name, root, strlen(name)); |
@@ -809,7 +801,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, | |||
809 | error = PTR_ERR(path.dentry); | 801 | error = PTR_ERR(path.dentry); |
810 | goto out_putfd; | 802 | goto out_putfd; |
811 | } | 803 | } |
812 | path.mnt = mntget(ipc_ns->mq_mnt); | 804 | path.mnt = mntget(mnt); |
813 | 805 | ||
814 | if (oflag & O_CREAT) { | 806 | if (oflag & O_CREAT) { |
815 | if (path.dentry->d_inode) { /* entry already exists */ | 807 | if (path.dentry->d_inode) { /* entry already exists */ |
@@ -820,6 +812,10 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, | |||
820 | } | 812 | } |
821 | filp = do_open(&path, oflag); | 813 | filp = do_open(&path, oflag); |
822 | } else { | 814 | } else { |
815 | if (ro) { | ||
816 | error = ro; | ||
817 | goto out; | ||
818 | } | ||
823 | filp = do_create(ipc_ns, root->d_inode, | 819 | filp = do_create(ipc_ns, root->d_inode, |
824 | &path, oflag, mode, | 820 | &path, oflag, mode, |
825 | u_attr ? &attr : NULL); | 821 | u_attr ? &attr : NULL); |
@@ -845,6 +841,7 @@ out_putfd: | |||
845 | fd = error; | 841 | fd = error; |
846 | } | 842 | } |
847 | mutex_unlock(&root->d_inode->i_mutex); | 843 | mutex_unlock(&root->d_inode->i_mutex); |
844 | mnt_drop_write(mnt); | ||
848 | out_putname: | 845 | out_putname: |
849 | putname(name); | 846 | putname(name); |
850 | return fd; | 847 | return fd; |
@@ -857,40 +854,38 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) | |||
857 | struct dentry *dentry; | 854 | struct dentry *dentry; |
858 | struct inode *inode = NULL; | 855 | struct inode *inode = NULL; |
859 | struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; | 856 | struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; |
857 | struct vfsmount *mnt = ipc_ns->mq_mnt; | ||
860 | 858 | ||
861 | name = getname(u_name); | 859 | name = getname(u_name); |
862 | if (IS_ERR(name)) | 860 | if (IS_ERR(name)) |
863 | return PTR_ERR(name); | 861 | return PTR_ERR(name); |
864 | 862 | ||
865 | mutex_lock_nested(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex, | 863 | err = mnt_want_write(mnt); |
866 | I_MUTEX_PARENT); | 864 | if (err) |
867 | dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name)); | 865 | goto out_name; |
866 | mutex_lock_nested(&mnt->mnt_root->d_inode->i_mutex, I_MUTEX_PARENT); | ||
867 | dentry = lookup_one_len(name, mnt->mnt_root, strlen(name)); | ||
868 | if (IS_ERR(dentry)) { | 868 | if (IS_ERR(dentry)) { |
869 | err = PTR_ERR(dentry); | 869 | err = PTR_ERR(dentry); |
870 | goto out_unlock; | 870 | goto out_unlock; |
871 | } | 871 | } |
872 | 872 | ||
873 | if (!dentry->d_inode) { | ||
874 | err = -ENOENT; | ||
875 | goto out_err; | ||
876 | } | ||
877 | |||
878 | inode = dentry->d_inode; | 873 | inode = dentry->d_inode; |
879 | if (inode) | 874 | if (!inode) { |
875 | err = -ENOENT; | ||
876 | } else { | ||
880 | ihold(inode); | 877 | ihold(inode); |
881 | err = mnt_want_write(ipc_ns->mq_mnt); | 878 | err = vfs_unlink(dentry->d_parent->d_inode, dentry); |
882 | if (err) | 879 | } |
883 | goto out_err; | ||
884 | err = vfs_unlink(dentry->d_parent->d_inode, dentry); | ||
885 | mnt_drop_write(ipc_ns->mq_mnt); | ||
886 | out_err: | ||
887 | dput(dentry); | 880 | dput(dentry); |
888 | 881 | ||
889 | out_unlock: | 882 | out_unlock: |
890 | mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex); | 883 | mutex_unlock(&mnt->mnt_root->d_inode->i_mutex); |
891 | putname(name); | ||
892 | if (inode) | 884 | if (inode) |
893 | iput(inode); | 885 | iput(inode); |
886 | mnt_drop_write(mnt); | ||
887 | out_name: | ||
888 | putname(name); | ||
894 | 889 | ||
895 | return err; | 890 | return err; |
896 | } | 891 | } |