Skip to content

Commit d0de4dc

Browse files
eparistorvalds
authored andcommitted
inotify: fix double free/corruption of stuct user
On an error path in inotify_init1 a normal user can trigger a double free of struct user. This is a regression introduced by a2ae4cc ("inotify: stop kernel memory leak on file creation failure"). We fix this by making sure that if a group exists the user reference is dropped when the group is cleaned up. We should not explictly drop the reference on error and also drop the reference when the group is cleaned up. The new lifetime rules are that an inotify group lives from inotify_new_group to the last fsnotify_put_group. Since the struct user and inotify_devs are directly tied to this lifetime they are only changed/updated in those two locations. We get rid of all special casing of struct user or user->inotify_devs. Signed-off-by: Eric Paris <eparis@redhat.com> Cc: stable@kernel.org (2.6.37 and up) Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 623dda6 commit d0de4dc

File tree

2 files changed

+14
-26
lines changed

2 files changed

+14
-26
lines changed

fs/notify/inotify/inotify_fsnotify.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ static void inotify_free_group_priv(struct fsnotify_group *group)
198198
idr_for_each(&group->inotify_data.idr, idr_callback, group);
199199
idr_remove_all(&group->inotify_data.idr);
200200
idr_destroy(&group->inotify_data.idr);
201+
atomic_dec(&group->inotify_data.user->inotify_devs);
201202
free_uid(group->inotify_data.user);
202203
}
203204

fs/notify/inotify/inotify_user.c

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,6 @@ static int inotify_fasync(int fd, struct file *file, int on)
290290
static int inotify_release(struct inode *ignored, struct file *file)
291291
{
292292
struct fsnotify_group *group = file->private_data;
293-
struct user_struct *user = group->inotify_data.user;
294293

295294
pr_debug("%s: group=%p\n", __func__, group);
296295

@@ -299,8 +298,6 @@ static int inotify_release(struct inode *ignored, struct file *file)
299298
/* free this group, matching get was inotify_init->fsnotify_obtain_group */
300299
fsnotify_put_group(group);
301300

302-
atomic_dec(&user->inotify_devs);
303-
304301
return 0;
305302
}
306303

@@ -697,7 +694,7 @@ static int inotify_update_watch(struct fsnotify_group *group, struct inode *inod
697694
return ret;
698695
}
699696

700-
static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsigned int max_events)
697+
static struct fsnotify_group *inotify_new_group(unsigned int max_events)
701698
{
702699
struct fsnotify_group *group;
703700

@@ -710,8 +707,14 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign
710707
spin_lock_init(&group->inotify_data.idr_lock);
711708
idr_init(&group->inotify_data.idr);
712709
group->inotify_data.last_wd = 0;
713-
group->inotify_data.user = user;
714710
group->inotify_data.fa = NULL;
711+
group->inotify_data.user = get_current_user();
712+
713+
if (atomic_inc_return(&group->inotify_data.user->inotify_devs) >
714+
inotify_max_user_instances) {
715+
fsnotify_put_group(group);
716+
return ERR_PTR(-EMFILE);
717+
}
715718

716719
return group;
717720
}
@@ -721,7 +724,6 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign
721724
SYSCALL_DEFINE1(inotify_init1, int, flags)
722725
{
723726
struct fsnotify_group *group;
724-
struct user_struct *user;
725727
int ret;
726728

727729
/* Check the IN_* constants for consistency. */
@@ -731,31 +733,16 @@ SYSCALL_DEFINE1(inotify_init1, int, flags)
731733
if (flags & ~(IN_CLOEXEC | IN_NONBLOCK))
732734
return -EINVAL;
733735

734-
user = get_current_user();
735-
if (unlikely(atomic_read(&user->inotify_devs) >=
736-
inotify_max_user_instances)) {
737-
ret = -EMFILE;
738-
goto out_free_uid;
739-
}
740-
741736
/* fsnotify_obtain_group took a reference to group, we put this when we kill the file in the end */
742-
group = inotify_new_group(user, inotify_max_queued_events);
743-
if (IS_ERR(group)) {
744-
ret = PTR_ERR(group);
745-
goto out_free_uid;
746-
}
747-
748-
atomic_inc(&user->inotify_devs);
737+
group = inotify_new_group(inotify_max_queued_events);
738+
if (IS_ERR(group))
739+
return PTR_ERR(group);
749740

750741
ret = anon_inode_getfd("inotify", &inotify_fops, group,
751742
O_RDONLY | flags);
752-
if (ret >= 0)
753-
return ret;
743+
if (ret < 0)
744+
fsnotify_put_group(group);
754745

755-
fsnotify_put_group(group);
756-
atomic_dec(&user->inotify_devs);
757-
out_free_uid:
758-
free_uid(user);
759746
return ret;
760747
}
761748

0 commit comments

Comments
 (0)