2014-10-16 14:00:52

by Martin Schwidefsky

[permalink] [raw]
Subject: [PATCH] kernel/kmod: fix use-after-free of the sub_info structure

Found this in the message log on a s390 system:

BUG kmalloc-192 (Not tainted): Poison overwritten
Disabling lock debugging due to kernel taint
INFO: 0x00000000684761f4-0x00000000684761f7. First byte 0xff instead of 0x6b
INFO: Allocated in call_usermodehelper_setup+0x70/0x128 age=71 cpu=2 pid=648
__slab_alloc.isra.47.constprop.56+0x5f6/0x658
kmem_cache_alloc_trace+0x106/0x408
call_usermodehelper_setup+0x70/0x128
call_usermodehelper+0x62/0x90
cgroup_release_agent+0x178/0x1c0
process_one_work+0x36e/0x680
worker_thread+0x2f0/0x4f8
kthread+0x10a/0x120
kernel_thread_starter+0x6/0xc
kernel_thread_starter+0x0/0xc
INFO: Freed in call_usermodehelper_exec+0x110/0x1b8 age=71 cpu=2 pid=648
__slab_free+0x94/0x560
kfree+0x364/0x3e0
call_usermodehelper_exec+0x110/0x1b8
cgroup_release_agent+0x178/0x1c0
process_one_work+0x36e/0x680
worker_thread+0x2f0/0x4f8
kthread+0x10a/0x120
kernel_thread_starter+0x6/0xc
kernel_thread_starter+0x0/0xc

There is a use-after-free bug on the subprocess_info structure allocated
by the user mode helper. In case do_execve() returns with an error
____call_usermodehelper() stores the error code to sub_info->retval,
but sub_info can already have been freed.

For UMH_NO_WAIT and UMH_WAIT_EXEC the call to kernel_thread() in
__call_usermodehelper() can return while do_execve() is still running.
For UMH_NO_WAIT __call_usermodehelper() frees the sub_info structure
directly, for UMH_WAIT_EXEC the call to umh_complete() allows
call_usermodehelper_exec() to continue which then frees sub_info.

To fix this race the code needs to make sure that the call to
call_usermodehelper_freeinfo() is always done after the last store
to sub_info->retval.

Signed-off-by: Martin Schwidefsky <[email protected]>
---
kernel/kmod.c | 74 ++++++++++++++++++++++++++---------------------------------
1 file changed, 33 insertions(+), 41 deletions(-)

diff --git a/kernel/kmod.c b/kernel/kmod.c
index 8637e04..641c44b 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -196,12 +196,33 @@ int __request_module(bool wait, const char *fmt, ...)
EXPORT_SYMBOL(__request_module);
#endif /* CONFIG_MODULES */

+static void call_usermodehelper_freeinfo(struct subprocess_info *info)
+{
+ if (info->cleanup)
+ (*info->cleanup)(info);
+ kfree(info);
+}
+
+static void umh_complete(struct subprocess_info *sub_info)
+{
+ struct completion *comp = xchg(&sub_info->complete, NULL);
+ /*
+ * See call_usermodehelper_exec(). If xchg() returns NULL
+ * we own sub_info, the UMH_KILLABLE caller has gone away.
+ */
+ if (comp)
+ complete(comp);
+ else
+ call_usermodehelper_freeinfo(sub_info);
+}
+
/*
* This is the task which runs the usermode application
*/
static int ____call_usermodehelper(void *data)
{
struct subprocess_info *sub_info = data;
+ int wait = sub_info->wait & ~UMH_KILLABLE;
struct cred *new;
int retval;

@@ -221,7 +242,7 @@ static int ____call_usermodehelper(void *data)
retval = -ENOMEM;
new = prepare_kernel_cred(current);
if (!new)
- goto fail;
+ goto out;

spin_lock(&umh_sysctl_lock);
new->cap_bset = cap_intersect(usermodehelper_bset, new->cap_bset);
@@ -233,7 +254,7 @@ static int ____call_usermodehelper(void *data)
retval = sub_info->init(sub_info, new);
if (retval) {
abort_creds(new);
- goto fail;
+ goto out;
}
}

@@ -242,13 +263,14 @@ static int ____call_usermodehelper(void *data)
retval = do_execve(getname_kernel(sub_info->path),
(const char __user *const __user *)sub_info->argv,
(const char __user *const __user *)sub_info->envp);
- if (!retval)
- return 0;
-
- /* Exec failed? */
-fail:
+out:
sub_info->retval = retval;
- do_exit(0);
+ if (wait != UMH_WAIT_PROC)
+ /* For UMH_WAIT_PROC wait_for_helper calls umh_complete */
+ umh_complete(sub_info);
+ if (retval)
+ do_exit(0);
+ return 0;
}

static int call_helper(void *data)
@@ -258,26 +280,6 @@ static int call_helper(void *data)
return ____call_usermodehelper(data);
}

-static void call_usermodehelper_freeinfo(struct subprocess_info *info)
-{
- if (info->cleanup)
- (*info->cleanup)(info);
- kfree(info);
-}
-
-static void umh_complete(struct subprocess_info *sub_info)
-{
- struct completion *comp = xchg(&sub_info->complete, NULL);
- /*
- * See call_usermodehelper_exec(). If xchg() returns NULL
- * we own sub_info, the UMH_KILLABLE caller has gone away.
- */
- if (comp)
- complete(comp);
- else
- call_usermodehelper_freeinfo(sub_info);
-}
-
/* Keventd can't block, but this (a child) can. */
static int wait_for_helper(void *data)
{
@@ -336,18 +338,8 @@ static void __call_usermodehelper(struct work_struct *work)
kmod_thread_locker = NULL;
}

- switch (wait) {
- case UMH_NO_WAIT:
- call_usermodehelper_freeinfo(sub_info);
- break;
-
- case UMH_WAIT_PROC:
- if (pid > 0)
- break;
- /* FALLTHROUGH */
- case UMH_WAIT_EXEC:
- if (pid < 0)
- sub_info->retval = pid;
+ if (pid < 0) {
+ sub_info->retval = pid;
umh_complete(sub_info);
}
}
@@ -588,7 +580,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
goto out;
}

- sub_info->complete = &done;
+ sub_info->complete = (wait == UMH_NO_WAIT) ? NULL : &done;
sub_info->wait = wait;

queue_work(khelper_wq, &sub_info->work);
--
1.8.5.5


2014-10-16 16:57:40

by Tetsuo Handa

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_info structure

Martin Schwidefsky wrote:
> Found this in the message log on a s390 system:
>
> BUG kmalloc-192 (Not tainted): Poison overwritten

The use-after-free location you are suspecting is

----------
retval = do_execve(getname_kernel(sub_info->path),
(const char __user *const __user *)sub_info->argv,
(const char __user *const __user *)sub_info->envp);
if (!retval)
return 0;

/* Exec failed? */
fail:
sub_info->retval = retval;
do_exit(0);
}
----------

, isn't it?

I could not interpret the

> For UMH_NO_WAIT __call_usermodehelper() frees the sub_info structure
> directly, for UMH_WAIT_EXEC the call to umh_complete() allows
> call_usermodehelper_exec() to continue which then frees sub_info.

lines.

For both UMH_NO_WAIT and UMH_WAIT_EXEC cases,

kernel_thread(call_helper, sub_info, CLONE_VFORK | SIGCHLD)

in __call_usermodehelper() waits for do_execve() to succeed or do_exit(),
doesn't it? What is wrong with assigning sub_info->retval in
____call_usermodehelper() when do_execve() did not succeed?
Which function/thread can free sub_info before assigning sub_info->retval?

Regards.

2014-10-16 17:40:45

by Oleg Nesterov

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_info structure

On 10/16, Martin Schwidefsky wrote:
>
> There is a use-after-free bug on the subprocess_info structure allocated
> by the user mode helper. In case do_execve() returns with an error
> ____call_usermodehelper() stores the error code to sub_info->retval,
> but sub_info can already have been freed.

Hmm, yes... do_execve() can fail after mm_release(). CLONE_VFORK doesn't
help in this case.

> @@ -242,13 +263,14 @@ static int ____call_usermodehelper(void *data)
> retval = do_execve(getname_kernel(sub_info->path),
> (const char __user *const __user *)sub_info->argv,
> (const char __user *const __user *)sub_info->envp);
> - if (!retval)
> - return 0;
> -
> - /* Exec failed? */
> -fail:
> +out:
> sub_info->retval = retval;
> - do_exit(0);
> + if (wait != UMH_WAIT_PROC)
> + /* For UMH_WAIT_PROC wait_for_helper calls umh_complete */
> + umh_complete(sub_info);
> + if (retval)
> + do_exit(0);
> + return 0;
> }

OK... I am wondering if __call_usermodehelper() still needs CLONE_VFORK
with this patch.

> @@ -588,7 +580,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
> goto out;
> }
>
> - sub_info->complete = &done;
> + sub_info->complete = (wait == UMH_NO_WAIT) ? NULL : &done;

This probably needs a comment, and the comment in umh_complete() should
be updated,

- we own sub_info, the UMH_KILLABLE caller has gone away.
+ we own sub_info, the UMH_KILLABLE caller has gone away
+ or the caller used UMH_NO_WAIT.

The patch looks correct at first glance. I'll try to re-read it later
once again.

Thanks!

Oleg.

