kernel/kmod: fix use-after-free of the sub_info structure
PATCH 1/1 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.
--- kernel/kmod.c | 74 ++++++++++++++++++++++++++--------------------------------- 1 file changed, 33 insertions(+), 41 deletions(-)
kernel/kmod.c +33 -41
@@ -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);
Signature · 1 line
1.8.5.5
Quoted 3 lines · Martin Schwidefsky
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
Quoted 3 lines
For
UMH_NO_WAIT__call_usermodehelper()frees thesub_infostructure directly, forUMH_WAIT_EXECthe call toumh_complete()allowscall_usermodehelper_exec()to continue which then freessub_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.
Quoted 6 lines · Tetsuo Handa
For both
UMH_NO_WAITandUMH_WAIT_EXECcases,
kernel_thread(call_helper, sub_info, CLONE_VFORK | SIGCHLD)in
__call_usermodehelper()waits fordo_execve()to succeed ordo_exit(),
Well, not really. kernel_thread(CLONE_VFORK) waits for do_exit() or exec_mmap(), and exec can fail after that.
Oleg.
Re: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure
Quoted 10 lines · Oleg Nesterov
Quoted 6 lines · Tetsuo Handa
For both
UMH_NO_WAITandUMH_WAIT_EXECcases,
kernel_thread(call_helper, sub_info, CLONE_VFORK | SIGCHLD)in
__call_usermodehelper()waits fordo_execve()to succeed ordo_exit(),Well, not really.
kernel_thread(CLONE_VFORK)waits fordo_exit()orexec_mmap(), and exec can fail after that.
Ah, I see. Here is a draft of an updated patch.
Can we rewrite
Quoted 2 lines · Martin Schwidefsky
for
UMH_WAIT_EXECthe call toumh_complete()allowscall_usermodehelper_exec()to continue which then freessub_info.
lines?
By the way, it seems to me that nothing prevents
if (info->cleanup)
(*info->cleanup)(info);Quoted 1 line
from crashing when
info->cleanuppoints 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.
--- kernel/kmod.c | 66 +++++++++++++++++++++++++-------------------------------- 1 files changed, 29 insertions(+), 37 deletions(-)
kernel/kmod.c +29 -37
@@ -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);
Signature · 1 line
1.7.1
Re: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure
Quoted 2 lines · Tetsuo Handa
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.
Quoted 8 lines
By the way, it seems to me that nothing prevents
if (info->cleanup) (*info->cleanup)(info);from crashing when
info->cleanuppoints to a function in a loadable kernel module and the loadable kernel module got unloaded before the worker thread callscall_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.
Re: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure
Quoted 15 lines · On Thu, 16 Oct 2014 23:58:34 +0200 Oleg Nesterov <[email protected]> wrote:
Quoted 2 lines · Tetsuo Handa
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); > }
Quoted 4 lines
?
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.
Quoted 3 lines
I also agree that the changelog could mention
exec_mmap. Plus a comment aboutUMH_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.
Signature · 4 lines
blue skies, Martin. "Reality continues to ruin my life." - Calvin.
Martin Schwidefsky
Reviewed-by Oleg NesterovRe: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure
In reply to Oleg NesterovQuoted 3 lines · 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 aboutUMH_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.
--- kernel/kmod.c | 76 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 39 deletions(-)
kernel/kmod.c +37 -39
@@ -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);
Signature · 1 line
1.8.5.5
Quoted 8 lines · Martin Schwidefsky
Quoted 3 lines · Oleg Nesterov
I also agree that the changelog could mention
exec_mmap. Plus a comment aboutUMH_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_execvecan 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.
Oleg Nesterov
Reviewed-byRe: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure
In reply to Martin SchwidefskyQuoted 9 lines · Martin Schwidefsky
Quoted 3 lines · 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 aboutUMH_NO_WAIT&&sub_info->complete== NULL. So yes, perhaps v2 makes sense if Martin agrees.Version 2 of the patch.
Thanks!
Quoted 3 lines
All change requests have gone in except for the
mention ofexec_mmap. I don't quite get the relevance of it,do_execvecan 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
cover (Was: kernel/kmod: fix use-after-free of the sub_infostructure)
In reply to Martin SchwidefskyQuoted 2 lines · Martin Schwidefsky
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(-)
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.
--- kernel/kmod.c | 12 +++--------- 1 files changed, 3 insertions(+), 9 deletions(-)
kernel/kmod.c +3 -9
@@ -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 ; }
Signature · 1 line
1.5.5.1
PATCH 2/2 usermodehelper: kill the kmod_thread_locker logic
In reply to Oleg NesterovNow 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.
--- kernel/kmod.c | 33 +++------------------------------ 1 files changed, 3 insertions(+), 30 deletions(-)
kernel/kmod.c +3 -30
@@ -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.
Signature · 1 line
1.5.5.1
Re: [PATCH 0/2] (Was: kernel/kmod: fix use-after-free of thesub_infostructure)
In reply to Oleg NesterovQuoted 4 lines · Oleg Nesterov
Quoted 2 lines · Martin Schwidefsky
Version 2 of the patch.
Quoted 9 lines
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.
Re: [PATCH] kernel/kmod: fix use-after-free of the sub_infostructure
In reply to Tetsuo HandaQuoted 11 lines · On Fri, 17 Oct 2014 06:30:29 +0900 Tetsuo Handa…
Regarding
UMH_NO_WAIT, thesub_infostructure can be freed by__call_usermodehelper()before the worker thread returns fromdo_execve(), allowing memory corruption whendo_execve()failed afterexec_mmap()is called.Regarding
UMH_WAIT_EXEC, the call toumh_complete()allowscall_usermodehelper_exec()to continue which then freessub_info.To fix this race, we need to make sure that the call to
call_usermodehelper_freeinfo()incall_usermodehelper_exec()is always made after the last store tosub_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.
Signature · 4 lines
blue skies, Martin. "Reality continues to ruin my life." - Calvin.
Quoted 5 lines · Martin Schwidefsky
There is a use-after-free bug on the
subprocess_infostructure allocated by the user mode helper. In casedo_execve()returns with an error____call_usermodehelper()stores the error code tosub_info->retval, butsub_infocan already have been freed.
Hmm, yes... do_execve() can fail after mm_release(). CLONE_VFORK doesn't help in this case.
Quoted 9 lines
kernel/kmod.c +0
-5@@ -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:
kernel/kmod.c static int ____call_usermodehelper(void *data) · line 274
Quoted 10 lines
kernel/kmod.c +7
-1+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.
kernel/kmod.c int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) · line 583
Quoted 6 lines
kernel/kmod.c +1
-1@@ -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.
Oleg Nesterov
Reviewed-byQuoted 3 lines · Oleg Nesterov
OK... I am wondering if
__call_usermodehelper()still needsCLONE_VFORKwith this patch.
Yes, looks like it doesn't, but this needs another patch.
Quoted 10 lines
kernel/kmod.c int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) · line 583
Quoted 6 lines
kernel/kmod.c +1
-1@@ -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,
Quoted 6 lines
- we own
sub_info, theUMH_KILLABLEcaller has gone away.
+ we ownsub_info, theUMH_KILLABLEcaller has gone away
+ or the caller usedUMH_NO_WAIT.The patch looks correct at first glance. I'll try to re-read it later once again.