ACK: [maverick CVE 1/1] inotify: fix double free/corruption of stuct user
Stefan Bader
stefan.bader at canonical.com
Fri Oct 7 12:38:58 UTC 2011
On 07.10.2011 14:27, Andy Whitcroft wrote:
> From: Eric Paris <eparis at redhat.com>
>
> On an error path in inotify_init1 a normal user can trigger a double
> free of struct user. This is a regression introduced by a2ae4cc9a16e
> ("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 at redhat.com>
> Cc: stable at kernel.org (2.6.37 and up)
> Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
>
> (cherry picked from commit d0de4dc584ec6aa3b26fffea320a8457827768fc)
> CVE-2011-1479
> BugLink: http://bugs.launchpad.net/bugs/869203
> Signed-off-by: Andy Whitcroft <apw at canonical.com>
> ---
> fs/notify/inotify/inotify_fsnotify.c | 1 +
> fs/notify/inotify/inotify_user.c | 39 +++++++++++----------------------
> 2 files changed, 14 insertions(+), 26 deletions(-)
>
> diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c
> index e27960c..518e1bc 100644
> --- a/fs/notify/inotify/inotify_fsnotify.c
> +++ b/fs/notify/inotify/inotify_fsnotify.c
> @@ -147,6 +147,7 @@ static void inotify_free_group_priv(struct fsnotify_group *group)
> idr_for_each(&group->inotify_data.idr, idr_callback, group);
> idr_remove_all(&group->inotify_data.idr);
> idr_destroy(&group->inotify_data.idr);
> + atomic_dec(&group->inotify_data.user->inotify_devs);
> free_uid(group->inotify_data.user);
> }
>
> diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
> index 72f8825..7b1a2c3 100644
> --- a/fs/notify/inotify/inotify_user.c
> +++ b/fs/notify/inotify/inotify_user.c
> @@ -290,15 +290,12 @@ 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;
>
> fsnotify_clear_marks_by_group(group);
>
> /* free this group, matching get was inotify_init->fsnotify_obtain_group */
> fsnotify_put_group(group);
>
> - atomic_dec(&user->inotify_devs);
> -
> return 0;
> }
>
> @@ -616,7 +613,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;
> unsigned int grp_num;
> @@ -632,8 +629,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;
> }
> @@ -643,7 +646,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. */
> @@ -653,31 +655,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;
> }
>
More information about the kernel-team
mailing list