@@ -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 */
@@ -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);
@@ -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)
{
@@ -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
@@ -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
}
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 <walter-zh.wu@mediatek.com> Cc: Andrey Ryabinin <aryabinin@virtuozzo.com> 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(-)