2014-10-16 17:45:22

by Oleg Nesterov

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_info structure

On 10/17, Tetsuo Handa wrote:
>
> For both UMH_NO_WAIT and UMH_WAIT_EXEC cases,
>
> kernel_thread(call_helper, sub_info, CLONE_VFORK | SIGCHLD)
>
> in __call_usermodehelper() waits for do_execve() to succeed or do_exit(),

Well, not really. kernel_thread(CLONE_VFORK) waits for do_exit() or
exec_mmap(), and exec can fail after that.

Oleg.

2014-10-16 20:19:38

by Oleg Nesterov

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_info structure

On 10/16, Oleg Nesterov wrote:
>
> OK... I am wondering if __call_usermodehelper() still needs CLONE_VFORK
> with this patch.

Yes, looks like it doesn't, but this needs another patch.

> > @@ -588,7 +580,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
> > goto out;
> > }
> >
> > - sub_info->complete = &done;
> > + sub_info->complete = (wait == UMH_NO_WAIT) ? NULL : &done;
>
> This probably needs a comment, and the comment in umh_complete() should
> be updated,
>
> - we own sub_info, the UMH_KILLABLE caller has gone away.
> + we own sub_info, the UMH_KILLABLE caller has gone away
> + or the caller used UMH_NO_WAIT.
>
> The patch looks correct at first glance. I'll try to re-read it later
> once again.

Reviewed-by: Oleg Nesterov <[email protected]>

2014-10-16 21:30:43

by Tetsuo Handa

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure

Oleg Nesterov wrote:
> On 10/17, Tetsuo Handa wrote:
> >
> > For both UMH_NO_WAIT and UMH_WAIT_EXEC cases,
> >
> > kernel_thread(call_helper, sub_info, CLONE_VFORK | SIGCHLD)
> >
> > in __call_usermodehelper() waits for do_execve() to succeed or do_exit(),
>
> Well, not really. kernel_thread(CLONE_VFORK) waits for do_exit() or
> exec_mmap(), and exec can fail after that.

Ah, I see. Here is a draft of an updated patch.

Can we rewrite

Martin Schwidefsky wrote:
> for UMH_WAIT_EXEC the call to umh_complete() allows
> call_usermodehelper_exec() to continue which then frees sub_info.

lines?

By the way, it seems to me that nothing prevents

if (info->cleanup)
(*info->cleanup)(info);

>from crashing when info->cleanup points to a function in a loadable kernel
module and the loadable kernel module got unloaded before the worker thread
calls call_usermodehelper_freeinfo().
----------
[PATCH] kernel/kmod: fix use-after-free of the sub_info structure

A use-after-free bug was found on the subprocess_info structure allocated
by the user mode helper.

The message log on a s390 system:
-----
BUG kmalloc-192 (Not tainted): Poison overwritten
Disabling lock debugging due to kernel taint
INFO: 0x00000000684761f4-0x00000000684761f7. First byte 0xff instead of 0x6b
INFO: Allocated in call_usermodehelper_setup+0x70/0x128 age=71 cpu=2 pid=648
__slab_alloc.isra.47.constprop.56+0x5f6/0x658
kmem_cache_alloc_trace+0x106/0x408
call_usermodehelper_setup+0x70/0x128
call_usermodehelper+0x62/0x90
cgroup_release_agent+0x178/0x1c0
process_one_work+0x36e/0x680
worker_thread+0x2f0/0x4f8
kthread+0x10a/0x120
kernel_thread_starter+0x6/0xc
kernel_thread_starter+0x0/0xc
INFO: Freed in call_usermodehelper_exec+0x110/0x1b8 age=71 cpu=2 pid=648
__slab_free+0x94/0x560
kfree+0x364/0x3e0
call_usermodehelper_exec+0x110/0x1b8
cgroup_release_agent+0x178/0x1c0
process_one_work+0x36e/0x680
worker_thread+0x2f0/0x4f8
kthread+0x10a/0x120
kernel_thread_starter+0x6/0xc
kernel_thread_starter+0x0/0xc
-----

Regarding UMH_NO_WAIT, the sub_info structure can be freed by
__call_usermodehelper() before the worker thread returns from
do_execve(), allowing memory corruption when do_execve() failed
after exec_mmap() is called.

Regarding UMH_WAIT_EXEC, the call to umh_complete() allows
call_usermodehelper_exec() to continue which then frees sub_info.

To fix this race, we need to make sure that the call to
call_usermodehelper_freeinfo() in call_usermodehelper_exec() is
always made after the last store to sub_info->retval.

Signed-off-by: Martin Schwidefsky <[email protected]>
---
kernel/kmod.c | 66 +++++++++++++++++++++++++--------------------------------
1 files changed, 29 insertions(+), 37 deletions(-)

