Skip to content

Commit

Permalink
Bare-metal KASan implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
rodionov committed Mar 6, 2024
0 parents commit 7d91f96
Show file tree
Hide file tree
Showing 29 changed files with 2,584 additions and 0 deletions.
33 changes: 33 additions & 0 deletions CONTRIBUTING
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# How to contribute

We'd love to accept your patches and contributions to this project.

## Before you begin

### Sign our Contributor License Agreement

Contributions to this project must be accompanied by a
[Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
You (or your employer) retain the copyright to your contribution; this simply
gives us permission to use and redistribute your contributions as part of the
project.

If you or your current employer have already signed the Google CLA (even if it
was for a different project), you probably don't need to do it again.

Visit <https://cla.developers.google.com/> to see your current agreements or to
sign a new one.

### Review our community guidelines

This project follows
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).

## Contribution process

### Code reviews

All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
339 changes: 339 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

98 changes: 98 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright 2024 Google LLC
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

CC := clang
LD := ld.lld

ARCH ?= arm
SUPPORTED_ARCH := arm aarch64 riscv32 riscv64 x86

ifeq ($(findstring $(ARCH),$(SUPPORTED_ARCH)),)
$(error Unsupported architecture $(ARCH), choose one from $(SUPPORTED_ARCH))
endif

include Makefile.$(ARCH)

CFLAGS := $(CFLAGS_ARCH)
CFLAGS += -ffreestanding

CFLAGS += -DTARGET_ARCH_$(ARCH)
CFLAGS += -DKASAN_SHADOW_MAPPING_OFFSET=$(KASAN_SHADOW_MAPPING_OFFSET)
CFLAGS += -DKASAN_SHADOW_MEMORY_START=$(KASAN_SHADOW_MEMORY_START)
CFLAGS += -DKASAN_SHADOW_MEMORY_SIZE=$(KASAN_SHADOW_MEMORY_SIZE)
CFLAGS += -DTARGET_DRAM_START=$(TARGET_DRAM_START)
CFLAGS += -DTARGET_DRAM_END=$(TARGET_DRAM_END)

CFLAGS += -DPRINTF_DISABLE_SUPPORT_FLOAT
CFLAGS += -DPRINTF_DISABLE_SUPPORT_EXPONENTIAL
CFLAGS += -DPRINTF_DISABLE_SUPPORT_PTRDIFF_T
CFLAGS += -DPRINTF_DISABLE_SUPPORT_LONG_LONG

CFLAGS += -Wno-incompatible-library-redeclaration

CFLAGS += -Ithird_party/printf -I./

LDFLAGS := -nostdlib

# KASan-specific compiler options
KASAN_SANITIZE_STACK := 1
KASAN_SANITIZE_GLOBALS := 1

KASAN_CC_FLAGS := -fsanitize=kernel-address
KASAN_CC_FLAGS += -fno-builtin
KASAN_CC_FLAGS += -mllvm -asan-mapping-offset=$(KASAN_SHADOW_MAPPING_OFFSET)
KASAN_CC_FLAGS += -mllvm -asan-instrumentation-with-call-threshold=0
KASAN_CC_FLAGS += -mllvm -asan-stack=$(KASAN_SANITIZE_STACK)
KASAN_CC_FLAGS += -mllvm -asan-globals=$(KASAN_SANITIZE_GLOBALS)
KASAN_CC_FLAGS += -DKASAN_ENABLED

SRCS := kasan.c \
heap.c \
kasan_test.c \
sanitized_lib.c \
rt_utils.c \
start_$(ARCH).S \
third_party/printf/printf.c

OBJS := $(SRCS:.c=.o)
OBJS := $(OBJS:.S=.o)

LD_SCRIPT := kasan_test.ld
LD_SCRIPT_GEN := kasan_test.lds

# Use KASAN_CC_FLAGS for the code we would like to cover with KASan
sanitized_lib.o: CFLAGS := $(CFLAGS) $(KASAN_CC_FLAGS)

