summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Wu <walter-zh.wu@mediatek.com>2019-09-23 18:34:13 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-09-24 18:54:07 -0400
commitae8f06b31a83e54777514308a63f669a1fed519e (patch)
tree2963246f28ec918cfa7b5d75742eda07cf8deb62
parentc59180ae3e5b43d3534748b19a57905712ea5fff (diff)
kasan: add memory corruption identification for software tag-based mode
Add memory corruption identification at bug report for software tag-based mode. The report shows whether it is "use-after-free" or "out-of-bound" error instead of "invalid-access" error. This will make it easier for programmers to see the memory corruption problem. We extend the slab to store five old free pointer tag and free backtrace, we can check if the tagged address is in the slab record and make a good guess if the object is more like "use-after-free" or "out-of-bound". therefore every slab memory corruption can be identified whether it's "use-after-free" or "out-of-bound". [aryabinin@virtuozzo.com: simplify & clenup code] Link: https://lkml.kernel.org/r/3318f9d7-a760-3cc8-b700-f06108ae745f@virtuozzo.com] Link: http://lkml.kernel.org/r/20190821180332.11450-1-aryabinin@virtuozzo.com Signed-off-by: Walter Wu <walter-zh.wu@mediatek.com> Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com> Acked-by: Andrey Konovalov <andreyknvl@google.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Alexander Potapenko <glider@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--lib/Kconfig.kasan8
-rw-r--r--mm/kasan/common.c22
-rw-r--r--mm/kasan/kasan.h14
-rw-r--r--mm/kasan/report.c44
-rw-r--r--mm/kasan/tags_report.c24
5 files changed, 99 insertions, 13 deletions
diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
index 7fa97a8b5717..6c9682ce0254 100644
--- a/lib/Kconfig.kasan
+++ b/lib/Kconfig.kasan
@@ -134,6 +134,14 @@ config KASAN_S390_4_LEVEL_PAGING
134 to 3TB of RAM with KASan enabled). This options allows to force 134 to 3TB of RAM with KASan enabled). This options allows to force
135 4-level paging instead. 135 4-level paging instead.
136 136
137config KASAN_SW_TAGS_IDENTIFY
138 bool "Enable memory corruption identification"
139 depends on KASAN_SW_TAGS
140 help
141 This option enables best-effort identification of bug type
142 (use-after-free or out-of-bounds) at the cost of increased
143 memory consumption.
144
137config TEST_KASAN 145config TEST_KASAN
138 tristate "Module for testing KASAN for bug detection" 146 tristate "Module for testing KASAN for bug detection"
139 depends on m && KASAN 147 depends on m && KASAN
diff --git a/mm/kasan/common.c b/mm/kasan/common.c
index 95d16a42db6b..6b6f1198c72b 100644
--- a/mm/kasan/common.c
+++ b/mm/kasan/common.c
@@ -304,7 +304,6 @@ size_t kasan_metadata_size(struct kmem_cache *cache)
304struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache, 304struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache,
305 const void *object) 305 const void *object)
306{ 306{
307 BUILD_BUG_ON(sizeof(struct kasan_alloc_meta) > 32);
308 return (void *)object + cache->kasan_info.alloc_meta_offset; 307 return (void *)object + cache->kasan_info.alloc_meta_offset;
309} 308}
310 309
@@ -315,6 +314,24 @@ struct kasan_free_meta *get_free_info(struct kmem_cache *cache,
315 return (void *)object + cache->kasan_info.free_meta_offset; 314 return (void *)object + cache->kasan_info.free_meta_offset;
316} 315}
317 316
317
318static void kasan_set_free_info(struct kmem_cache *cache,
319 void *object, u8 tag)
320{
321 struct kasan_alloc_meta *alloc_meta;
322 u8 idx = 0;
323
324 alloc_meta = get_alloc_info(cache, object);
325
326#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
327 idx = alloc_meta->free_track_idx;
328 alloc_meta->free_pointer_tag[idx] = tag;
329 alloc_meta->free_track_idx = (idx + 1) % KASAN_NR_FREE_STACKS;
330#endif
331
332 set_track(&alloc_meta->free_track[idx], GFP_NOWAIT);
333}
334
318void kasan_poison_slab(struct page *page) 335void kasan_poison_slab(struct page *page)
319{ 336{
320 unsigned long i; 337 unsigned long i;
@@ -452,7 +469,8 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
452 unlikely(!(cache->flags & SLAB_KASAN))) 469 unlikely(!(cache->flags & SLAB_KASAN)))
453 return false; 470 return false;
454 471
455 set_track(&get_alloc_info(cache, object)->free_track, GFP_NOWAIT); 472 kasan_set_free_info(cache, object, tag);
473
456 quarantine_put(get_free_info(cache, object), cache); 474 quarantine_put(get_free_info(cache, object), cache);
457 475
458 return IS_ENABLED(CONFIG_KASAN_GENERIC); 476 return IS_ENABLED(CONFIG_KASAN_GENERIC);
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 014f19e76247..35cff6bbb716 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -95,9 +95,19 @@ struct kasan_track {
95 depot_stack_handle_t stack; 95 depot_stack_handle_t stack;
96}; 96};
97 97
98#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
99#define KASAN_NR_FREE_STACKS 5
100#else
101#define KASAN_NR_FREE_STACKS 1
102#endif
103
98struct kasan_alloc_meta { 104struct kasan_alloc_meta {
99 struct kasan_track alloc_track; 105 struct kasan_track alloc_track;
100 struct kasan_track free_track; 106 struct kasan_track free_track[KASAN_NR_FREE_STACKS];
107#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
108 u8 free_pointer_tag[KASAN_NR_FREE_STACKS];
109 u8 free_track_idx;
110#endif
101}; 111};
102 112
103struct qlist_node { 113struct qlist_node {
@@ -146,6 +156,8 @@ void kasan_report(unsigned long addr, size_t size,
146 bool is_write, unsigned long ip); 156 bool is_write, unsigned long ip);
147void kasan_report_invalid_free(void *object, unsigned long ip); 157void kasan_report_invalid_free(void *object, unsigned long ip);
148 158
159struct page *kasan_addr_to_page(const void *addr);
160
149#if defined(CONFIG_KASAN_GENERIC) && \ 161#if defined(CONFIG_KASAN_GENERIC) && \
150 (defined(CONFIG_SLAB) || defined(CONFIG_SLUB)) 162 (defined(CONFIG_SLAB) || defined(CONFIG_SLUB))
151void quarantine_put(struct kasan_free_meta *info, struct kmem_cache *cache); 163void quarantine_put(struct kasan_free_meta *info, struct kmem_cache *cache);
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 0e5f965f1882..621782100eaa 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -111,7 +111,7 @@ static void print_track(struct kasan_track *track, const char *prefix)
111 } 111 }
112} 112}
113 113
114static struct page *addr_to_page(const void *addr) 114struct page *kasan_addr_to_page(const void *addr)
115{ 115{
116 if ((addr >= (void *)PAGE_OFFSET) && 116 if ((addr >= (void *)PAGE_OFFSET) &&
117 (addr < high_memory)) 117 (addr < high_memory))
@@ -151,15 +151,38 @@ static void describe_object_addr(struct kmem_cache *cache, void *object,
151 (void *)(object_addr + cache->object_size)); 151 (void *)(object_addr + cache->object_size));
152} 152}
153 153
154static struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
155 void *object, u8 tag)
156{
157 struct kasan_alloc_meta *alloc_meta;
158 int i = 0;
159
160 alloc_meta = get_alloc_info(cache, object);
161
162#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
163 for (i = 0; i < KASAN_NR_FREE_STACKS; i++) {
164 if (alloc_meta->free_pointer_tag[i] == tag)
165 break;
166 }
167 if (i == KASAN_NR_FREE_STACKS)
168 i = alloc_meta->free_track_idx;
169#endif
170
171 return &alloc_meta->free_track[i];
172}
173
154static void describe_object(struct kmem_cache *cache, void *object, 174static void describe_object(struct kmem_cache *cache, void *object,
155 const void *addr) 175 const void *addr, u8 tag)
156{ 176{
157 struct kasan_alloc_meta *alloc_info = get_alloc_info(cache, object); 177 struct kasan_alloc_meta *alloc_info = get_alloc_info(cache, object);
158 178
159 if (cache->flags & SLAB_KASAN) { 179 if (cache->flags & SLAB_KASAN) {
180 struct kasan_track *free_track;
181
160 print_track(&alloc_info->alloc_track, "Allocated"); 182 print_track(&alloc_info->alloc_track, "Allocated");
161 pr_err("\n"); 183 pr_err("\n");
162 print_track(&alloc_info->free_track, "Freed"); 184 free_track = kasan_get_free_track(cache, object, tag);
185 print_track(free_track, "Freed");
163 pr_err("\n"); 186 pr_err("\n");
164 } 187 }
165 188
@@ -344,9 +367,9 @@ static void print_address_stack_frame(const void *addr)
344 print_decoded_frame_descr(frame_descr); 367 print_decoded_frame_descr(frame_descr);
345} 368}
346 369
347static void print_address_description(void *addr) 370static void print_address_description(void *addr, u8 tag)
348{ 371{
349 struct page *page = addr_to_page(addr); 372 struct page *page = kasan_addr_to_page(addr);
350 373
351 dump_stack(); 374 dump_stack();
352 pr_err("\n"); 375 pr_err("\n");
@@ -355,7 +378,7 @@ static void print_address_description(void *addr)
355 struct kmem_cache *cache = page->slab_cache; 378 struct kmem_cache *cache = page->slab_cache;
356 void *object = nearest_obj(cache, page, addr); 379 void *object = nearest_obj(cache, page, addr);
357 380
358 describe_object(cache, object, addr); 381 describe_object(cache, object, addr, tag);
359 } 382 }
360 383
361 if (kernel_or_module_addr(addr) && !init_task_stack_addr(addr)) { 384 if (kernel_or_module_addr(addr) && !init_task_stack_addr(addr)) {
@@ -435,13 +458,14 @@ static bool report_enabled(void)
435void kasan_report_invalid_free(void *object, unsigned long ip) 458void kasan_report_invalid_free(void *object, unsigned long ip)
436{ 459{
437 unsigned long flags; 460 unsigned long flags;
461 u8 tag = get_tag(object);
438 462
463 object = reset_tag(object);
439 start_report(&flags); 464 start_report(&flags);
440 pr_err("BUG: KASAN: double-free or invalid-free in %pS\n", (void *)ip); 465 pr_err("BUG: KASAN: double-free or invalid-free in %pS\n", (void *)ip);
441 print_tags(get_tag(object), reset_tag(object)); 466 print_tags(tag, object);
442 object = reset_tag(object);
443 pr_err("\n"); 467 pr_err("\n");
444 print_address_description(object); 468 print_address_description(object, tag);
445 pr_err("\n"); 469 pr_err("\n");
446 print_shadow_for_address(object); 470 print_shadow_for_address(object);
447 end_report(&flags); 471 end_report(&flags);
@@ -479,7 +503,7 @@ void __kasan_report(unsigned long addr, size_t size, bool is_write, unsigned lon
479 pr_err("\n"); 503 pr_err("\n");
480 504
481 if (addr_has_shadow(untagged_addr)) { 505 if (addr_has_shadow(untagged_addr)) {
482 print_address_description(untagged_addr); 506 print_address_description(untagged_addr, get_tag(tagged_addr));
483 pr_err("\n"); 507 pr_err("\n");
484 print_shadow_for_address(info.first_bad_addr); 508 print_shadow_for_address(info.first_bad_addr);
485 } else { 509 } else {
diff --git a/mm/kasan/tags_report.c b/mm/kasan/tags_report.c
index 8eaf5f722271..969ae08f59d7 100644
--- a/mm/kasan/tags_report.c
+++ b/mm/kasan/tags_report.c
@@ -36,6 +36,30 @@
36 36
37const char *get_bug_type(struct kasan_access_info *info) 37const char *get_bug_type(struct kasan_access_info *info)
38{ 38{
39#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
40 struct kasan_alloc_meta *alloc_meta;
41 struct kmem_cache *cache;
42 struct page *page;
43 const void *addr;
44 void *object;
45 u8 tag;
46 int i;
47
48 tag = get_tag(info->access_addr);
49 addr = reset_tag(info->access_addr);
50 page = kasan_addr_to_page(addr);
51 if (page && PageSlab(page)) {
52 cache = page->slab_cache;
53 object = nearest_obj(cache, page, (void *)addr);
54 alloc_meta = get_alloc_info(cache, object);
55
56 for (i = 0; i < KASAN_NR_FREE_STACKS; i++)
57 if (alloc_meta->free_pointer_tag[i] == tag)
58 return "use-after-free";
59 return "out-of-bounds";
60 }
61
62#endif
39 return "invalid-access"; 63 return "invalid-access";
40} 64}
41 65