mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-23 20:53:53 +08:00
2f004eea0f
Make #GP exceptions caused by out-of-bounds KASAN shadow accesses easier to understand by computing the address of the original access and printing that. More details are in the comments in the patch. This turns an error like this: kasan: CONFIG_KASAN_INLINE enabled kasan: GPF could be caused by NULL-ptr deref or user memory access general protection fault, probably for non-canonical address 0xe017577ddf75b7dd: 0000 [#1] PREEMPT SMP KASAN PTI into this: general protection fault, probably for non-canonical address 0xe017577ddf75b7dd: 0000 [#1] PREEMPT SMP KASAN PTI KASAN: maybe wild-memory-access in range [0x00badbeefbadbee8-0x00badbeefbadbeef] The hook is placed in architecture-independent code, but is currently only wired up to the X86 exception handler because I'm not sufficiently familiar with the address space layout and exception handling mechanisms on other architectures. Signed-off-by: Jann Horn <jannh@google.com> Signed-off-by: Borislav Petkov <bp@suse.de> Reviewed-by: Dmitry Vyukov <dvyukov@google.com> Cc: Alexander Potapenko <glider@google.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andrey Konovalov <andreyknvl@google.com> Cc: Andrey Ryabinin <aryabinin@virtuozzo.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: kasan-dev@googlegroups.com Cc: linux-mm <linux-mm@kvack.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Sean Christopherson <sean.j.christopherson@intel.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: x86-ml <x86@kernel.org> Link: https://lkml.kernel.org/r/20191218231150.12139-4-jannh@google.com
238 lines
7.1 KiB
C
238 lines
7.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_KASAN_H
|
|
#define _LINUX_KASAN_H
|
|
|
|
#include <linux/types.h>
|
|
|
|
struct kmem_cache;
|
|
struct page;
|
|
struct vm_struct;
|
|
struct task_struct;
|
|
|
|
#ifdef CONFIG_KASAN
|
|
|
|
#include <asm/kasan.h>
|
|
#include <asm/pgtable.h>
|
|
|
|
extern unsigned char kasan_early_shadow_page[PAGE_SIZE];
|
|
extern pte_t kasan_early_shadow_pte[PTRS_PER_PTE];
|
|
extern pmd_t kasan_early_shadow_pmd[PTRS_PER_PMD];
|
|
extern pud_t kasan_early_shadow_pud[PTRS_PER_PUD];
|
|
extern p4d_t kasan_early_shadow_p4d[MAX_PTRS_PER_P4D];
|
|
|
|
int kasan_populate_early_shadow(const void *shadow_start,
|
|
const void *shadow_end);
|
|
|
|
static inline void *kasan_mem_to_shadow(const void *addr)
|
|
{
|
|
return (void *)((unsigned long)addr >> KASAN_SHADOW_SCALE_SHIFT)
|
|
+ KASAN_SHADOW_OFFSET;
|
|
}
|
|
|
|
/* Enable reporting bugs after kasan_disable_current() */
|
|
extern void kasan_enable_current(void);
|
|
|
|
/* Disable reporting bugs for current task */
|
|
extern void kasan_disable_current(void);
|
|
|
|
void kasan_unpoison_shadow(const void *address, size_t size);
|
|
|
|
void kasan_unpoison_task_stack(struct task_struct *task);
|
|
void kasan_unpoison_stack_above_sp_to(const void *watermark);
|
|
|
|
void kasan_alloc_pages(struct page *page, unsigned int order);
|
|
void kasan_free_pages(struct page *page, unsigned int order);
|
|
|
|
void kasan_cache_create(struct kmem_cache *cache, unsigned int *size,
|
|
slab_flags_t *flags);
|
|
|
|
void kasan_poison_slab(struct page *page);
|
|
void kasan_unpoison_object_data(struct kmem_cache *cache, void *object);
|
|
void kasan_poison_object_data(struct kmem_cache *cache, void *object);
|
|
void * __must_check kasan_init_slab_obj(struct kmem_cache *cache,
|
|
const void *object);
|
|
|
|
void * __must_check kasan_kmalloc_large(const void *ptr, size_t size,
|
|
gfp_t flags);
|
|
void kasan_kfree_large(void *ptr, unsigned long ip);
|
|
void kasan_poison_kfree(void *ptr, unsigned long ip);
|
|
void * __must_check kasan_kmalloc(struct kmem_cache *s, const void *object,
|
|
size_t size, gfp_t flags);
|
|
void * __must_check kasan_krealloc(const void *object, size_t new_size,
|
|
gfp_t flags);
|
|
|
|
void * __must_check kasan_slab_alloc(struct kmem_cache *s, void *object,
|
|
gfp_t flags);
|
|
bool kasan_slab_free(struct kmem_cache *s, void *object, unsigned long ip);
|
|
|
|
struct kasan_cache {
|
|
int alloc_meta_offset;
|
|
int free_meta_offset;
|
|
};
|
|
|
|
/*
|
|
* These functions provide a special case to support backing module
|
|
* allocations with real shadow memory. With KASAN vmalloc, the special
|
|
* case is unnecessary, as the work is handled in the generic case.
|
|
*/
|
|
#ifndef CONFIG_KASAN_VMALLOC
|
|
int kasan_module_alloc(void *addr, size_t size);
|
|
void kasan_free_shadow(const struct vm_struct *vm);
|
|
#else
|
|
static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
|
|
static inline void kasan_free_shadow(const struct vm_struct *vm) {}
|
|
#endif
|
|
|
|
int kasan_add_zero_shadow(void *start, unsigned long size);
|
|
void kasan_remove_zero_shadow(void *start, unsigned long size);
|
|
|
|
size_t __ksize(const void *);
|
|
static inline void kasan_unpoison_slab(const void *ptr)
|
|
{
|
|
kasan_unpoison_shadow(ptr, __ksize(ptr));
|
|
}
|
|
size_t kasan_metadata_size(struct kmem_cache *cache);
|
|
|
|
bool kasan_save_enable_multi_shot(void);
|
|
void kasan_restore_multi_shot(bool enabled);
|
|
|
|
#else /* CONFIG_KASAN */
|
|
|
|
static inline void kasan_unpoison_shadow(const void *address, size_t size) {}
|
|
|
|
static inline void kasan_unpoison_task_stack(struct task_struct *task) {}
|
|
static inline void kasan_unpoison_stack_above_sp_to(const void *watermark) {}
|
|
|
|
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) {}
|
|
|
|
static inline void kasan_cache_create(struct kmem_cache *cache,
|
|
unsigned int *size,
|
|
slab_flags_t *flags) {}
|
|
|
|
static inline void kasan_poison_slab(struct page *page) {}
|
|
static inline void kasan_unpoison_object_data(struct kmem_cache *cache,
|
|
void *object) {}
|
|
static inline void kasan_poison_object_data(struct kmem_cache *cache,
|
|
void *object) {}
|
|
static inline void *kasan_init_slab_obj(struct kmem_cache *cache,
|
|
const void *object)
|
|
{
|
|
return (void *)object;
|
|
}
|
|
|
|
static inline void *kasan_kmalloc_large(void *ptr, size_t size, gfp_t flags)
|
|
{
|
|
return ptr;
|
|
}
|
|
static inline void kasan_kfree_large(void *ptr, unsigned long ip) {}
|
|
static inline void kasan_poison_kfree(void *ptr, unsigned long ip) {}
|
|
static inline void *kasan_kmalloc(struct kmem_cache *s, const void *object,
|
|
size_t size, gfp_t flags)
|
|
{
|
|
return (void *)object;
|
|
}
|
|
static inline void *kasan_krealloc(const void *object, size_t new_size,
|
|
gfp_t flags)
|
|
{
|
|
return (void *)object;
|
|
}
|
|
|
|
static inline void *kasan_slab_alloc(struct kmem_cache *s, void *object,
|
|
gfp_t flags)
|
|
{
|
|
return object;
|
|
}
|
|
static inline bool kasan_slab_free(struct kmem_cache *s, void *object,
|
|
unsigned long ip)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
|
|
static inline void kasan_free_shadow(const struct vm_struct *vm) {}
|
|
|
|
static inline int kasan_add_zero_shadow(void *start, unsigned long size)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline void kasan_remove_zero_shadow(void *start,
|
|
unsigned long size)
|
|
{}
|
|
|
|
static inline void kasan_unpoison_slab(const void *ptr) { }
|
|
static inline size_t kasan_metadata_size(struct kmem_cache *cache) { return 0; }
|
|
|
|
#endif /* CONFIG_KASAN */
|
|
|
|
#ifdef CONFIG_KASAN_GENERIC
|
|
|
|
#define KASAN_SHADOW_INIT 0
|
|
|
|
void kasan_cache_shrink(struct kmem_cache *cache);
|
|
void kasan_cache_shutdown(struct kmem_cache *cache);
|
|
|
|
#else /* CONFIG_KASAN_GENERIC */
|
|
|
|
static inline void kasan_cache_shrink(struct kmem_cache *cache) {}
|
|
static inline void kasan_cache_shutdown(struct kmem_cache *cache) {}
|
|
|
|
#endif /* CONFIG_KASAN_GENERIC */
|
|
|
|
#ifdef CONFIG_KASAN_SW_TAGS
|
|
|
|
#define KASAN_SHADOW_INIT 0xFF
|
|
|
|
void kasan_init_tags(void);
|
|
|
|
void *kasan_reset_tag(const void *addr);
|
|
|
|
void kasan_report(unsigned long addr, size_t size,
|
|
bool is_write, unsigned long ip);
|
|
|
|
#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 */
|
|
|
|
#ifdef CONFIG_KASAN_VMALLOC
|
|
int kasan_populate_vmalloc(unsigned long addr, unsigned long size);
|
|
void kasan_poison_vmalloc(const void *start, unsigned long size);
|
|
void kasan_unpoison_vmalloc(const void *start, unsigned long size);
|
|
void kasan_release_vmalloc(unsigned long start, unsigned long end,
|
|
unsigned long free_region_start,
|
|
unsigned long free_region_end);
|
|
#else
|
|
static inline int kasan_populate_vmalloc(unsigned long start,
|
|
unsigned long size)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline void kasan_poison_vmalloc(const void *start, unsigned long size)
|
|
{ }
|
|
static inline void kasan_unpoison_vmalloc(const void *start, unsigned long size)
|
|
{ }
|
|
static inline void kasan_release_vmalloc(unsigned long start,
|
|
unsigned long end,
|
|
unsigned long free_region_start,
|
|
unsigned long free_region_end) {}
|
|
#endif
|
|
|
|
#ifdef CONFIG_KASAN_INLINE
|
|
void kasan_non_canonical_hook(unsigned long addr);
|
|
#else /* CONFIG_KASAN_INLINE */
|
|
static inline void kasan_non_canonical_hook(unsigned long addr) { }
|
|
#endif /* CONFIG_KASAN_INLINE */
|
|
|
|
#endif /* LINUX_KASAN_H */
|