lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20200810072515.632-1-walter-zh.wu@mediatek.com>
Date:   Mon, 10 Aug 2020 15:25:15 +0800
From:   Walter Wu <walter-zh.wu@...iatek.com>
To:     Andrey Ryabinin <aryabinin@...tuozzo.com>,
        Alexander Potapenko <glider@...gle.com>,
        Dmitry Vyukov <dvyukov@...gle.com>,
        Matthias Brugger <matthias.bgg@...il.com>,
        Andrew Morton <akpm@...ux-foundation.org>,
        Tejun Heo <tj@...nel.org>,
        Lai Jiangshan <jiangshanlai@...il.com>
CC:     <kasan-dev@...glegroups.com>, <linux-mm@...ck.org>,
        <linux-kernel@...r.kernel.org>,
        <linux-arm-kernel@...ts.infradead.org>,
        wsd_upstream <wsd_upstream@...iatek.com>,
        <linux-mediatek@...ts.infradead.org>,
        Walter Wu <walter-zh.wu@...iatek.com>
Subject: [PATCH 2/5] workqueue: kasan: record and print workqueue stack

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@...iatek.com>
Cc: Andrey Ryabinin <aryabinin@...tuozzo.com>
Cc: Dmitry Vyukov <dvyukov@...gle.com>
Cc: Alexander Potapenko <glider@...gle.com>
Cc: Tejun Heo <tj@...nel.org>
Cc: Lai Jiangshan <jiangshanlai@...il.com>
Cc: Andrew Morton <akpm@...ux-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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