[PATCH 2/5] workqueue: kasan: record and print workqueue stack
From: Walter Wu <hidden>
Date: 2020-08-10 07:25:27
Also in:
linux-mediatek, linux-mm, lkml
Subsystem:
kasan, memory management, the rest, workqueue · Maintainers:
Andrey Ryabinin, Andrew Morton, Linus Torvalds, Tejun Heo
This patch records the last two enqueueing work call stacks on workqueue and prints up to 2 workqueue stacks in KASAN report. It is useful for programmers to solve use-after-free or double-free memory wq issue. When queue_work() is called, then queue the work into a workqueue, we store this call stack in order to print it in KASAN report. Signed-off-by: Walter Wu <redacted> Cc: Andrey Ryabinin <redacted> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Alexander Potapenko <glider@google.com> Cc: Tejun Heo <tj@kernel.org> Cc: Lai Jiangshan <jiangshanlai@gmail.com> Cc: Andrew Morton <akpm@linux-foundation.org> --- include/linux/kasan.h | 2 ++ kernel/workqueue.c | 3 +++ mm/kasan/generic.c | 21 +++++++++++++++++++++ mm/kasan/kasan.h | 8 +++++--- mm/kasan/report.c | 11 +++++++++++ 5 files changed, 42 insertions(+), 3 deletions(-)
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 43ae040ae9b2..687cbf2faf83 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h@@ -174,6 +174,7 @@ void kasan_cache_shrink(struct kmem_cache *cache); void kasan_cache_shutdown(struct kmem_cache *cache); void kasan_record_aux_stack(void *ptr); void kasan_record_tmr_stack(void *ptr); +void kasan_record_wq_stack(void *ptr); #else /* CONFIG_KASAN_GENERIC */
@@ -181,6 +182,7 @@ static inline void kasan_cache_shrink(struct kmem_cache *cache) {} static inline void kasan_cache_shutdown(struct kmem_cache *cache) {} static inline void kasan_record_aux_stack(void *ptr) {} static inline void kasan_record_tmr_stack(void *ptr) {} +static inline void kasan_record_wq_stack(void *ptr) {} #endif /* CONFIG_KASAN_GENERIC */
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index c41c3c17b86a..0e5963e06730 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c@@ -1324,6 +1324,9 @@ static void insert_work(struct pool_workqueue *pwq, struct work_struct *work, { struct worker_pool *pool = pwq->pool; + /* record the work in order to print it in KASAN reports */ + kasan_record_wq_stack(work); + /* we own @work, set data and link */ set_work_pwq(work, pwq, extra_flags); list_add_tail(&work->entry, head);
diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
index 627792d11569..592dc58fbe42 100644
--- a/mm/kasan/generic.c
+++ b/mm/kasan/generic.c@@ -367,6 +367,27 @@ void kasan_record_tmr_stack(void *addr) alloc_info->tmr_stack[0] = kasan_save_stack(GFP_NOWAIT); } +void kasan_record_wq_stack(void *addr) +{ + struct page *page = kasan_addr_to_page(addr); + struct kmem_cache *cache; + struct kasan_alloc_meta *alloc_info; + void *object; + + if (!(page && PageSlab(page))) + return; + + cache = page->slab_cache; + object = nearest_obj(cache, page, addr); + alloc_info = get_alloc_info(cache, object); + + /* + * record the last two workqueue stacks. + */ + alloc_info->wq_stack[1] = alloc_info->wq_stack[0]; + alloc_info->wq_stack[0] = kasan_save_stack(GFP_NOWAIT); +} + void kasan_set_free_info(struct kmem_cache *cache, void *object, u8 tag) {
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 4059f327767c..a4f76b1bde0a 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h@@ -108,12 +108,14 @@ struct kasan_alloc_meta { struct kasan_track alloc_track; #ifdef CONFIG_KASAN_GENERIC /* - * call_rcu() call stack and timer queueing stack are stored - * into struct kasan_alloc_meta. - * The free stack is stored into struct kasan_free_meta. + * call_rcu() call stack, timer queueing stack, and workqueue + * queueing stack are stored into kasan_alloc_meta. + * + * With generic KASAN the free stack is stored into kasan_free_meta. */ depot_stack_handle_t aux_stack[2]; depot_stack_handle_t tmr_stack[2]; + depot_stack_handle_t wq_stack[2]; #else struct kasan_track free_track[KASAN_NR_FREE_STACKS]; #endif
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index f602f090d90b..e6bc470fcd0a 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c@@ -203,6 +203,17 @@ static void describe_object(struct kmem_cache *cache, void *object, print_stack(alloc_info->tmr_stack[1]); pr_err("\n"); } + + if (alloc_info->wq_stack[0]) { + pr_err("Last workqueue stack:\n"); + print_stack(alloc_info->wq_stack[0]); + pr_err("\n"); + } + if (alloc_info->wq_stack[1]) { + pr_err("Second to last workqueue stack:\n"); + print_stack(alloc_info->wq_stack[1]); + pr_err("\n"); + } #endif }
--
2.18.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel