Skip to content

Commit

Permalink
arm64: add KASAN support
Browse files Browse the repository at this point in the history
This patch adds arch specific code for kernel address sanitizer
(see Documentation/kasan.txt).

1/8 of kernel addresses reserved for shadow memory. There was no
big enough hole for this, so virtual addresses for shadow were
stolen from vmalloc area.

At early boot stage the whole shadow region populated with just
one physical page (kasan_zero_page). Later, this page reused
as readonly zero shadow for some memory that KASan currently
don't track (vmalloc).
After mapping the physical memory, pages for shadow memory are
allocated and mapped.

Functions like memset/memmove/memcpy do a lot of memory accesses.
If bad pointer passed to one of these function it is important
to catch this. Compiler's instrumentation cannot do this since
these functions are written in assembly.
KASan replaces memory functions with manually instrumented variants.
Original functions declared as weak symbols so strong definitions
in mm/kasan/kasan.c could replace them. Original functions have aliases
with '__' prefix in name, so we could call non-instrumented variant
if needed.
Some files built without kasan instrumentation (e.g. mm/slub.c).
Original mem* function replaced (via #define) with prefixed variants
to disable memory access checks for such files.

Signed-off-by: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Tested-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
  • Loading branch information
aryabinin authored and ctmarinas committed Oct 12, 2015
1 parent fd2203d commit 39d114d
Show file tree
Hide file tree
Showing 18 changed files with 288 additions and 6 deletions.
1 change: 1 addition & 0 deletions arch/arm64/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ config ARM64
select HAVE_ARCH_AUDITSYSCALL
select HAVE_ARCH_BITREVERSE
select HAVE_ARCH_JUMP_LABEL
select HAVE_ARCH_KASAN if SPARSEMEM_VMEMMAP
select HAVE_ARCH_KGDB
select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_TRACEHOOK
Expand Down
7 changes: 7 additions & 0 deletions arch/arm64/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ else
TEXT_OFFSET := 0x00080000
endif

# KASAN_SHADOW_OFFSET = VA_START + (1 << (VA_BITS - 3)) - (1 << 61)
# in 32-bit arithmetic
KASAN_SHADOW_OFFSET := $(shell printf "0x%08x00000000\n" $$(( \
(0xffffffff & (-1 << ($(CONFIG_ARM64_VA_BITS) - 32))) \
+ (1 << ($(CONFIG_ARM64_VA_BITS) - 32 - 3)) \
- (1 << (64 - 32 - 3)) )) )

export TEXT_OFFSET GZFLAGS

core-y += arch/arm64/kernel/ arch/arm64/mm/
Expand Down
36 changes: 36 additions & 0 deletions arch/arm64/include/asm/kasan.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef __ASM_KASAN_H
#define __ASM_KASAN_H

#ifndef __ASSEMBLY__

#ifdef CONFIG_KASAN

#include <asm/memory.h>

/*
* KASAN_SHADOW_START: beginning of the kernel virtual addresses.
* KASAN_SHADOW_END: KASAN_SHADOW_START + 1/8 of kernel virtual addresses.
*/
#define KASAN_SHADOW_START (VA_START)
#define KASAN_SHADOW_END (KASAN_SHADOW_START + (1UL << (VA_BITS - 3)))

/*
* This value is used to map an address to the corresponding shadow
* address by the following formula:
* shadow_addr = (address >> 3) + KASAN_SHADOW_OFFSET;
*
* (1 << 61) shadow addresses - [KASAN_SHADOW_OFFSET,KASAN_SHADOW_END]
* cover all 64-bits of virtual addresses. So KASAN_SHADOW_OFFSET
* should satisfy the following equation:
* KASAN_SHADOW_OFFSET = KASAN_SHADOW_END - (1ULL << 61)
*/
#define KASAN_SHADOW_OFFSET (KASAN_SHADOW_END - (1ULL << (64 - 3)))

void kasan_init(void);

#else
static inline void kasan_init(void) { }
#endif

#endif
#endif
7 changes: 7 additions & 0 deletions arch/arm64/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,14 @@
* fixed mappings and modules
*/
#define VMEMMAP_SIZE ALIGN((1UL << (VA_BITS - PAGE_SHIFT)) * sizeof(struct page), PUD_SIZE)

#ifndef CONFIG_KASAN
#define VMALLOC_START (VA_START)
#else
#include <asm/kasan.h>
#define VMALLOC_START (KASAN_SHADOW_END + SZ_64K)
#endif

#define VMALLOC_END (PAGE_OFFSET - PUD_SIZE - VMEMMAP_SIZE - SZ_64K)

#define vmemmap ((struct page *)(VMALLOC_END + SZ_64K))
Expand Down
16 changes: 16 additions & 0 deletions arch/arm64/include/asm/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,33 @@ extern __kernel_size_t strnlen(const char *, __kernel_size_t);

#define __HAVE_ARCH_MEMCPY
extern void *memcpy(void *, const void *, __kernel_size_t);
extern void *__memcpy(void *, const void *, __kernel_size_t);

#define __HAVE_ARCH_MEMMOVE
extern void *memmove(void *, const void *, __kernel_size_t);
extern void *__memmove(void *, const void *, __kernel_size_t);

#define __HAVE_ARCH_MEMCHR
extern void *memchr(const void *, int, __kernel_size_t);

#define __HAVE_ARCH_MEMSET
extern void *memset(void *, int, __kernel_size_t);
extern void *__memset(void *, int, __kernel_size_t);

#define __HAVE_ARCH_MEMCMP
extern int memcmp(const void *, const void *, size_t);


#if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)

/*
* For files that are not instrumented (e.g. mm/slub.c) we
* should use not instrumented version of mem* functions.
*/

#define memcpy(dst, src, len) __memcpy(dst, src, len)
#define memmove(dst, src, len) __memmove(dst, src, len)
#define memset(s, c, n) __memset(s, c, n)
#endif

#endif
2 changes: 2 additions & 0 deletions arch/arm64/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_efi-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_armv8_deprecated.o := -I$(src)

KASAN_SANITIZE_efi-stub.o := n

CFLAGS_REMOVE_ftrace.o = -pg
CFLAGS_REMOVE_insn.o = -pg
CFLAGS_REMOVE_return_address.o = -pg
Expand Down
3 changes: 3 additions & 0 deletions arch/arm64/kernel/arm64ksyms.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ EXPORT_SYMBOL(strnlen);
EXPORT_SYMBOL(memset);
EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(__memset);
EXPORT_SYMBOL(__memcpy);
EXPORT_SYMBOL(__memmove);
EXPORT_SYMBOL(memchr);
EXPORT_SYMBOL(memcmp);

Expand Down
3 changes: 3 additions & 0 deletions arch/arm64/kernel/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,9 @@ __mmap_switched:
str_l x21, __fdt_pointer, x5 // Save FDT pointer
str_l x24, memstart_addr, x6 // Save PHYS_OFFSET
mov x29, #0
#ifdef CONFIG_KASAN
bl kasan_early_init
#endif
b start_kernel
ENDPROC(__mmap_switched)

Expand Down
6 changes: 6 additions & 0 deletions arch/arm64/kernel/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ __efistub_strcmp = __pi_strcmp;
__efistub_strncmp = __pi_strncmp;
__efistub___flush_dcache_area = __pi___flush_dcache_area;

#ifdef CONFIG_KASAN
__efistub___memcpy = __pi_memcpy;
__efistub___memmove = __pi_memmove;
__efistub___memset = __pi_memset;
#endif

__efistub__text = _text;
__efistub__end = _end;
__efistub__edata = _edata;
Expand Down
16 changes: 13 additions & 3 deletions arch/arm64/kernel/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <linux/bitops.h>
#include <linux/elf.h>
#include <linux/gfp.h>
#include <linux/kasan.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/moduleloader.h>
Expand All @@ -34,9 +35,18 @@

void *module_alloc(unsigned long size)
{
return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
GFP_KERNEL, PAGE_KERNEL_EXEC, 0,
NUMA_NO_NODE, __builtin_return_address(0));
void *p;

p = __vmalloc_node_range(size, MODULE_ALIGN, MODULES_VADDR, MODULES_END,
GFP_KERNEL, PAGE_KERNEL_EXEC, 0,
NUMA_NO_NODE, __builtin_return_address(0));

if (p && (kasan_module_alloc(p, size) < 0)) {
vfree(p);
return NULL;
}

return p;
}