diff --git a/kernel/kmod.c b/kernel/kmod.c
index 8637e04..378b47f 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -196,12 +196,33 @@ int __request_module(bool wait, const char *fmt, ...)
EXPORT_SYMBOL(__request_module);
#endif /* CONFIG_MODULES */

+static void call_usermodehelper_freeinfo(struct subprocess_info *info)
+{
+ if (info->cleanup)
+ (*info->cleanup)(info);
+ kfree(info);
+}
+
+static void umh_complete(struct subprocess_info *sub_info)
+{
+ struct completion *comp = xchg(&sub_info->complete, NULL);
+ /*
+ * See call_usermodehelper_exec(). If xchg() returns NULL
+ * we own sub_info, the caller has gone away.
+ */
+ if (comp)
+ complete(comp);
+ else
+ call_usermodehelper_freeinfo(sub_info);
+}
+
/*
* This is the task which runs the usermode application
*/
static int ____call_usermodehelper(void *data)
{
struct subprocess_info *sub_info = data;
+ int wait = sub_info->wait & ~UMH_KILLABLE;
struct cred *new;
int retval;

@@ -242,12 +263,13 @@ static int ____call_usermodehelper(void *data)
retval = do_execve(getname_kernel(sub_info->path),
(const char __user *const __user *)sub_info->argv,
(const char __user *const __user *)sub_info->envp);
- if (!retval)
- return 0;
-
- /* Exec failed? */
fail:
sub_info->retval = retval;
+ /* wait_for_helper() will call umh_complete() if UMH_WAIT_PROC. */
+ if (wait != UMH_WAIT_PROC)
+ umh_complete(sub_info);
+ if (!retval)
+ return 0;
do_exit(0);
}

@@ -258,26 +280,6 @@ static int call_helper(void *data)
return ____call_usermodehelper(data);
}

-static void call_usermodehelper_freeinfo(struct subprocess_info *info)
-{
- if (info->cleanup)
- (*info->cleanup)(info);
- kfree(info);
-}
-
-static void umh_complete(struct subprocess_info *sub_info)
-{
- struct completion *comp = xchg(&sub_info->complete, NULL);
- /*
- * See call_usermodehelper_exec(). If xchg() returns NULL
- * we own sub_info, the UMH_KILLABLE caller has gone away.
- */
- if (comp)
- complete(comp);
- else
- call_usermodehelper_freeinfo(sub_info);
-}
-
/* Keventd can't block, but this (a child) can. */
static int wait_for_helper(void *data)
{
@@ -336,18 +338,8 @@ static void __call_usermodehelper(struct work_struct *work)
kmod_thread_locker = NULL;
}

- switch (wait) {
- case UMH_NO_WAIT:
- call_usermodehelper_freeinfo(sub_info);
- break;
-
- case UMH_WAIT_PROC:
- if (pid > 0)
- break;
- /* FALLTHROUGH */
- case UMH_WAIT_EXEC:
- if (pid < 0)
- sub_info->retval = pid;
+ if (pid < 0) {
+ sub_info->retval = pid;
umh_complete(sub_info);
}
}
@@ -588,7 +580,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
goto out;
}

- sub_info->complete = &done;
+ sub_info->complete = (wait == UMH_NO_WAIT) ? NULL : &done;
sub_info->wait = wait;

queue_work(khelper_wq, &sub_info->work);
--
1.7.1

2014-10-16 22:01:45

by Oleg Nesterov

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure

On 10/17, Tetsuo Handa wrote:
>
> Ah, I see. Here is a draft of an updated patch.

Do you mean this part

> sub_info->retval = retval;
> + /* wait_for_helper() will call umh_complete() if UMH_WAIT_PROC. */
> + if (wait != UMH_WAIT_PROC)
> + umh_complete(sub_info);
> + if (!retval)
> + return 0;
> do_exit(0);
> }

?

Personally I agree, this looks a bit better to me. But this is cosmetic
and subjective, I leave this to Martin ;)

I also agree that the changelog could mention exec_mmap. Plus a comment
about UMH_NO_WAIT && sub_info->complete == NULL. So yes, perhaps v2 makes
sense if Martin agrees.

> By the way, it seems to me that nothing prevents
>
> if (info->cleanup)
> (*info->cleanup)(info);
>
> from crashing when info->cleanup points to a function in a loadable kernel
> module and the loadable kernel module got unloaded before the worker thread
> calls call_usermodehelper_freeinfo().

Just don't do this? I mean, in this case the caller of call_usermodehelper()
is obviously buggy? Or I missed your point?

Oleg.

2014-10-17 07:04:43

by Martin Schwidefsky

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure

