Skip to content

Commit

Permalink
mm: page_alloc: add kasan hooks on alloc and free paths
Browse files Browse the repository at this point in the history
Add kernel address sanitizer hooks to mark allocated page's addresses as
accessible in corresponding shadow region.  Mark freed pages as
inaccessible.

Signed-off-by: Andrey Ryabinin <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Konstantin Serebryany <[email protected]>
Cc: Dmitry Chernenkov <[email protected]>
Signed-off-by: Andrey Konovalov <[email protected]>
Cc: Yuri Gribov <[email protected]>
Cc: Konstantin Khlebnikov <[email protected]>
Cc: Sasha Levin <[email protected]>
Cc: Christoph Lameter <[email protected]>
Cc: Joonsoo Kim <[email protected]>
Cc: Dave Hansen <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: "H. Peter Anvin" <[email protected]>
Cc: Christoph Lameter <[email protected]>
Cc: Pekka Enberg <[email protected]>
Cc: David Rientjes <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
aryabinin authored and torvalds committed Feb 14, 2015
1 parent ef7f0d6 commit b8c73fc
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 0 deletions.
6 changes: 6 additions & 0 deletions include/linux/kasan.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,19 @@ static inline void kasan_disable_current(void)

void kasan_unpoison_shadow(const void *address, size_t size);

void kasan_alloc_pages(struct page *page, unsigned int order);
void kasan_free_pages(struct page *page, unsigned int order);

#else /* CONFIG_KASAN */

static inline void kasan_unpoison_shadow(const void *address, size_t size) {}

static inline void kasan_enable_current(void) {}
static inline void kasan_disable_current(void) {}

static inline void kasan_alloc_pages(struct page *page, unsigned int order) {}
static inline void kasan_free_pages(struct page *page, unsigned int order) {}

#endif /* CONFIG_KASAN */

#endif /* LINUX_KASAN_H */
2 changes: 2 additions & 0 deletions mm/compaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <linux/sysfs.h>
#include <linux/balloon_compaction.h>
#include <linux/page-isolation.h>
#include <linux/kasan.h>
#include "internal.h"

#ifdef CONFIG_COMPACTION
Expand Down Expand Up @@ -72,6 +73,7 @@ static void map_pages(struct list_head *list)
list_for_each_entry(page, list, lru) {
arch_alloc_page(page, 0);
kernel_map_pages(page, 1, 1);
kasan_alloc_pages(page, 0);
}
}

Expand Down
14 changes: 14 additions & 0 deletions mm/kasan/kasan.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,20 @@ static __always_inline void check_memory_region(unsigned long addr,
kasan_report(addr, size, write, _RET_IP_);
}

void kasan_alloc_pages(struct page *page, unsigned int order)
{
if (likely(!PageHighMem(page)))
kasan_unpoison_shadow(page_address(page), PAGE_SIZE << order);
}

void kasan_free_pages(struct page *page, unsigned int order)
{
if (likely(!PageHighMem(page)))
kasan_poison_shadow(page_address(page),
PAGE_SIZE << order,
KASAN_FREE_PAGE);
}

#define DEFINE_ASAN_LOAD_STORE(size) \
void __asan_load##size(unsigned long addr) \
{ \
Expand Down
2 changes: 2 additions & 0 deletions mm/kasan/kasan.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)
#define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE_SIZE - 1)

#define KASAN_FREE_PAGE 0xFF /* page was freed */

struct kasan_access_info {
const void *access_addr;
const void *first_bad_addr;
Expand Down
11 changes: 11 additions & 0 deletions mm/kasan/report.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ static void print_error_description(struct kasan_access_info *info)
shadow_val = *(u8 *)kasan_mem_to_shadow(info->first_bad_addr);

switch (shadow_val) {
case KASAN_FREE_PAGE:
bug_type = "use after free";
break;
case 0 ... KASAN_SHADOW_SCALE_SIZE - 1:
bug_type = "out of bounds access";
break;
Expand All @@ -69,6 +72,14 @@ static void print_error_description(struct kasan_access_info *info)

static void print_address_description(struct kasan_access_info *info)
{
const void *addr = info->access_addr;

if ((addr >= (void *)PAGE_OFFSET) &&
(addr < high_memory)) {
struct page *page = virt_to_head_page(addr);
dump_page(page, "kasan: bad access detected");
}

dump_stack();
}

Expand Down
3 changes: 3 additions & 0 deletions mm/page_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/kmemcheck.h>
#include <linux/kasan.h>
#include <linux/module.h>
#include <linux/suspend.h>
#include <linux/pagevec.h>
Expand Down Expand Up @@ -787,6 +788,7 @@ static bool free_pages_prepare(struct page *page, unsigned int order)

trace_mm_page_free(page, order);
kmemcheck_free_shadow(page, order);
kasan_free_pages(page, order);

if (PageAnon(page))
page->mapping = NULL;
Expand Down Expand Up @@ -970,6 +972,7 @@ static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,

arch_alloc_page(page, order);
kernel_map_pages(page, 1 << order, 1);
kasan_alloc_pages(page, order);

if (gfp_flags & __GFP_ZERO)
prep_zero_page(page, order, gfp_flags);
Expand Down

0 comments on commit b8c73fc

Please sign in to comment.