# This workaround is not needed if you build the project with the LLVM
# toolchain of version 18 and higher (i.e. which includes
# https://github.com/llvm/llvm-project/pull/72933)
sanitized_lib.o: CFLAGS := $(subst $(ARCH_TARGET),$(KASAN_TARGET),$(CFLAGS))

%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@

%.o: %.S
$(CC) $(CFLAGS) -c $< -o $@

$(LD_SCRIPT_GEN): $(LD_SCRIPT)
$(CC) -E -P -x c $(CFLAGS) $< >> $@

kasan_test: $(LD_SCRIPT_GEN) $(OBJS)
$(LD) -T $(LD_SCRIPT_GEN) $(LDFLAGS) $(OBJS) -o $@

.PHONY: run
run: kasan_test
$(QEMU) $<

.PHONY: clean
clean:
rm -rf kasan_test $(OBJS) $(LD_SCRIPT_GEN)
rm -rf $(foreach arch,$(SUPPORTED_ARCH),start_$(arch).o)
15 changes: 15 additions & 0 deletions Makefile.aarch64
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
TARGET_DRAM_START := 0x40000000
TARGET_DRAM_END := 0x4fffffff
UART_BASE_ADDRESS := 0x09000000

KASAN_SHADOW_MAPPING_OFFSET := 0x42700000
KASAN_SHADOW_MEMORY_START := 0x4A700000
KASAN_SHADOW_MEMORY_SIZE := 0x2000000

ARCH_TARGET := aarch64-arm-none-eabi
KASAN_TARGET := aarch64-arm-linux-gnu

CFLAGS_ARCH := --target=$(ARCH_TARGET) -mcpu=cortex-a53+nofp+nosimd
CFLAGS_ARCH += -DUART_BASE_ADDRESS=$(UART_BASE_ADDRESS)

QEMU := qemu-system-aarch64 -M virt -cpu cortex-a53 -m 256M -nographic -kernel
15 changes: 15 additions & 0 deletions Makefile.arm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
TARGET_DRAM_START := 0x40000000
TARGET_DRAM_END := 0x4fffffff
UART_BASE_ADDRESS := 0x09000000

KASAN_SHADOW_MAPPING_OFFSET := 0x42700000
KASAN_SHADOW_MEMORY_START := 0x4A700000
KASAN_SHADOW_MEMORY_SIZE := 0x2000000

ARCH_TARGET := arm-arm-none-eabi
KASAN_TARGET := arm-arm-linux-gnu

CFLAGS_ARCH := --target=$(ARCH_TARGET) -mfloat-abi=soft -mcpu=cortex-a7
CFLAGS_ARCH += -DUART_BASE_ADDRESS=$(UART_BASE_ADDRESS)

QEMU := qemu-system-arm -M virt -cpu cortex-a7 -m 256M -nographic -kernel
14 changes: 14 additions & 0 deletions Makefile.riscv32
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
TARGET_DRAM_START := 0x80000000
TARGET_DRAM_END := 0x8fffffff
UART_BASE_ADDRESS := 0x10000000

KASAN_SHADOW_MAPPING_OFFSET := 0x77000000
KASAN_SHADOW_MEMORY_START := 0x87000000
KASAN_SHADOW_MEMORY_SIZE := 0x2000000

ARCH_TARGET := riscv32-none-none
KASAN_TARGET := riscv32-linux-gnu

CFLAGS_ARCH := --target=$(ARCH_TARGET) -march=rv32imafd
CFLAGS_ARCH += -DUART_BASE_ADDRESS=$(UART_BASE_ADDRESS)
QEMU := qemu-system-riscv32 -M virt -bios none -m 256M -nographic -kernel
14 changes: 14 additions & 0 deletions Makefile.riscv64
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
TARGET_DRAM_START := 0x80000000
TARGET_DRAM_END := 0x8fffffff
UART_BASE_ADDRESS := 0x10000000

