[PATCH 1/5] timer: kasan: record and print timer stack

Walter Wu walter-zh.wu at mediatek.com
Mon Aug 10 03:23:13 EDT 2020


This patch records the last two timer queueing stacks and prints
up to 2 timer stacks in KASAN report. It is useful for programmers
to solve use-after-free or double-free memory timer issues.

When timer_setup() or timer_setup_on_stack() is called, then it
prepares to use this timer and sets timer callback, we store
this call stack in order to print it in KASAN report.

Signed-off-by: Walter Wu <walter-zh.wu at mediatek.com>
Cc: Andrey Ryabinin <aryabinin at virtuozzo.com>
Cc: Dmitry Vyukov <dvyukov at google.com>
Cc: Alexander Potapenko <glider at google.com>
Cc: Thomas Gleixner <tglx at linutronix.de>
Cc: John Stultz <john.stultz at linaro.org>
Cc: Stephen Boyd <sboyd at kernel.org>
Cc: Andrew Morton <akpm at linux-foundation.org>
---
 include/linux/kasan.h |  2 ++
 kernel/time/timer.c   |  2 ++
 mm/kasan/generic.c    | 21 +++++++++++++++++++++
 mm/kasan/kasan.h      |  4 +++-
 mm/kasan/report.c     | 11 +++++++++++
 5 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index 23b7ee00572d..763664b36dc6 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -175,12 +175,14 @@ static inline size_t kasan_metadata_size(struct kmem_cache *cache) { return 0; }
 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);
 
 #else /* CONFIG_KASAN_GENERIC */
 
 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) {}
 
 #endif /* CONFIG_KASAN_GENERIC */
 
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index a5221abb4594..ef2da9ddfac7 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -783,6 +783,8 @@ static void do_init_timer(struct timer_list *timer,
 	timer->function = func;
 	timer->flags = flags | raw_smp_processor_id();
 	lockdep_init_map(&timer->lockdep_map, name, key, 0);
+
+	kasan_record_tmr_stack(timer);
 }
 
 /**
diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
index 4b3cbad7431b..f35dcec990ab 100644
--- a/mm/kasan/generic.c
+++ b/mm/kasan/generic.c
@@ -347,6 +347,27 @@ void kasan_record_aux_stack(void *addr)
 	alloc_info->aux_stack[0] = kasan_save_stack(GFP_NOWAIT);
 }
 
+void kasan_record_tmr_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 timer stacks.
+	 */
+	alloc_info->tmr_stack[1] = alloc_info->tmr_stack[0];
+	alloc_info->tmr_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 ef655a1c6e15..c50827f388a3 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -108,10 +108,12 @@ struct kasan_alloc_meta {
 	struct kasan_track alloc_track;
 #ifdef CONFIG_KASAN_GENERIC
 	/*
-	 * call_rcu() call stack is stored into struct kasan_alloc_meta.
+	 * 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.
 	 */
 	depot_stack_handle_t aux_stack[2];
+	depot_stack_handle_t tmr_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 fed3c8fdfd25..6fa3bfee381f 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -191,6 +191,17 @@ static void describe_object(struct kmem_cache *cache, void *object,
 			print_stack(alloc_info->aux_stack[1]);
 			pr_err("\n");
 		}
+
+		if (alloc_info->tmr_stack[0]) {
+			pr_err("Last timer stack:\n");
+			print_stack(alloc_info->tmr_stack[0]);
+			pr_err("\n");
+		}
+		if (alloc_info->tmr_stack[1]) {
+			pr_err("Second to last timer stack:\n");
+			print_stack(alloc_info->tmr_stack[1]);
+			pr_err("\n");
+		}
 #endif
 	}
 
-- 
2.18.0


More information about the Linux-mediatek mailing list