aboutsummaryrefslogtreecommitdiffstats
path: root/mm/kasan
diff options
context:
space:
mode:
authorAlexander Potapenko <glider@google.com>2016-03-25 17:22:08 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-25 19:37:42 -0400
commitcd11016e5f5212c13c0cec7384a525edc93b4921 (patch)
tree31e2efd8d8eb6be398ccd0789bb9e865b299dc68 /mm/kasan
parentbe7635e7287e0e8013af3c89a6354a9e0182594c (diff)
mm, kasan: stackdepot implementation. Enable stackdepot for SLAB
Implement the stack depot and provide CONFIG_STACKDEPOT. Stack depot will allow KASAN store allocation/deallocation stack traces for memory chunks. The stack traces are stored in a hash table and referenced by handles which reside in the kasan_alloc_meta and kasan_free_meta structures in the allocated memory chunks. IRQ stack traces are cut below the IRQ entry point to avoid unnecessary duplication. Right now stackdepot support is only enabled in SLAB allocator. Once KASAN features in SLAB are on par with those in SLUB we can switch SLUB to stackdepot as well, thus removing the dependency on SLUB stack bookkeeping, which wastes a lot of memory. This patch is based on the "mm: kasan: stack depots" patch originally prepared by Dmitry Chernenkov. Joonsoo has said that he plans to reuse the stackdepot code for the mm/page_owner.c debugging facility. [akpm@linux-foundation.org: s/depot_stack_handle/depot_stack_handle_t] [aryabinin@virtuozzo.com: comment style fixes] Signed-off-by: Alexander Potapenko <glider@google.com> Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com> Cc: Christoph Lameter <cl@linux.com> Cc: Pekka Enberg <penberg@kernel.org> Cc: David Rientjes <rientjes@google.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: Andrey Konovalov <adech.fo@gmail.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Konstantin Serebryany <kcc@google.com> Cc: Dmitry Chernenkov <dmitryc@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/kasan')
-rw-r--r--mm/kasan/kasan.c55
-rw-r--r--mm/kasan/kasan.h11
-rw-r--r--mm/kasan/report.c12
3 files changed, 66 insertions, 12 deletions
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index cb998e0ec9d3..acb3b6c4dd89 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -17,7 +17,9 @@
17#define DISABLE_BRANCH_PROFILING 17#define DISABLE_BRANCH_PROFILING
18 18
19#include <linux/export.h> 19#include <linux/export.h>
20#include <linux/interrupt.h>
20#include <linux/init.h> 21#include <linux/init.h>
22#include <linux/kasan.h>
21#include <linux/kernel.h> 23#include <linux/kernel.h>
22#include <linux/kmemleak.h> 24#include <linux/kmemleak.h>
23#include <linux/linkage.h> 25#include <linux/linkage.h>
@@ -32,7 +34,6 @@
32#include <linux/string.h> 34#include <linux/string.h>
33#include <linux/types.h> 35#include <linux/types.h>
34#include <linux/vmalloc.h> 36#include <linux/vmalloc.h>
35#include <linux/kasan.h>
36 37
37#include "kasan.h" 38#include "kasan.h"
38#include "../slab.h" 39#include "../slab.h"
@@ -413,23 +414,65 @@ void kasan_poison_object_data(struct kmem_cache *cache, void *object)
413#endif 414#endif
414} 415}
415 416
416static inline void set_track(struct kasan_track *track) 417#ifdef CONFIG_SLAB
418static inline int in_irqentry_text(unsigned long ptr)
419{
420 return (ptr >= (unsigned long)&__irqentry_text_start &&
421 ptr < (unsigned long)&__irqentry_text_end) ||
422 (ptr >= (unsigned long)&__softirqentry_text_start &&
423 ptr < (unsigned long)&__softirqentry_text_end);
424}
425
426static inline void filter_irq_stacks(struct stack_trace *trace)
427{
428 int i;
429
430 if (!trace->nr_entries)
431 return;
432 for (i = 0; i < trace->nr_entries; i++)
433 if (in_irqentry_text(trace->entries[i])) {
434 /* Include the irqentry function into the stack. */
435 trace->nr_entries = i + 1;
436 break;
437 }
438}
439
440static inline depot_stack_handle_t save_stack(gfp_t flags)
441{
442 unsigned long entries[KASAN_STACK_DEPTH];
443 struct stack_trace trace = {
444 .nr_entries = 0,
445 .entries = entries,
446 .max_entries = KASAN_STACK_DEPTH,
447 .skip = 0
448 };
449
450 save_stack_trace(&trace);
451 filter_irq_stacks(&trace);
452 if (trace.nr_entries != 0 &&
453 trace.entries[trace.nr_entries-1] == ULONG_MAX)
454 trace.nr_entries--;
455
456 return depot_save_stack(&trace, flags);
457}
458
459static inline void set_track(struct kasan_track *track, gfp_t flags)
417{ 460{
418 track->cpu = raw_smp_processor_id();
419 track->pid = current->pid; 461 track->pid = current->pid;
420 track->when = jiffies; 462 track->stack = save_stack(flags);
421} 463}
422 464
423#ifdef CONFIG_SLAB
424struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache, 465struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache,
425 const void *object) 466 const void *object)
426{ 467{
468 BUILD_BUG_ON(sizeof(struct kasan_alloc_meta) > 32);
427 return (void *)object + cache->kasan_info.alloc_meta_offset; 469 return (void *)object + cache->kasan_info.alloc_meta_offset;
428} 470}
429 471
430struct kasan_free_meta *get_free_info(struct kmem_cache *cache, 472struct kasan_free_meta *get_free_info(struct kmem_cache *cache,
431 const void *object) 473 const void *object)
432{ 474{
475 BUILD_BUG_ON(sizeof(struct kasan_free_meta) > 32);
433 return (void *)object + cache->kasan_info.free_meta_offset; 476 return (void *)object + cache->kasan_info.free_meta_offset;
434} 477}
435#endif 478#endif
@@ -486,7 +529,7 @@ void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size,
486 529
487 alloc_info->state = KASAN_STATE_ALLOC; 530 alloc_info->state = KASAN_STATE_ALLOC;
488 alloc_info->alloc_size = size; 531 alloc_info->alloc_size = size;
489 set_track(&alloc_info->track); 532 set_track(&alloc_info->track, flags);
490 } 533 }
491#endif 534#endif
492} 535}
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 7b9e4ab9b66b..30a2f0ba0e09 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -2,6 +2,7 @@
2#define __MM_KASAN_KASAN_H 2#define __MM_KASAN_KASAN_H
3 3
4#include <linux/kasan.h> 4#include <linux/kasan.h>
5#include <linux/stackdepot.h>
5 6
6#define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT) 7#define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)
7#define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE_SIZE - 1) 8#define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE_SIZE - 1)
@@ -64,16 +65,18 @@ enum kasan_state {
64 KASAN_STATE_FREE 65 KASAN_STATE_FREE
65}; 66};
66 67
68#define KASAN_STACK_DEPTH 64
69
67struct kasan_track { 70struct kasan_track {
68 u64 cpu : 6; /* for NR_CPUS = 64 */ 71 u32 pid;
69 u64 pid : 16; /* 65536 processes */ 72 depot_stack_handle_t stack;
70 u64 when : 42; /* ~140 years */
71}; 73};
72 74
73struct kasan_alloc_meta { 75struct kasan_alloc_meta {
76 struct kasan_track track;
74 u32 state : 2; /* enum kasan_state */ 77 u32 state : 2; /* enum kasan_state */
75 u32 alloc_size : 30; 78 u32 alloc_size : 30;
76 struct kasan_track track; 79 u32 reserved;
77}; 80};
78 81
79struct kasan_free_meta { 82struct kasan_free_meta {
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 3e3385cc97ac..60869a5a0124 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -18,6 +18,7 @@
18#include <linux/printk.h> 18#include <linux/printk.h>
19#include <linux/sched.h> 19#include <linux/sched.h>
20#include <linux/slab.h> 20#include <linux/slab.h>
21#include <linux/stackdepot.h>
21#include <linux/stacktrace.h> 22#include <linux/stacktrace.h>
22#include <linux/string.h> 23#include <linux/string.h>
23#include <linux/types.h> 24#include <linux/types.h>
@@ -118,8 +119,15 @@ static inline bool init_task_stack_addr(const void *addr)
118#ifdef CONFIG_SLAB 119#ifdef CONFIG_SLAB
119static void print_track(struct kasan_track *track) 120static void print_track(struct kasan_track *track)
120{ 121{
121 pr_err("PID = %u, CPU = %u, timestamp = %lu\n", track->pid, 122 pr_err("PID = %u\n", track->pid);
122 track->cpu, (unsigned long)track->when); 123 if (track->stack) {
124 struct stack_trace trace;
125
126 depot_fetch_stack(track->stack, &trace);
127 print_stack_trace(&trace, 0);
128 } else {
129 pr_err("(stack is not available)\n");
130 }
123} 131}
124 132
125static void object_err(struct kmem_cache *cache, struct page *page, 133static void object_err(struct kmem_cache *cache, struct page *page,