Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1636478 > unrolled thread
| Started by | Vegard Nossum <vegard.nossum@oracle.com> |
|---|---|
| First post | 2017-05-05 18:30 +0200 |
| Last post | 2017-05-06 22:00 +0200 |
| Articles | 4 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH] kthread: fix use-after-free if kthread fork fails Vegard Nossum <vegard.nossum@oracle.com> - 2017-05-05 18:30 +0200
Re: [PATCH] kthread: fix use-after-free if kthread fork fails Oleg Nesterov <oleg@redhat.com> - 2017-05-05 18:50 +0200
Re: [PATCH] kthread: fix use-after-free if kthread fork fails Vegard Nossum <vegard.nossum@oracle.com> - 2017-05-05 19:20 +0200
Re: [PATCH] kthread: fix use-after-free if kthread fork fails Oleg Nesterov <oleg@redhat.com> - 2017-05-06 22:00 +0200
| From | Vegard Nossum <vegard.nossum@oracle.com> |
|---|---|
| Date | 2017-05-05 18:30 +0200 |
| Subject | [PATCH] kthread: fix use-after-free if kthread fork fails |
| Message-ID | <tDOhc-46o-3@gated-at.bofh.it> |
If a kthread forks (e.g. usermodehelper since commit 1da5c46fa965) but
fails in copy_process() between calling dup_task_struct() and setting
p->set_child_tid, then the value of p->set_child_tid will be inherited
from the parent and get prematurely freed by free_kthread_struct().
kthread()
- worker_thread()
- process_one_work()
| - call_usermodehelper_exec_work()
| - kernel_thread()
| - _do_fork()
| - copy_process()
| - dup_task_struct()
| - arch_dup_task_struct()
| - tsk->set_child_tid = current->set_child_tid // implied
| - ...
| - goto bad_fork_*
| - ...
| - free_task(tsk)
| - free_kthread_struct(tsk)
| - kfree(tsk->set_child_tid)
- ...
- schedule()
- __schedule()
- wq_worker_sleeping()
- kthread_data(task)->flags // UAF
The problem started showing up with commit 1da5c46fa965 since it reused
->set_child_tid for the kthread worker data.
A better long-term solution might be to get rid of the ->set_child_tid
abuse. The comment in set_kthread_struct() also looks slightly wrong.
Fixes: 1da5c46fa965ff90f5ffc080b6ab3fae5e227bc3 ("kthread: Make struct kthread kmalloc'ed")
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Andy Lutomirski <luto@kernel.org>
Debugged-by: Jamie Iles <jamie.iles@oracle.com>
Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com>
---
kernel/fork.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/kernel/fork.c b/kernel/fork.c
index dd5a371c392a..fbdc29365b83 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -518,6 +518,13 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
atomic_set(&tsk->stack_refcount, 1);
#endif
+ /*
+ * Forking kthreads (e.g. usermodehelper) should not inherit this
+ * field since it's a pointer to a 'struct kthread' which is not
+ * reference counted.
+ */
+ tsk->set_child_tid = NULL;
+
if (err)
goto free_stack;
--
2.12.0.rc0
[toc] | [next] | [standalone]
| From | Oleg Nesterov <oleg@redhat.com> |
|---|---|
| Date | 2017-05-05 18:50 +0200 |
| Message-ID | <tDOAx-4hv-3@gated-at.bofh.it> |
| In reply to | #1636478 |
On 05/05, Vegard Nossum wrote: > > If a kthread forks (e.g. usermodehelper since commit 1da5c46fa965) but > fails in copy_process() between calling dup_task_struct() and setting > p->set_child_tid, then the value of p->set_child_tid will be inherited > from the parent and get prematurely freed by free_kthread_struct(). Aaah... thanks! > --- a/kernel/fork.c > +++ b/kernel/fork.c > @@ -518,6 +518,13 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) > atomic_set(&tsk->stack_refcount, 1); > #endif > > + /* > + * Forking kthreads (e.g. usermodehelper) should not inherit this > + * field since it's a pointer to a 'struct kthread' which is not > + * reference counted. > + */ > + tsk->set_child_tid = NULL; > + Can't we just move both p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; /* * Clear TID on mm_release()? */ p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr : NULL; lines here? Oleg.
[toc] | [prev] | [next] | [standalone]
| From | Vegard Nossum <vegard.nossum@oracle.com> |
|---|---|
| Date | 2017-05-05 19:20 +0200 |
| Message-ID | <tDP3A-4H2-7@gated-at.bofh.it> |
| In reply to | #1636485 |
[Multipart message — attachments visible in raw view] — view raw
On 05/05/17 18:44, Oleg Nesterov wrote: > On 05/05, Vegard Nossum wrote: >> >> If a kthread forks (e.g. usermodehelper since commit 1da5c46fa965) but >> fails in copy_process() between calling dup_task_struct() and setting >> p->set_child_tid, then the value of p->set_child_tid will be inherited >> from the parent and get prematurely freed by free_kthread_struct(). > > Aaah... thanks! > >> --- a/kernel/fork.c >> +++ b/kernel/fork.c >> @@ -518,6 +518,13 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) >> atomic_set(&tsk->stack_refcount, 1); >> #endif >> >> + /* >> + * Forking kthreads (e.g. usermodehelper) should not inherit this >> + * field since it's a pointer to a 'struct kthread' which is not >> + * reference counted. >> + */ >> + tsk->set_child_tid = NULL; >> + > > Can't we just move both > > p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; > /* > * Clear TID on mm_release()? > */ > p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr : NULL; > > lines here? clone_flags is not available in dup_task_struct(), but we could move those lines higher in copy_process(). The reason we didn't do it was that we thought it was a little fragile/unobvious that this has to happen before free_task() is called and that it was safer to clear it in dup_task_struct() (which also contains zeroing of other fields). The newly attached patch has been tested and seems to work, if you prefer it. Vegard
[toc] | [prev] | [next] | [standalone]
| From | Oleg Nesterov <oleg@redhat.com> |
|---|---|
| Date | 2017-05-06 22:00 +0200 |
| Message-ID | <tEe1Y-3PE-7@gated-at.bofh.it> |
| In reply to | #1636507 |
On 05/05, Vegard Nossum wrote: > > On 05/05/17 18:44, Oleg Nesterov wrote: > > > >Can't we just move both > > > > p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; > > /* > > * Clear TID on mm_release()? > > */ > > p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr : NULL; > > > >lines here? > > clone_flags is not available in dup_task_struct(), but we could move > those lines higher in copy_process(). Yes, yes, this is what I meant. > The newly attached patch has been tested and seems to work, if you > prefer it. Yes, please, this loos a bit better simply because we do not need to set it twice. And I agree this needs cleanups. Even if we forget about this particular problem and the usage of set_child_tid, we should add copy_misc() which should absorb a lot of chaotic initializations from copy_process() imo. Oleg.
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web