Kernel: Initial integration of Kernel Address Sanitizer (KASAN)

KASAN is a dynamic analysis tool that finds memory errors. It focuses
mostly on finding use-after-free and out-of-bound read/writes bugs.

KASAN works by allocating a "shadow memory" region which is used to store
whether each byte of memory is safe to access. The compiler then instruments
the kernel code and a check is inserted which validates the state of the
shadow memory region on every memory access (load or store).

To fully integrate KASAN into the SerenityOS kernel we need to:

 a) Implement the KASAN interface to intercept the injected loads/stores.

      void __asan_load*(address);
      void __asan_store(address);

 b) Setup KASAN region and determine the shadow memory offset + translation.
    This might be challenging since Serenity is only 32bit at this time.

    Ex: Linux implements kernel address -> shadow address translation like:

      static inline void *kasan_mem_to_shadow(const void *addr)
      {
          return ((unsigned long)addr >> KASAN_SHADOW_SCALE_SHIFT)
                  + KASAN_SHADOW_OFFSET;
      }

 c) Integrating KASAN with Kernel allocators.
    The kernel allocators need to be taught how to record allocation state
    in the shadow memory region.

This commit only implements the initial steps of this long process:
- A new (default OFF) CMake build flag `ENABLE_KERNEL_ADDRESS_SANITIZER`
- Stubs out enough of the KASAN interface to allow the Kernel to link clean.

Currently the KASAN kernel crashes on boot (triple fault because of the crash
in strlen other sanitizer are seeing) but the goal here is to just get started,
and this should help others jump in and continue making progress on KASAN.

References:
* ASAN Paper: https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/37752.pdf
* KASAN Docs: https://github.com/google/kasan
* NetBSD KASAN Blog: https://blog.netbsd.org/tnf/entry/kernel_address_sanitizer_part_3
* LWN KASAN Article: https://lwn.net/Articles/612153/
* Tracking Issue #5351
This commit is contained in:
Brian Gianforcaro 2021-02-14 12:47:10 -08:00 committed by Andreas Kling
parent be48a89b35
commit 96943ab07c
Notes: sideshowbarker 2024-07-18 22:15:53 +09:00
4 changed files with 172 additions and 0 deletions

View file

@ -18,6 +18,7 @@ set(SERENITY_ARCH "i686" CACHE STRING "Target architecture for SerenityOS.")
# Central location for all custom options used in the Serenity build.
option(ENABLE_ADDRESS_SANITIZER "Enable address sanitizer testing in gcc/clang" FALSE)
option(ENABLE_KERNEL_ADDRESS_SANITIZER "Enable kernel address sanitizer testing in gcc/clang" FALSE)
option(ENABLE_MEMORY_SANITIZER "Enable memory sanitizer testing in gcc/clang" FALSE)
option(ENABLE_UNDEFINED_SANITIZER "Enable undefined behavior sanitizer testing in gcc/clang" FALSE)
option(ENABLE_FUZZER_SANITIZER "Enable fuzzer sanitizer testing in clang" FALSE)

126
Kernel/AddressSanitizer.cpp Normal file
View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2021, Brian Gianforcaro <b.gianfo@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if defined(__SANITIZE_ADDRESS__)
# include <Kernel/AddressSanitizer.h>
void Kernel::AddressSanitizer::shadow_va_check_load(unsigned long address, size_t size, void* return_address)
{
(void)address;
(void)size;
(void)return_address;
}
void Kernel::AddressSanitizer::shadow_va_check_store(unsigned long address, size_t size, void* return_address)
{
(void)address;
(void)size;
(void)return_address;
}
using namespace Kernel;
using namespace Kernel::AddressSanitizer;
extern "C" {
// Define a macro to easily declare the KASAN load and store callbacks for
// the various sizes of data type.
//
# define ADDRESS_SANITIZER_LOAD_STORE(size) \
void __asan_load##size(unsigned long); \
void __asan_load##size(unsigned long address) \
{ \
shadow_va_check_load(address, size, __builtin_return_address(0)); \
} \
void __asan_load##size##_noabort(unsigned long); \
void __asan_load##size##_noabort(unsigned long address) \
{ \
shadow_va_check_load(address, size, __builtin_return_address(0)); \
} \
void __asan_store##size(unsigned long); \
void __asan_store##size(unsigned long address) \
{ \
shadow_va_check_store(address, size, __builtin_return_address(0)); \
} \
void __asan_store##size##_noabort(unsigned long); \
void __asan_store##size##_noabort(unsigned long address) \
{ \
shadow_va_check_store(address, size, __builtin_return_address(0)); \
}
ADDRESS_SANITIZER_LOAD_STORE(1);
ADDRESS_SANITIZER_LOAD_STORE(2);
ADDRESS_SANITIZER_LOAD_STORE(4);
ADDRESS_SANITIZER_LOAD_STORE(8);
ADDRESS_SANITIZER_LOAD_STORE(16);
# undef ADDRESS_SANITIZER_LOAD_STORE
void __asan_loadN(unsigned long, size_t);
void __asan_loadN(unsigned long address, size_t size)
{
shadow_va_check_load(address, size, __builtin_return_address(0));
}
void __asan_loadN_noabort(unsigned long, size_t);
void __asan_loadN_noabort(unsigned long address, size_t size)
{
shadow_va_check_load(address, size, __builtin_return_address(0));
}
void __asan_storeN(unsigned long, size_t);
void __asan_storeN(unsigned long address, size_t size)
{
shadow_va_check_store(address, size, __builtin_return_address(0));
}
void __asan_storeN_noabort(unsigned long, size_t);
void __asan_storeN_noabort(unsigned long address, size_t size)
{
shadow_va_check_store(address, size, __builtin_return_address(0));
}
// Performs shadow memory cleanup of the current thread's stack before a
// function marked with the [[noreturn]] attribute is called.
//
void __asan_handle_no_return(void);
void __asan_handle_no_return(void)
{
}
void __asan_before_dynamic_init(const char*);
void __asan_before_dynamic_init(const char* /* module_name */)
{
}
void __asan_after_dynamic_init();
void __asan_after_dynamic_init()
{
}
}
#endif

37
Kernel/AddressSanitizer.h Normal file
View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2021, Brian Gianforcaro<b.gianfo@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <AK/Types.h>
namespace Kernel::AddressSanitizer {
void shadow_va_check_load(unsigned long address, size_t size, void* return_addr);
void shadow_va_check_store(unsigned long address, size_t size, void* return_addr);
}

View file

@ -10,6 +10,7 @@ set(KERNEL_SOURCES
ACPI/Initialize.cpp
ACPI/MultiProcessorParser.cpp
ACPI/Parser.cpp
AddressSanitizer.cpp
Arch/i386/CPU.cpp
Arch/i386/ProcessorInfo.cpp
Arch/i386/SafeMem.cpp
@ -307,6 +308,13 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-asynchronous-unwind-tables")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -nostdlib -nostdinc -nostdinc++")
# Kernel Address Sanitize (KASAN) implementation is still a work in progress, this option
# is not currently meant to be used, besides when developing Kernel ASAN support.
#
if (ENABLE_KERNEL_ADDRESS_SANITIZER)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=kernel-address")
endif()
add_compile_definitions(KERNEL)
# HACK: This is a workaround for CLion to grok the kernel sources.