On Fri, 17 Oct 2014 06:30:29 +0900
Tetsuo Handa <[email protected]> wrote:

> Regarding UMH_NO_WAIT, the sub_info structure can be freed by
> __call_usermodehelper() before the worker thread returns from
> do_execve(), allowing memory corruption when do_execve() failed
> after exec_mmap() is called.
>
> Regarding UMH_WAIT_EXEC, the call to umh_complete() allows
> call_usermodehelper_exec() to continue which then frees sub_info.
>
> To fix this race, we need to make sure that the call to
> call_usermodehelper_freeinfo() in call_usermodehelper_exec() is
> always made after the last store to sub_info->retval.

I like this improved description for the UMH_NO_WAIT and UMH_WAIT_EXEC
cases. I mix it with parts of the original description.

--
blue skies,
Martin.

"Reality continues to ruin my life." - Calvin.

2014-10-17 07:05:03

by Martin Schwidefsky

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure

On Thu, 16 Oct 2014 23:58:34 +0200
Oleg Nesterov <[email protected]> wrote:

> On 10/17, Tetsuo Handa wrote:
> >
> > Ah, I see. Here is a draft of an updated patch.
>
> Do you mean this part
>
> > sub_info->retval = retval;
> > + /* wait_for_helper() will call umh_complete() if UMH_WAIT_PROC. */
> > + if (wait != UMH_WAIT_PROC)
> > + umh_complete(sub_info);
> > + if (!retval)
> > + return 0;
> > do_exit(0);
> > }
>
> ?
>
> Personally I agree, this looks a bit better to me. But this is cosmetic
> and subjective, I leave this to Martin ;)

I don't mind the different code order. As you seem to prefer the above I
use your version.

> I also agree that the changelog could mention exec_mmap. Plus a comment
> about UMH_NO_WAIT && sub_info->complete == NULL. So yes, perhaps v2 makes
> sense if Martin agrees.

Ok, this part is a bit tricky and deserves a comment. I'll create one
and send v2.

--
blue skies,
Martin.

"Reality continues to ruin my life." - Calvin.

2014-10-17 07:37:03

by Martin Schwidefsky

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure

On Thu, 16 Oct 2014 23:58:34 +0200
Oleg Nesterov <[email protected]> wrote:

> I also agree that the changelog could mention exec_mmap. Plus a comment
> about UMH_NO_WAIT && sub_info->complete == NULL. So yes, perhaps v2 makes
> sense if Martin agrees.

Version 2 of the patch. All change requests have gone in except for the
mention of exec_mmap. I don't quite get the relevance of it, do_execve
can fail for the various reasons.
---
Subject: [PATCH] kernel/kmod: fix use-after-free of the sub_info structure

From: Martin Schwidefsky <[email protected]>

Found this in the message log on a s390 system:

BUG kmalloc-192 (Not tainted): Poison overwritten
Disabling lock debugging due to kernel taint
INFO: 0x00000000684761f4-0x00000000684761f7. First byte 0xff instead of 0x6b
INFO: Allocated in call_usermodehelper_setup+0x70/0x128 age=71 cpu=2 pid=648
__slab_alloc.isra.47.constprop.56+0x5f6/0x658
kmem_cache_alloc_trace+0x106/0x408
call_usermodehelper_setup+0x70/0x128
call_usermodehelper+0x62/0x90
cgroup_release_agent+0x178/0x1c0
process_one_work+0x36e/0x680
worker_thread+0x2f0/0x4f8
kthread+0x10a/0x120
kernel_thread_starter+0x6/0xc
kernel_thread_starter+0x0/0xc
INFO: Freed in call_usermodehelper_exec+0x110/0x1b8 age=71 cpu=2 pid=648
__slab_free+0x94/0x560
kfree+0x364/0x3e0
call_usermodehelper_exec+0x110/0x1b8
cgroup_release_agent+0x178/0x1c0
process_one_work+0x36e/0x680
worker_thread+0x2f0/0x4f8
kthread+0x10a/0x120
kernel_thread_starter+0x6/0xc
kernel_thread_starter+0x0/0xc

There is a use-after-free bug on the subprocess_info structure allocated
by the user mode helper. In case do_execve() returns with an error
____call_usermodehelper() stores the error code to sub_info->retval,
but sub_info can already have been freed.

Regarding UMH_NO_WAIT, the sub_info structure can be freed by
__call_usermodehelper() before the worker thread returns from
do_execve(), allowing memory corruption when do_execve() failed
after exec_mmap() is called.

Regarding UMH_WAIT_EXEC, the call to umh_complete() allows
call_usermodehelper_exec() to continue which then frees sub_info.

To fix this race the code needs to make sure that the call to
call_usermodehelper_freeinfo() is always done after the last store
to sub_info->retval.

