Commit 3c9e3aa1 authored by Andrey Konovalov's avatar Andrey Konovalov Committed by Linus Torvalds

kasan: add tag related helper functions

This commit adds a few helper functions, that are meant to be used to work
with tags embedded in the top byte of kernel pointers: to set, to get or
to reset the top byte.

Link: http://lkml.kernel.org/r/f6c6437bb8e143bc44f42c3c259c62e734be7935.1544099024.git.andreyknvl@google.comSigned-off-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 9c23f847
...@@ -4,12 +4,16 @@ ...@@ -4,12 +4,16 @@
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#ifdef CONFIG_KASAN
#include <linux/linkage.h> #include <linux/linkage.h>
#include <asm/memory.h> #include <asm/memory.h>
#include <asm/pgtable-types.h> #include <asm/pgtable-types.h>
#define arch_kasan_set_tag(addr, tag) __tag_set(addr, tag)
#define arch_kasan_reset_tag(addr) __tag_reset(addr)
#define arch_kasan_get_tag(addr) __tag_get(addr)
#ifdef CONFIG_KASAN
/* /*
* KASAN_SHADOW_START: beginning of the kernel virtual addresses. * KASAN_SHADOW_START: beginning of the kernel virtual addresses.
* KASAN_SHADOW_END: KASAN_SHADOW_START + 1/N of kernel virtual addresses, * KASAN_SHADOW_END: KASAN_SHADOW_START + 1/N of kernel virtual addresses,
......
...@@ -226,6 +226,18 @@ extern u64 vabits_user; ...@@ -226,6 +226,18 @@ extern u64 vabits_user;
#define untagged_addr(addr) \ #define untagged_addr(addr) \
((__typeof__(addr))sign_extend64((u64)(addr), 55)) ((__typeof__(addr))sign_extend64((u64)(addr), 55))
#ifdef CONFIG_KASAN_SW_TAGS
#define __tag_shifted(tag) ((u64)(tag) << 56)
#define __tag_set(addr, tag) (__typeof__(addr))( \
((u64)(addr) & ~__tag_shifted(0xff)) | __tag_shifted(tag))
#define __tag_reset(addr) untagged_addr(addr)
#define __tag_get(addr) (__u8)((u64)(addr) >> 56)
#else
#define __tag_set(addr, tag) (addr)
#define __tag_reset(addr) (addr)
#define __tag_get(addr) 0
#endif
/* /*
* Physical vs virtual RAM address space conversion. These are * Physical vs virtual RAM address space conversion. These are
* private definitions which should NOT be used outside memory.h * private definitions which should NOT be used outside memory.h
......
...@@ -252,6 +252,8 @@ void __init kasan_init(void) ...@@ -252,6 +252,8 @@ void __init kasan_init(void)
memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE); memset(kasan_early_shadow_page, KASAN_SHADOW_INIT, PAGE_SIZE);
cpu_replace_ttbr1(lm_alias(swapper_pg_dir)); cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
kasan_init_tags();
/* At this point kasan is fully initialized. Enable error messages */ /* At this point kasan is fully initialized. Enable error messages */
init_task.kasan_depth = 0; init_task.kasan_depth = 0;
pr_info("KernelAddressSanitizer initialized\n"); pr_info("KernelAddressSanitizer initialized\n");
......
...@@ -169,6 +169,19 @@ static inline void kasan_cache_shutdown(struct kmem_cache *cache) {} ...@@ -169,6 +169,19 @@ static inline void kasan_cache_shutdown(struct kmem_cache *cache) {}
#define KASAN_SHADOW_INIT 0xFF #define KASAN_SHADOW_INIT 0xFF
void kasan_init_tags(void);
void *kasan_reset_tag(const void *addr);
#else /* CONFIG_KASAN_SW_TAGS */
static inline void kasan_init_tags(void) { }
static inline void *kasan_reset_tag(const void *addr)
{
return (void *)addr;
}
#endif /* CONFIG_KASAN_SW_TAGS */ #endif /* CONFIG_KASAN_SW_TAGS */
#endif /* LINUX_KASAN_H */ #endif /* LINUX_KASAN_H */
...@@ -8,6 +8,10 @@ ...@@ -8,6 +8,10 @@
#define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT) #define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)
#define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE_SIZE - 1) #define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE_SIZE - 1)
#define KASAN_TAG_KERNEL 0xFF /* native kernel pointers tag */
#define KASAN_TAG_INVALID 0xFE /* inaccessible memory tag */
#define KASAN_TAG_MAX 0xFD /* maximum value for random tags */
#define KASAN_FREE_PAGE 0xFF /* page was freed */ #define KASAN_FREE_PAGE 0xFF /* page was freed */
#define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocations */ #define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocations */
#define KASAN_KMALLOC_REDZONE 0xFC /* redzone inside slub object */ #define KASAN_KMALLOC_REDZONE 0xFC /* redzone inside slub object */
...@@ -126,6 +130,33 @@ static inline void quarantine_reduce(void) { } ...@@ -126,6 +130,33 @@ static inline void quarantine_reduce(void) { }
static inline void quarantine_remove_cache(struct kmem_cache *cache) { } static inline void quarantine_remove_cache(struct kmem_cache *cache) { }
#endif #endif
#ifdef CONFIG_KASAN_SW_TAGS
u8 random_tag(void);
#else
static inline u8 random_tag(void)
{
return 0;
}
#endif
#ifndef arch_kasan_set_tag
#define arch_kasan_set_tag(addr, tag) ((void *)(addr))
#endif
#ifndef arch_kasan_reset_tag
#define arch_kasan_reset_tag(addr) ((void *)(addr))
#endif
#ifndef arch_kasan_get_tag
#define arch_kasan_get_tag(addr) 0
#endif
#define set_tag(addr, tag) ((void *)arch_kasan_set_tag((addr), (tag)))
#define reset_tag(addr) ((void *)arch_kasan_reset_tag(addr))
#define get_tag(addr) arch_kasan_get_tag(addr)
/* /*
* Exported functions for interfaces called from assembly or from generated * Exported functions for interfaces called from assembly or from generated
* code. Declarations here to avoid warning about missing declarations. * code. Declarations here to avoid warning about missing declarations.
......
...@@ -38,6 +38,43 @@ ...@@ -38,6 +38,43 @@
#include "kasan.h" #include "kasan.h"
#include "../slab.h" #include "../slab.h"
static DEFINE_PER_CPU(u32, prng_state);
void kasan_init_tags(void)
{
int cpu;
for_each_possible_cpu(cpu)
per_cpu(prng_state, cpu) = get_random_u32();
}
/*
* If a preemption happens between this_cpu_read and this_cpu_write, the only
* side effect is that we'll give a few allocated in different contexts objects
* the same tag. Since tag-based KASAN is meant to be used a probabilistic
* bug-detection debug feature, this doesn't have significant negative impact.
*
* Ideally the tags use strong randomness to prevent any attempts to predict
* them during explicit exploit attempts. But strong randomness is expensive,
* and we did an intentional trade-off to use a PRNG. This non-atomic RMW
* sequence has in fact positive effect, since interrupts that randomly skew
* PRNG at unpredictable points do only good.
*/
u8 random_tag(void)
{
u32 state = this_cpu_read(prng_state);
state = 1664525 * state + 1013904223;
this_cpu_write(prng_state, state);
return (u8)(state % (KASAN_TAG_MAX + 1));
}
void *kasan_reset_tag(const void *addr)
{
return reset_tag(addr);
}
void check_memory_region(unsigned long addr, size_t size, bool write, void check_memory_region(unsigned long addr, size_t size, bool write,
unsigned long ret_ip) unsigned long ret_ip)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment