diff options
author | Vegard Nossum <vegard.nossum@oracle.com> | 2017-05-09 03:39:59 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2017-05-22 16:21:16 -0400 |
commit | 4d6501dce079c1eb6bf0b1d8f528a5e81770109e (patch) | |
tree | 652f4e0fccb85573bde0ada8e74c15ff0295fb3f /kernel/fork.c | |
parent | 5ea30e4e58040cfd6434c2f33dc3ea76e2c15b05 (diff) |
kthread: Fix use-after-free if kthread fork fails
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.
Debugged-by: Jamie Iles <jamie.iles@oracle.com>
Fixes: 1da5c46fa965 ("kthread: Make struct kthread kmalloc'ed")
Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jamie Iles <jamie.iles@oracle.com>
Cc: stable@vger.kernel.org
Link: http://lkml.kernel.org/r/20170509073959.17858-1-vegard.nossum@oracle.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/fork.c')
-rw-r--r-- | kernel/fork.c | 17 |
1 files changed, 12 insertions, 5 deletions
diff --git a/kernel/fork.c b/kernel/fork.c index d681f8f10d2d..b7cdea10239c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1553,6 +1553,18 @@ static __latent_entropy struct task_struct *copy_process( | |||
1553 | if (!p) | 1553 | if (!p) |
1554 | goto fork_out; | 1554 | goto fork_out; |
1555 | 1555 | ||
1556 | /* | ||
1557 | * This _must_ happen before we call free_task(), i.e. before we jump | ||
1558 | * to any of the bad_fork_* labels. This is to avoid freeing | ||
1559 | * p->set_child_tid which is (ab)used as a kthread's data pointer for | ||
1560 | * kernel threads (PF_KTHREAD). | ||
1561 | */ | ||
1562 | p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; | ||
1563 | /* | ||
1564 | * Clear TID on mm_release()? | ||
1565 | */ | ||
1566 | p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr : NULL; | ||
1567 | |||
1556 | ftrace_graph_init_task(p); | 1568 | ftrace_graph_init_task(p); |
1557 | 1569 | ||
1558 | rt_mutex_init_task(p); | 1570 | rt_mutex_init_task(p); |
@@ -1716,11 +1728,6 @@ static __latent_entropy struct task_struct *copy_process( | |||
1716 | } | 1728 | } |
1717 | } | 1729 | } |
1718 | 1730 | ||
1719 | p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; | ||
1720 | /* | ||
1721 | * Clear TID on mm_release()? | ||
1722 | */ | ||
1723 | p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr : NULL; | ||
1724 | #ifdef CONFIG_BLOCK | 1731 | #ifdef CONFIG_BLOCK |
1725 | p->plug = NULL; | 1732 | p->plug = NULL; |
1726 | #endif | 1733 | #endif |