Signed-off-by: Martin Schwidefsky <[email protected]>
Reviewed-by: Oleg Nesterov <[email protected]>
Cc: Tetsuo Handa <[email protected]>
---
kernel/kmod.c | 76 +++++++++++++++++++++++++++++------------------------------
1 file changed, 37 insertions(+), 39 deletions(-)

diff --git a/kernel/kmod.c b/kernel/kmod.c
index 8637e04..80f7a6d 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -196,12 +196,34 @@ int __request_module(bool wait, const char *fmt, ...)
EXPORT_SYMBOL(__request_module);
#endif /* CONFIG_MODULES */

+static void call_usermodehelper_freeinfo(struct subprocess_info *info)
+{
+ if (info->cleanup)
+ (*info->cleanup)(info);
+ kfree(info);
+}
+
+static void umh_complete(struct subprocess_info *sub_info)
+{
+ struct completion *comp = xchg(&sub_info->complete, NULL);
+ /*
+ * See call_usermodehelper_exec(). If xchg() returns NULL
+ * we own sub_info, the UMH_KILLABLE caller has gone away
+ * or the caller used UMH_NO_WAIT.
+ */
+ if (comp)
+ complete(comp);
+ else
+ call_usermodehelper_freeinfo(sub_info);
+}
+
/*
* This is the task which runs the usermode application
*/
static int ____call_usermodehelper(void *data)
{
struct subprocess_info *sub_info = data;
+ int wait = sub_info->wait & ~UMH_KILLABLE;
struct cred *new;
int retval;

@@ -221,7 +243,7 @@ static int ____call_usermodehelper(void *data)
retval = -ENOMEM;
new = prepare_kernel_cred(current);
if (!new)
- goto fail;
+ goto out;

spin_lock(&umh_sysctl_lock);
new->cap_bset = cap_intersect(usermodehelper_bset, new->cap_bset);
@@ -233,7 +255,7 @@ static int ____call_usermodehelper(void *data)
retval = sub_info->init(sub_info, new);
if (retval) {
abort_creds(new);
- goto fail;
+ goto out;
}
}

@@ -242,12 +264,13 @@ static int ____call_usermodehelper(void *data)
retval = do_execve(getname_kernel(sub_info->path),
(const char __user *const __user *)sub_info->argv,
(const char __user *const __user *)sub_info->envp);
+out:
+ sub_info->retval = retval;
+ /* wait_for_helper() will call umh_complete if UHM_WAIT_PROC. */
+ if (wait != UMH_WAIT_PROC)
+ umh_complete(sub_info);
if (!retval)
return 0;
-
- /* Exec failed? */
-fail:
- sub_info->retval = retval;
do_exit(0);
}

@@ -258,26 +281,6 @@ static int call_helper(void *data)
return ____call_usermodehelper(data);
}

-static void call_usermodehelper_freeinfo(struct subprocess_info *info)
-{
- if (info->cleanup)
- (*info->cleanup)(info);
- kfree(info);
-}
-
-static void umh_complete(struct subprocess_info *sub_info)
-{
- struct completion *comp = xchg(&sub_info->complete, NULL);
- /*
- * See call_usermodehelper_exec(). If xchg() returns NULL
- * we own sub_info, the UMH_KILLABLE caller has gone away.
- */
- if (comp)
- complete(comp);
- else
- call_usermodehelper_freeinfo(sub_info);
-}
-
/* Keventd can't block, but this (a child) can. */
static int wait_for_helper(void *data)
{
@@ -336,18 +339,8 @@ static void __call_usermodehelper(struct work_struct *work)
kmod_thread_locker = NULL;
}

- switch (wait) {
- case UMH_NO_WAIT:
- call_usermodehelper_freeinfo(sub_info);
- break;
-
- case UMH_WAIT_PROC:
- if (pid > 0)
- break;
- /* FALLTHROUGH */
- case UMH_WAIT_EXEC:
- if (pid < 0)
- sub_info->retval = pid;
+ if (pid < 0) {
+ sub_info->retval = pid;
umh_complete(sub_info);
}
}
@@ -588,7 +581,12 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
goto out;
}

- sub_info->complete = &done;
+ /*
+ * Set the completion pointer only if there is a waiter.
+ * This makes it possible to use umh_complete to free
+ * the data structure in case of UMH_NO_WAIT.
+ */
+ sub_info->complete = (wait == UMH_NO_WAIT) ? NULL : &done;
sub_info->wait = wait;

queue_work(khelper_wq, &sub_info->work);
--
1.8.5.5

2014-10-17 12:55:28

by Tetsuo Handa

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_info structure

