inotify: fix double free/corruption of stuct user
[pandora-kernel.git] / fs / notify / inotify / inotify_user.c
index bd46e7c..8445fbc 100644 (file)
@@ -290,7 +290,6 @@ static int inotify_fasync(int fd, struct file *file, int on)
 static int inotify_release(struct inode *ignored, struct file *file)
 {
        struct fsnotify_group *group = file->private_data;
-       struct user_struct *user = group->inotify_data.user;
 
        pr_debug("%s: group=%p\n", __func__, group);
 
@@ -299,8 +298,6 @@ static int inotify_release(struct inode *ignored, struct file *file)
        /* free this group, matching get was inotify_init->fsnotify_obtain_group */
        fsnotify_put_group(group);
 
-       atomic_dec(&user->inotify_devs);
-
        return 0;
 }
 
@@ -697,7 +694,7 @@ retry:
        return ret;
 }
 
-static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsigned int max_events)
+static struct fsnotify_group *inotify_new_group(unsigned int max_events)
 {
        struct fsnotify_group *group;
 
@@ -710,8 +707,14 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign
        spin_lock_init(&group->inotify_data.idr_lock);
        idr_init(&group->inotify_data.idr);
        group->inotify_data.last_wd = 0;
-       group->inotify_data.user = user;
        group->inotify_data.fa = NULL;
+       group->inotify_data.user = get_current_user();
+
+       if (atomic_inc_return(&group->inotify_data.user->inotify_devs) >
+           inotify_max_user_instances) {
+               fsnotify_put_group(group);
+               return ERR_PTR(-EMFILE);
+       }
 
        return group;
 }
@@ -721,7 +724,6 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign
 SYSCALL_DEFINE1(inotify_init1, int, flags)
 {
        struct fsnotify_group *group;
-       struct user_struct *user;
        int ret;
 
        /* Check the IN_* constants for consistency.  */
@@ -731,31 +733,16 @@ SYSCALL_DEFINE1(inotify_init1, int, flags)
        if (flags & ~(IN_CLOEXEC | IN_NONBLOCK))
                return -EINVAL;
 
-       user = get_current_user();
-       if (unlikely(atomic_read(&user->inotify_devs) >=
-                       inotify_max_user_instances)) {
-               ret = -EMFILE;
-               goto out_free_uid;
-       }
-
        /* fsnotify_obtain_group took a reference to group, we put this when we kill the file in the end */
-       group = inotify_new_group(user, inotify_max_queued_events);
-       if (IS_ERR(group)) {
-               ret = PTR_ERR(group);
-               goto out_free_uid;
-       }
-
-       atomic_inc(&user->inotify_devs);
+       group = inotify_new_group(inotify_max_queued_events);
+       if (IS_ERR(group))
+               return PTR_ERR(group);
 
        ret = anon_inode_getfd("inotify", &inotify_fops, group,
                                  O_RDONLY | flags);
-       if (ret >= 0)
-               return ret;
+       if (ret < 0)
+               fsnotify_put_group(group);
 
-       fsnotify_put_group(group);
-       atomic_dec(&user->inotify_devs);
-out_free_uid:
-       free_uid(user);
        return ret;
 }