KASAN_SHADOW_MAPPING_OFFSET := 0x77000000
KASAN_SHADOW_MEMORY_START := 0x87000000
KASAN_SHADOW_MEMORY_SIZE := 0x2000000

ARCH_TARGET := riscv64-none-none
KASAN_TARGET := riscv64-linux-gnu

CFLAGS_ARCH := --target=$(ARCH_TARGET) -march=rv64imafd -fPIC
CFLAGS_ARCH += -DUART_BASE_ADDRESS=$(UART_BASE_ADDRESS)
QEMU := qemu-system-riscv64 -M virt -bios none -m 256M -nographic -kernel
12 changes: 12 additions & 0 deletions Makefile.x86
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
TARGET_DRAM_START := 0x00100000
TARGET_DRAM_END := 0x07ffffff

KASAN_SHADOW_MAPPING_OFFSET := 0x06FE0000
KASAN_SHADOW_MEMORY_START := 0x07000000
KASAN_SHADOW_MEMORY_SIZE := 0x1000000

ARCH_TARGET := i386-none-none
KASAN_TARGET := i386-linux-gnu

CFLAGS_ARCH := --target=$(ARCH_TARGET)
QEMU := qemu-system-i386 -nographic -m 128M -kernel
131 changes: 131 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Bare-metal KASan implementation

*This is not an officially supported Google product.*

This project demonstrates how to enable Kernel Address Sanitizer (KASan) for
bare-metal code running on ARM, RISC-V and x86 architectures. It implements a
set of KASan test cases that catch various classes of memory corruption bugs
at runtime.

The implementation of KASan run-time routines in this project is inspired by
the corresponding implementation of
[KASan](https://www.kernel.org/doc/html/v4.14/dev-tools/kasan.html) in Linux
kernel.

## Prerequisites

To build and run the program you would need to use LLVM toolchain (`clang` and
`ld.lld`) for cross complitation and QEMU system emulator for supported
architectures.

For example, here are the necessary Debian package names needed to build and
run the project:

```
sudo apt-get install build-essential gcc-multilib llvm clang lld \
qemu-system-arm qemu-system-misc qemu-system-x86
```

## Project layout

The project constists of the following components:

* `kasan_test.c` -- main test driver which runs KASan test cases
* `sanitized_lib.c` -- this module implements the test cases and is built with
the KASan instrumentation
* `kasan.c` -- implementation of runtime routines needed for KASan sanitizer
* `heap.c` -- simple implementation of heap management routines for testing
KASan
* `third_party/printf.c` -- a compact implementation of `printf` function
* `rt_utils.c` -- run-time utility functions
* `start_arch.S` -- architecture-specific low-level entry point in assembly
for the bare-metal program
* `kasan_test.ld` -- linker script for the program
* `Makefile` -- in addition to instructions on how to build and run the project
this file contains definitions of some important parameters,
such as KASan shadow memory address, DRAM start address and KASan
configuration options.
* `Makefile.arch` -- Makefile fragments whith architecture-specific parameters
for building and running the project in the emulator


## Running

To build and execute the test suite run `ARCH=target_arch make clean run`
where `target_arch` is one of the supported architectures: `arm`, `aarch64`,
`riscv32`, `riscv64` and `x86`. If the target architecture isn't specified
(i.e. `make clean run`) then `arm` is assumed as default option.

As an example, running `make clean run` should build the bare-metal program
and execute it in QEMU ARM system emulator with the following expected output:

```
qemu-system-arm -M virt-8.2 -cpu cortex-a7 -m 256M -nographic -kernel kasan_test
Starting bare-metal KASan test driver.
KASan test: heap OOB write
Writing 1 byte at offset 18 in 17-byte heap buffer allocated at 40004020
[KASan] ===================================================
[KASan] ERROR: Invalid memory access: address 0x40004032, size 0x1, is_write 1, ip 0x40000D98
[KASan] Shadow bytes around the buggy address 0x40004030 (shadow 0x4A700806):
[KASan] 0x4A7007D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7007E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7007F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] =>0x4A700800: FA FA FA FA 00 00[01]FB FB FB FB 00 00 00 00 00
[KASan] 0x4A700810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A700820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A700830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
KASan test: stack OOB read
Reading 1 byte at offset 18 in 17-byte stack buffer at 40014f90
[KASan] ===================================================
[KASan] ERROR: Invalid memory access: address 0x40014FA2, size 0x1, is_write 0, ip 0x40000E6C
[KASan] Shadow bytes around the buggy address 0x40014FA0 (shadow 0x4A7029F4):
[KASan] 0x4A7029C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7029D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7029E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] =>0x4A7029F0: F1 F1 00 00[01]F3 F3 F3 F3 F3 00 00 00 00 00 00
[KASan] 0x4A702A00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A702A10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A702A20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
KASan test: global OOB write
Writing an integer at index 18 in 17-element global integer array at 400035a0
[KASan] ===================================================
[KASan] ERROR: Invalid memory access: address 0x400035E8, size 0x4, is_write 1, ip 0x40000F38
[KASan] Shadow bytes around the buggy address 0x400035E8 (shadow 0x4A7006BD):
[KASan] 0x4A700680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A700690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7006A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] =>0x4A7006B0: 01 F9 F9 F9 00 00 00 00 00 00 00 00 04[F9]F9 F9
[KASan] 0x4A7006C0: F9 F9 F9 F9 00 00 01 F9 F9 F9 F9 F9 00 00 00 00
[KASan] 0x4A7006D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7006E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
KASan test: memset OOB write in globals
Memsetting global 17-byte buffer at 40003620 with 18 values of 0xaa
[KASan] ===================================================
[KASan] ERROR: Invalid memory access: address 0x40003620, size 0x12, is_write 1, ip 0x40000D1C
[KASan] Shadow bytes around the buggy address 0x40003630 (shadow 0x4A7006C6):
[KASan] 0x4A700690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7006A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7006B0: 01 F9 F9 F9 00 00 00 00 00 00 00 00 04 F9 F9 F9
[KASan] =>0x4A7006C0: F9 F9 F9 F9 00 00[01]F9 F9 F9 F9 F9 00 00 00 00
[KASan] 0x4A7006D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7006E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7006F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
KASan test: memcpy OOB read from globals
Memcopying 18 bytes from 17-byte global buffer into local array
[KASan] ===================================================
[KASan] ERROR: Invalid memory access: address 0x40003620, size 0x12, is_write 0, ip 0x40000D20
[KASan] Shadow bytes around the buggy address 0x40003630 (shadow 0x4A7006C6):
[KASan] 0x4A700690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7006A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7006B0: 01 F9 F9 F9 00 00 00 00 00 00 00 00 04 F9 F9 F9
[KASan] =>0x4A7006C0: F9 F9 F9 F9 00 00[01]F9 F9 F9 F9 F9 00 00 00 00
[KASan] 0x4A7006D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7006E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[KASan] 0x4A7006F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Press ctrl + a then x to exit.
```
39 changes: 39 additions & 0 deletions common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2024 Google LLC
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/

#ifndef __KASAN_COMMON_H__
#define __KASAN_COMMON_H__

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#define CALLER_PC ((unsigned long)__builtin_return_address(0))

#ifdef KASAN_ENABLED

void *__kasan_memcpy(void *dst, const void *src, unsigned int size,
uintptr_t pc);
void *__kasan_memset(void *buf, int c, unsigned int size, uintptr_t pc);

#define memcpy(dst, src, size) __kasan_memcpy(dst, src, size, CALLER_PC)
#define memset(buf, c, size) __kasan_memset(buf, c, size, CALLER_PC)

#else // KASAN_ENABLED

void *memcpy(void *dst, const void *src, unsigned long size);
void *memset(void *buf, int c, unsigned long size);

#endif // KASAN_ENABLED

#endif // __KASAN_COMMON_H__
Loading

0 comments on commit 7d91f96

Please sign in to comment.