Martin Schwidefsky wrote:
> Oleg Nesterov <[email protected]> wrote:
> > I also agree that the changelog could mention exec_mmap. Plus a comment
> > about UMH_NO_WAIT && sub_info->complete == NULL. So yes, perhaps v2 makes
> > sense if Martin agrees.
>
> Version 2 of the patch. All change requests have gone in except for the
> mention of exec_mmap. I don't quite get the relevance of it, do_execve
> can fail for the various reasons.

OK. I understood that the

| For UMH_NO_WAIT __call_usermodehelper() frees the sub_info structure
| directly, for UMH_WAIT_EXEC the call to umh_complete() allows
| call_usermodehelper_exec() to continue which then frees sub_info.

lines described the following sequence.

----------
Race window for call_usermodehelper_exec(UMH_WAIT_EXEC) case is shown below.

(1) The caller enters into sleep at wait_for_completion()
in call_usermodehelper_exec().
(2) The khelper thread calls __call_usermodehelper() and enters into
sleep at kthread_create(CLONE_VFORK).
(3) The worker thread calls do_execve() in ____call_usermodehelper()
called from call_helper().
(4) complete_vfork_done() called from mm_release() called from exec_mmap()
called from flush_old_exec() called from binfmt handler wakes up
the khelper thread.
(5) The khelper thread calls umh_complete() and wakes up the caller.
(6) The caller fetches sub_info->retval (which is 0) and calls
call_usermodehelper_freeinfo().
(7) do_execve() returns a negative value due to an unexpected failure
after complete_vfork_done() was called.
(8) The worker thread tries to store sub_info->retval (which is a negative
value).

When hitting this race window, the caller fails to know that do_execve()
failed and the worker thread triggers use-after-free memory corruption.

The race window for call_usermodehelper_exec(UMH_NO_WAIT) case is similar
except that the caller does not enter into sleep at (1) and the khelper
thread calls call_usermodehelper_freeinfo() at (5).
----------

do_execve() can fail for the various reasons. But this race unlikely happens
because do_execve() seldom fails after complete_vfork_done() was called.

2014-10-17 15:25:13

by Oleg Nesterov

[permalink] [raw]
Subject: Re: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure

On 10/17, Martin Schwidefsky wrote:
>
> On Thu, 16 Oct 2014 23:58:34 +0200
> Oleg Nesterov <[email protected]> wrote:
>
> > I also agree that the changelog could mention exec_mmap. Plus a comment
> > about UMH_NO_WAIT && sub_info->complete == NULL. So yes, perhaps v2 makes
> > sense if Martin agrees.
>
> Version 2 of the patch.

Thanks!

> All change requests have gone in except for the
> mention of exec_mmap. I don't quite get the relevance of it, do_execve
> can fail for the various reasons.

Yes. But if it fails before exec_mmap() (which in particular calls
mm_release()->complete_vfork_done()) we are safe; sub_info can't go away
because the parent sleeps in sys_wait4() if UMH_WAIT_PROC, or it sleeps
in wait_for_vfork_done() otherwise.

But this is minor, and this only relates to the changelog. So still/again

Reviewed-by: Oleg Nesterov <[email protected]>

2014-10-17 19:19:11

by Oleg Nesterov

[permalink] [raw]
Subject: [PATCH 0/2] (Was: kernel/kmod: fix use-after-free of the sub_infostructure)

On 10/17, Martin Schwidefsky wrote:
>
> Version 2 of the patch.

On top of this patch.

Tetsuo, can you review?

Oleg.

kernel/kmod.c | 43 +++++--------------------------------------
1 files changed, 5 insertions(+), 38 deletions(-)

2014-10-17 19:19:35

by Oleg Nesterov

[permalink] [raw]
Subject: [PATCH 1/2] usermodehelper: don't use CLONE_VFORK for ____call_usermodehelper()

After the previous fix CLONE_VFORK in __call_usermodehelper() buys
nothing, we rely on on umh_complete() in ____call_usermodehelper()
anyway.

Remove it. This also eliminates the unnecessary sleep/wakeup in the
likely case, and this allows the next change.

While at it, kill the "int wait" locals in ____call_usermodehelper()
and __call_usermodehelper(), they can safely use sub_info->wait.

Signed-off-by: Oleg Nesterov <[email protected]>
---
kernel/kmod.c | 12 +++---------
1 files changed, 3 insertions(+), 9 deletions(-)

diff --git a/kernel/kmod.c b/kernel/kmod.c
index 80f7a6d..4621771 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -223,7 +223,6 @@ static void umh_complete(struct subprocess_info *sub_info)
static int ____call_usermodehelper(void *data)
{
struct subprocess_info *sub_info = data;
- int wait = sub_info->wait & ~UMH_KILLABLE;
struct cred *new;
int retval;

@@ -267,7 +266,7 @@ static int ____call_usermodehelper(void *data)
out:
sub_info->retval = retval;
/* wait_for_helper() will call umh_complete if UHM_WAIT_PROC. */
- if (wait != UMH_WAIT_PROC)
+ if (!(sub_info->wait & UMH_WAIT_PROC))
umh_complete(sub_info);
if (!retval)
return 0;
@@ -323,18 +322,13 @@ static void __call_usermodehelper(struct work_struct *work)
{
struct subprocess_info *sub_info =
container_of(work, struct subprocess_info, work);
- int wait = sub_info->wait & ~UMH_KILLABLE;
pid_t pid;

- /* CLONE_VFORK: wait until the usermode helper has execve'd
- * successfully We need the data structures to stay around
- * until that is done. */
- if (wait == UMH_WAIT_PROC)
+ if (sub_info->wait & UMH_WAIT_PROC)
pid = kernel_thread(wait_for_helper, sub_info,
CLONE_FS | CLONE_FILES | SIGCHLD);
else {
- pid = kernel_thread(call_helper, sub_info,
- CLONE_VFORK | SIGCHLD);
+ pid = kernel_thread(call_helper, sub_info, SIGCHLD);
/* Worker thread stopped blocking khelper thread. */
kmod_thread_locker = NULL;
}
--
1.5.5.1

2014-10-17 19:19:55

by Oleg Nesterov

[permalink] [raw]
Subject: [PATCH 2/2] usermodehelper: kill the kmod_thread_locker logic

Now that we do not call kernel_thread(CLONE_VFORK) from the worker
thread we can not deadlock if do_execve() in turn triggers another
call_usermodehelper(), we can remove the kmod_thread_locker code.

Note: we should probably kill khelper_wq and simply use one of the
global workqueues, say, system_unbound_wq, this special wq for umh
buys nothing nowadays.

Signed-off-by: Oleg Nesterov <[email protected]>
---
kernel/kmod.c | 33 +++------------------------------
1 files changed, 3 insertions(+), 30 deletions(-)

diff --git a/kernel/kmod.c b/kernel/kmod.c
index 4621771..2777f40 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -47,13 +47,6 @@ extern int max_threads;

static struct workqueue_struct *khelper_wq;

-/*
- * kmod_thread_locker is used for deadlock avoidance. There is no explicit
- * locking to protect this global - it is private to the singleton khelper
- * thread and should only ever be modified by that thread.
- */
-static const struct task_struct *kmod_thread_locker;
-
#define CAP_BSET (void *)1
#define CAP_PI (void *)2

@@ -273,13 +266,6 @@ out:
do_exit(0);
}

-static int call_helper(void *data)
-{
- /* Worker thread started blocking khelper thread. */
- kmod_thread_locker = current;
- return ____call_usermodehelper(data);
-}
-
/* Keventd can't block, but this (a child) can. */
static int wait_for_helper(void *data)
{
@@ -327,11 +313,9 @@ static void __call_usermodehelper(struct work_struct *work)
if (sub_info->wait & UMH_WAIT_PROC)
pid = kernel_thread(wait_for_helper, sub_info,
CLONE_FS | CLONE_FILES | SIGCHLD);
- else {
- pid = kernel_thread(call_helper, sub_info, SIGCHLD);
- /* Worker thread stopped blocking khelper thread. */
- kmod_thread_locker = NULL;
- }
+ else
+ pid = kernel_thread(____call_usermodehelper, sub_info,
+ SIGCHLD);

if (pid < 0) {
sub_info->retval = pid;
@@ -565,17 +549,6 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait)
goto out;
}
/*
- * Worker thread must not wait for khelper thread at below
- * wait_for_completion() if the thread was created with CLONE_VFORK
- * flag, for khelper thread is already waiting for the thread at
- * wait_for_completion() in do_fork().
- */
- if (wait != UMH_NO_WAIT && current == kmod_thread_locker) {
- retval = -EBUSY;
- goto out;
- }
-
- /*
* Set the completion pointer only if there is a waiter.
* This makes it possible to use umh_complete to free
* the data structure in case of UMH_NO_WAIT.
--
1.5.5.1

2014-10-17 23:54:38

by Tetsuo Handa

[permalink] [raw]
Subject: Re: [PATCH 0/2] (Was: kernel/kmod: fix use-after-free of thesub_infostructure)

Oleg Nesterov wrote:
> On 10/17, Martin Schwidefsky wrote:
> >
> > Version 2 of the patch.
>
> On top of this patch.
>
> Tetsuo, can you review?
>
> Oleg.
>
> kernel/kmod.c | 43 +++++--------------------------------------
> 1 files changed, 5 insertions(+), 38 deletions(-)
>
Looks OK to me.

Reviewed-by: Tetsuo Handa <[email protected]>