enum aarch64_reloc_op {
Expand Down
4 changes: 4 additions & 0 deletions arch/arm64/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include <asm/elf.h>
#include <asm/cpufeature.h>
#include <asm/cpu_ops.h>
#include <asm/kasan.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/smp_plat.h>
Expand Down Expand Up @@ -434,6 +435,9 @@ void __init setup_arch(char **cmdline_p)

paging_init();
relocate_initrd();

kasan_init();

request_standard_resources();

early_ioremap_reset();
Expand Down
3 changes: 3 additions & 0 deletions arch/arm64/lib/memcpy.S
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@
stp \ptr, \regB, [\regC], \val
.endm

.weak memcpy
ENTRY(__memcpy)
ENTRY(memcpy)
#include "copy_template.S"
ret
ENDPIPROC(memcpy)
ENDPROC(__memcpy)
7 changes: 5 additions & 2 deletions arch/arm64/lib/memmove.S
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ C_h .req x12
D_l .req x13
D_h .req x14

.weak memmove
ENTRY(__memmove)
ENTRY(memmove)
cmp dstin, src
b.lo memcpy
b.lo __memcpy
add tmp1, src, count
cmp dstin, tmp1
b.hs memcpy /* No overlap. */
b.hs __memcpy /* No overlap. */

add dst, dstin, count
add src, src, count
Expand Down Expand Up @@ -195,3 +197,4 @@ ENTRY(memmove)
b.ne .Ltail63
ret
ENDPIPROC(memmove)
ENDPROC(__memmove)
3 changes: 3 additions & 0 deletions arch/arm64/lib/memset.S
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ dst .req x8
tmp3w .req w9
tmp3 .req x9

.weak memset
ENTRY(__memset)
ENTRY(memset)
mov dst, dstin /* Preserve return value. */
and A_lw, val, #255
Expand Down Expand Up @@ -214,3 +216,4 @@ ENTRY(memset)
b.ne .Ltail_maybe_long
ret
ENDPIPROC(memset)
ENDPROC(__memset)
3 changes: 3 additions & 0 deletions arch/arm64/mm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ obj-y := dma-mapping.o extable.o fault.o init.o \
context.o proc.o pageattr.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_ARM64_PTDUMP) += dump.o

obj-$(CONFIG_KASAN) += kasan_init.o
KASAN_SANITIZE_kasan_init.o := n
Loading

0 comments on commit 39d114d

Please sign in to comment.