diff options
author | David Rientjes <rientjes@google.com> | 2016-09-01 19:15:07 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-09-01 20:52:01 -0400 |
commit | c11600e4fed67ae4cd6a8096936afd445410e8ed (patch) | |
tree | 0f71f8ca5028621c2615eb83699535fa8b0ffe84 | |
parent | 19feeff18bbfde659baa58c2346f15a24d7c405e (diff) |
mm, mempolicy: task->mempolicy must be NULL before dropping final reference
KASAN allocates memory from the page allocator as part of
kmem_cache_free(), and that can reference current->mempolicy through any
number of allocation functions. It needs to be NULL'd out before the
final reference is dropped to prevent a use-after-free bug:
BUG: KASAN: use-after-free in alloc_pages_current+0x363/0x370 at addr ffff88010b48102c
CPU: 0 PID: 15425 Comm: trinity-c2 Not tainted 4.8.0-rc2+ #140
...
Call Trace:
dump_stack
kasan_object_err
kasan_report_error
__asan_report_load2_noabort
alloc_pages_current <-- use after free
depot_save_stack
save_stack
kasan_slab_free
kmem_cache_free
__mpol_put <-- free
do_exit
This patch sets current->mempolicy to NULL before dropping the final
reference.
Link: http://lkml.kernel.org/r/alpine.DEB.2.10.1608301442180.63329@chino.kir.corp.google.com
Fixes: cd11016e5f52 ("mm, kasan: stackdepot implementation. Enable stackdepot for SLAB")
Signed-off-by: David Rientjes <rientjes@google.com>
Reported-by: Vegard Nossum <vegard.nossum@oracle.com>
Acked-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: <stable@vger.kernel.org> [4.6+]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/mempolicy.h | 4 | ||||
-rw-r--r-- | kernel/exit.c | 7 | ||||
-rw-r--r-- | mm/mempolicy.c | 17 |
3 files changed, 22 insertions, 6 deletions
diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h index 4429d255c8ab..5e5b2969d931 100644 --- a/include/linux/mempolicy.h +++ b/include/linux/mempolicy.h | |||
@@ -195,6 +195,7 @@ static inline bool vma_migratable(struct vm_area_struct *vma) | |||
195 | } | 195 | } |
196 | 196 | ||
197 | extern int mpol_misplaced(struct page *, struct vm_area_struct *, unsigned long); | 197 | extern int mpol_misplaced(struct page *, struct vm_area_struct *, unsigned long); |
198 | extern void mpol_put_task_policy(struct task_struct *); | ||
198 | 199 | ||
199 | #else | 200 | #else |
200 | 201 | ||
@@ -297,5 +298,8 @@ static inline int mpol_misplaced(struct page *page, struct vm_area_struct *vma, | |||
297 | return -1; /* no node preference */ | 298 | return -1; /* no node preference */ |
298 | } | 299 | } |
299 | 300 | ||
301 | static inline void mpol_put_task_policy(struct task_struct *task) | ||
302 | { | ||
303 | } | ||
300 | #endif /* CONFIG_NUMA */ | 304 | #endif /* CONFIG_NUMA */ |
301 | #endif | 305 | #endif |
diff --git a/kernel/exit.c b/kernel/exit.c index 2f974ae042a6..091a78be3b09 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -848,12 +848,7 @@ void do_exit(long code) | |||
848 | TASKS_RCU(preempt_enable()); | 848 | TASKS_RCU(preempt_enable()); |
849 | exit_notify(tsk, group_dead); | 849 | exit_notify(tsk, group_dead); |
850 | proc_exit_connector(tsk); | 850 | proc_exit_connector(tsk); |
851 | #ifdef CONFIG_NUMA | 851 | mpol_put_task_policy(tsk); |
852 | task_lock(tsk); | ||
853 | mpol_put(tsk->mempolicy); | ||
854 | tsk->mempolicy = NULL; | ||
855 | task_unlock(tsk); | ||
856 | #endif | ||
857 | #ifdef CONFIG_FUTEX | 852 | #ifdef CONFIG_FUTEX |
858 | if (unlikely(current->pi_state_cache)) | 853 | if (unlikely(current->pi_state_cache)) |
859 | kfree(current->pi_state_cache); | 854 | kfree(current->pi_state_cache); |
diff --git a/mm/mempolicy.c b/mm/mempolicy.c index d8c4e38fb5f4..2da72a5b6ecc 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c | |||
@@ -2336,6 +2336,23 @@ out: | |||
2336 | return ret; | 2336 | return ret; |
2337 | } | 2337 | } |
2338 | 2338 | ||
2339 | /* | ||
2340 | * Drop the (possibly final) reference to task->mempolicy. It needs to be | ||
2341 | * dropped after task->mempolicy is set to NULL so that any allocation done as | ||
2342 | * part of its kmem_cache_free(), such as by KASAN, doesn't reference a freed | ||
2343 | * policy. | ||
2344 | */ | ||
2345 | void mpol_put_task_policy(struct task_struct *task) | ||
2346 | { | ||
2347 | struct mempolicy *pol; | ||
2348 | |||
2349 | task_lock(task); | ||
2350 | pol = task->mempolicy; | ||
2351 | task->mempolicy = NULL; | ||
2352 | task_unlock(task); | ||
2353 | mpol_put(pol); | ||
2354 | } | ||
2355 | |||
2339 | static void sp_delete(struct shared_policy *sp, struct sp_node *n) | 2356 | static void sp_delete(struct shared_policy *sp, struct sp_node *n) |
2340 | { | 2357 | { |
2341 | pr_debug("deleting %lx-l%lx\n", n->start, n->end); | 2358 | pr_debug("deleting %lx-l%lx\n", n->start, n->end); |