mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 03:22:43 +00:00 
			
		
		
		
	Kernel: Add initial basic support for KASAN
This commit adds minimal support for compiler-instrumentation based memory access sanitization. Currently we only support detection of kmalloc redzone accesses, and kmalloc use-after-free accesses. Support for inline checks (for improved performance), and for stack use-after-return and use-after-return detection is left for future PRs.
This commit is contained in:
		
							parent
							
								
									7ad7ae7000
								
							
						
					
					
						commit
						f7a1f28d7f
					
				
					 10 changed files with 538 additions and 63 deletions
				
			
		|  | @ -722,11 +722,15 @@ if (ENABLE_KERNEL_UNDEFINED_SANITIZER) | ||||||
|     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") |     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
| # 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) | if (ENABLE_KERNEL_ADDRESS_SANITIZER) | ||||||
|     add_compile_options(-fsanitize=kernel-address) |     add_compile_options(-fsanitize=kernel-address) | ||||||
|  |     if(CMAKE_CXX_COMPILER_ID MATCHES "Clang$") | ||||||
|  |         # TODO: Support inline KASAN for improved performance | ||||||
|  |         add_compile_options("SHELL:-mllvm -asan-instrumentation-with-call-threshold=0") | ||||||
|  |         # TODO: Support KASAN stack poisoning (inline) for use-after-return and use-after-scope detection | ||||||
|  |         add_compile_options("SHELL:-mllvm -asan-stack=0") | ||||||
|  |     endif() | ||||||
|  |     set_source_files_properties(Security/AddressSanitizer.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=kernel-address") | ||||||
|     add_link_options(-fsanitize=kernel-address) |     add_link_options(-fsanitize=kernel-address) | ||||||
| endif() | endif() | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2023, Idan Horowitz <idan.horowitz@serenityos.org> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/KASANDeadly.h> | ||||||
|  | #include <Kernel/Security/AddressSanitizer.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | UNMAP_AFTER_INIT SysFSKASANDeadly::SysFSKASANDeadly(SysFSDirectory const& parent_directory) | ||||||
|  |     : SysFSSystemBooleanVariable(parent_directory) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | UNMAP_AFTER_INIT NonnullRefPtr<SysFSKASANDeadly> SysFSKASANDeadly::must_create(SysFSDirectory const& parent_directory) | ||||||
|  | { | ||||||
|  |     return adopt_ref_if_nonnull(new (nothrow) SysFSKASANDeadly(parent_directory)).release_nonnull(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool SysFSKASANDeadly::value() const | ||||||
|  | { | ||||||
|  |     return AddressSanitizer::g_kasan_is_deadly; | ||||||
|  | } | ||||||
|  | void SysFSKASANDeadly::set_value(bool new_value) | ||||||
|  | { | ||||||
|  |     AddressSanitizer::g_kasan_is_deadly = new_value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,28 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2023, Idan Horowitz <idan.horowitz@serenityos.org> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <AK/RefPtr.h> | ||||||
|  | #include <AK/Types.h> | ||||||
|  | #include <Kernel/FileSystem/SysFS/Subsystems/Kernel/Configuration/BooleanVariable.h> | ||||||
|  | #include <Kernel/Library/UserOrKernelBuffer.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | class SysFSKASANDeadly final : public SysFSSystemBooleanVariable { | ||||||
|  | public: | ||||||
|  |     virtual StringView name() const override { return "kasan_is_deadly"sv; } | ||||||
|  |     static NonnullRefPtr<SysFSKASANDeadly> must_create(SysFSDirectory const&); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     virtual bool value() const override; | ||||||
|  |     virtual void set_value(bool new_value) override; | ||||||
|  | 
 | ||||||
|  |     explicit SysFSKASANDeadly(SysFSDirectory const&); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
| #include <AK/TemporaryChange.h> | #include <AK/TemporaryChange.h> | ||||||
| #include <AK/Vector.h> | #include <AK/Vector.h> | ||||||
| #include <AK/kmalloc.h> | #include <AK/kmalloc.h> | ||||||
|  | #include <Kernel/Security/AddressSanitizer.h> | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
|  | @ -68,7 +69,7 @@ public: | ||||||
|         return needed_chunks * CHUNK_SIZE + (needed_chunks + 7) / 8; |         return needed_chunks * CHUNK_SIZE + (needed_chunks + 7) / 8; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void* allocate(size_t size, size_t alignment, CallerWillInitializeMemory caller_will_initialize_memory) |     void* allocate(size_t size, size_t alignment, [[maybe_unused]] CallerWillInitializeMemory caller_will_initialize_memory) | ||||||
|     { |     { | ||||||
|         // The minimum possible alignment is CHUNK_SIZE, since we only track chunks here, nothing smaller.
 |         // The minimum possible alignment is CHUNK_SIZE, since we only track chunks here, nothing smaller.
 | ||||||
|         if (alignment < CHUNK_SIZE) |         if (alignment < CHUNK_SIZE) | ||||||
|  | @ -104,17 +105,23 @@ public: | ||||||
|         VERIFY(first_chunk.value() <= aligned_first_chunk); |         VERIFY(first_chunk.value() <= aligned_first_chunk); | ||||||
|         VERIFY(aligned_first_chunk + chunks_needed <= first_chunk.value() + chunks_needed + chunk_alignment); |         VERIFY(aligned_first_chunk + chunks_needed <= first_chunk.value() + chunks_needed + chunk_alignment); | ||||||
| 
 | 
 | ||||||
|  | #ifdef HAS_ADDRESS_SANITIZER | ||||||
|  |         AddressSanitizer::mark_region((FlatPtr)a, real_size, (chunks_needed * CHUNK_SIZE), AddressSanitizer::ShadowType::Malloc); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|         u8* ptr = a->data; |         u8* ptr = a->data; | ||||||
|         a->allocation_size_in_chunks = chunks_needed; |         a->allocation_size_in_chunks = chunks_needed; | ||||||
| 
 | 
 | ||||||
|         m_bitmap.set_range_and_verify_that_all_bits_flip(aligned_first_chunk, chunks_needed, true); |         m_bitmap.set_range_and_verify_that_all_bits_flip(aligned_first_chunk, chunks_needed, true); | ||||||
| 
 | 
 | ||||||
|         m_allocated_chunks += chunks_needed; |         m_allocated_chunks += chunks_needed; | ||||||
|  | #ifndef HAS_ADDRESS_SANITIZER | ||||||
|         if (caller_will_initialize_memory == CallerWillInitializeMemory::No) { |         if (caller_will_initialize_memory == CallerWillInitializeMemory::No) { | ||||||
|             if constexpr (HEAP_SCRUB_BYTE_ALLOC != 0) { |             if constexpr (HEAP_SCRUB_BYTE_ALLOC != 0) { | ||||||
|                 __builtin_memset(ptr, HEAP_SCRUB_BYTE_ALLOC, (chunks_needed * CHUNK_SIZE) - sizeof(AllocationHeader)); |                 __builtin_memset(ptr, HEAP_SCRUB_BYTE_ALLOC, (chunks_needed * CHUNK_SIZE) - sizeof(AllocationHeader)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|         VERIFY((FlatPtr)ptr % alignment == 0); |         VERIFY((FlatPtr)ptr % alignment == 0); | ||||||
|         return ptr; |         return ptr; | ||||||
|  | @ -137,9 +144,13 @@ public: | ||||||
|         VERIFY(m_allocated_chunks >= a->allocation_size_in_chunks); |         VERIFY(m_allocated_chunks >= a->allocation_size_in_chunks); | ||||||
|         m_allocated_chunks -= a->allocation_size_in_chunks; |         m_allocated_chunks -= a->allocation_size_in_chunks; | ||||||
| 
 | 
 | ||||||
|  | #ifdef HAS_ADDRESS_SANITIZER | ||||||
|  |         AddressSanitizer::fill_shadow((FlatPtr)a, a->allocation_size_in_chunks * CHUNK_SIZE, AddressSanitizer::ShadowType::Free); | ||||||
|  | #else | ||||||
|         if constexpr (HEAP_SCRUB_BYTE_FREE != 0) { |         if constexpr (HEAP_SCRUB_BYTE_FREE != 0) { | ||||||
|             __builtin_memset(a, HEAP_SCRUB_BYTE_FREE, a->allocation_size_in_chunks * CHUNK_SIZE); |             __builtin_memset(a, HEAP_SCRUB_BYTE_FREE, a->allocation_size_in_chunks * CHUNK_SIZE); | ||||||
|         } |         } | ||||||
|  | #endif | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool contains(void const* ptr) const |     bool contains(void const* ptr) const | ||||||
|  |  | ||||||
|  | @ -16,6 +16,7 @@ | ||||||
| #include <Kernel/Locking/Spinlock.h> | #include <Kernel/Locking/Spinlock.h> | ||||||
| #include <Kernel/Memory/MemoryManager.h> | #include <Kernel/Memory/MemoryManager.h> | ||||||
| #include <Kernel/Sections.h> | #include <Kernel/Sections.h> | ||||||
|  | #include <Kernel/Security/AddressSanitizer.h> | ||||||
| #include <Kernel/Tasks/PerformanceManager.h> | #include <Kernel/Tasks/PerformanceManager.h> | ||||||
| 
 | 
 | ||||||
| #if ARCH(X86_64) || ARCH(AARCH64) || ARCH(RISCV64) | #if ARCH(X86_64) || ARCH(AARCH64) || ARCH(RISCV64) | ||||||
|  | @ -65,11 +66,18 @@ public: | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void* allocate() |     void* allocate([[maybe_unused]] size_t requested_size) | ||||||
|     { |     { | ||||||
|         VERIFY(m_freelist); |         VERIFY(m_freelist); | ||||||
|         ++m_allocated_slabs; |         ++m_allocated_slabs; | ||||||
|         return exchange(m_freelist, m_freelist->next); | #ifdef HAS_ADDRESS_SANITIZER | ||||||
|  |         AddressSanitizer::fill_shadow((FlatPtr)m_freelist, sizeof(FreelistEntry::next), Kernel::AddressSanitizer::ShadowType::Unpoisoned8Bytes); | ||||||
|  | #endif | ||||||
|  |         auto* ptr = exchange(m_freelist, m_freelist->next); | ||||||
|  | #ifdef HAS_ADDRESS_SANITIZER | ||||||
|  |         AddressSanitizer::mark_region((FlatPtr)ptr, requested_size, m_slab_size, AddressSanitizer::ShadowType::Malloc); | ||||||
|  | #endif | ||||||
|  |         return ptr; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void deallocate(void* ptr) |     void deallocate(void* ptr) | ||||||
|  | @ -77,7 +85,13 @@ public: | ||||||
|         VERIFY(ptr >= &m_data && ptr < ((u8*)this + block_size)); |         VERIFY(ptr >= &m_data && ptr < ((u8*)this + block_size)); | ||||||
|         --m_allocated_slabs; |         --m_allocated_slabs; | ||||||
|         auto* freelist_entry = (FreelistEntry*)ptr; |         auto* freelist_entry = (FreelistEntry*)ptr; | ||||||
|  | #ifdef HAS_ADDRESS_SANITIZER | ||||||
|  |         AddressSanitizer::fill_shadow((FlatPtr)freelist_entry, sizeof(FreelistEntry::next), Kernel::AddressSanitizer::ShadowType::Unpoisoned8Bytes); | ||||||
|  | #endif | ||||||
|         freelist_entry->next = m_freelist; |         freelist_entry->next = m_freelist; | ||||||
|  | #ifdef HAS_ADDRESS_SANITIZER | ||||||
|  |         AddressSanitizer::fill_shadow((FlatPtr)freelist_entry, m_slab_size, AddressSanitizer::ShadowType::Free); | ||||||
|  | #endif | ||||||
|         m_freelist = freelist_entry; |         m_freelist = freelist_entry; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -122,7 +136,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     size_t slab_size() const { return m_slab_size; } |     size_t slab_size() const { return m_slab_size; } | ||||||
| 
 | 
 | ||||||
|     void* allocate(CallerWillInitializeMemory caller_will_initialize_memory) |     void* allocate(size_t requested_size, [[maybe_unused]] CallerWillInitializeMemory caller_will_initialize_memory) | ||||||
|     { |     { | ||||||
|         if (m_usable_blocks.is_empty()) { |         if (m_usable_blocks.is_empty()) { | ||||||
|             // FIXME: This allocation wastes `block_size` bytes due to the implementation of kmalloc_aligned().
 |             // FIXME: This allocation wastes `block_size` bytes due to the implementation of kmalloc_aligned().
 | ||||||
|  | @ -136,19 +150,23 @@ public: | ||||||
|             m_usable_blocks.append(*block); |             m_usable_blocks.append(*block); | ||||||
|         } |         } | ||||||
|         auto* block = m_usable_blocks.first(); |         auto* block = m_usable_blocks.first(); | ||||||
|         auto* ptr = block->allocate(); |         auto* ptr = block->allocate(requested_size); | ||||||
|         if (block->is_full()) |         if (block->is_full()) | ||||||
|             m_full_blocks.append(*block); |             m_full_blocks.append(*block); | ||||||
| 
 | 
 | ||||||
|  | #ifndef HAS_ADDRESS_SANITIZER | ||||||
|         if (caller_will_initialize_memory == CallerWillInitializeMemory::No) { |         if (caller_will_initialize_memory == CallerWillInitializeMemory::No) { | ||||||
|             memset(ptr, KMALLOC_SCRUB_BYTE, m_slab_size); |             memset(ptr, KMALLOC_SCRUB_BYTE, m_slab_size); | ||||||
|         } |         } | ||||||
|  | #endif | ||||||
|         return ptr; |         return ptr; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void deallocate(void* ptr) |     void deallocate(void* ptr) | ||||||
|     { |     { | ||||||
|  | #ifndef HAS_ADDRESS_SANITIZER | ||||||
|         memset(ptr, KFREE_SCRUB_BYTE, m_slab_size); |         memset(ptr, KFREE_SCRUB_BYTE, m_slab_size); | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
|         auto* block = (KmallocSlabBlock*)((FlatPtr)ptr & KmallocSlabBlock::block_mask); |         auto* block = (KmallocSlabBlock*)((FlatPtr)ptr & KmallocSlabBlock::block_mask); | ||||||
|         bool block_was_full = block->is_full(); |         bool block_was_full = block->is_full(); | ||||||
|  | @ -227,7 +245,7 @@ struct KmallocGlobalData { | ||||||
| 
 | 
 | ||||||
|         for (auto& slabheap : slabheaps) { |         for (auto& slabheap : slabheaps) { | ||||||
|             if (size <= slabheap.slab_size() && alignment <= slabheap.slab_size()) |             if (size <= slabheap.slab_size() && alignment <= slabheap.slab_size()) | ||||||
|                 return slabheap.allocate(caller_will_initialize_memory); |                 return slabheap.allocate(size, caller_will_initialize_memory); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         for (auto& subheap : subheaps) { |         for (auto& subheap : subheaps) { | ||||||
|  |  | ||||||
|  | @ -24,6 +24,7 @@ | ||||||
| #include <Kernel/Memory/SharedInodeVMObject.h> | #include <Kernel/Memory/SharedInodeVMObject.h> | ||||||
| #include <Kernel/Prekernel/Prekernel.h> | #include <Kernel/Prekernel/Prekernel.h> | ||||||
| #include <Kernel/Sections.h> | #include <Kernel/Sections.h> | ||||||
|  | #include <Kernel/Security/AddressSanitizer.h> | ||||||
| #include <Kernel/Tasks/Process.h> | #include <Kernel/Tasks/Process.h> | ||||||
| 
 | 
 | ||||||
| extern u8 start_of_kernel_image[]; | extern u8 start_of_kernel_image[]; | ||||||
|  | @ -105,6 +106,10 @@ UNMAP_AFTER_INIT MemoryManager::MemoryManager() | ||||||
|     // By using a tag we don't have to query the VMObject for every page
 |     // By using a tag we don't have to query the VMObject for every page
 | ||||||
|     // whether it was committed or not
 |     // whether it was committed or not
 | ||||||
|     m_lazy_committed_page = committed_pages.take_one(); |     m_lazy_committed_page = committed_pages.take_one(); | ||||||
|  | 
 | ||||||
|  | #ifdef HAS_ADDRESS_SANITIZER | ||||||
|  |     initialize_kasan_shadow_memory(); | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| UNMAP_AFTER_INIT MemoryManager::~MemoryManager() = default; | UNMAP_AFTER_INIT MemoryManager::~MemoryManager() = default; | ||||||
|  | @ -579,6 +584,26 @@ UNMAP_AFTER_INIT void MemoryManager::initialize_physical_pages() | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef HAS_ADDRESS_SANITIZER | ||||||
|  | void MemoryManager::initialize_kasan_shadow_memory() | ||||||
|  | { | ||||||
|  |     m_global_data.with([&](auto& global_data) { | ||||||
|  |         // We map every 8 bytes of normal memory to 1 byte of shadow memory, so we need a 1/9 of total memory for the shadow memory.
 | ||||||
|  |         auto virtual_range = global_data.region_tree.total_range(); | ||||||
|  |         auto shadow_range_size = MUST(page_round_up(ceil_div(virtual_range.size(), 9ul))); | ||||||
|  |         dbgln("MM: Reserving {} bytes for KASAN shadow memory", shadow_range_size); | ||||||
|  | 
 | ||||||
|  |         auto vmobject = MUST(AnonymousVMObject::try_create_with_size(shadow_range_size, AllocationStrategy::AllocateNow)); | ||||||
|  |         auto* shadow_region = MUST(Region::create_unplaced(move(vmobject), 0, {}, Memory::Region::Access::ReadWrite)).leak_ptr(); | ||||||
|  |         auto shadow_range = VirtualRange { virtual_range.base().offset(virtual_range.size() - shadow_range_size), shadow_range_size }; | ||||||
|  |         MUST(global_data.region_tree.place_specifically(*shadow_region, shadow_range)); | ||||||
|  |         MUST(shadow_region->map(kernel_page_directory())); | ||||||
|  | 
 | ||||||
|  |         AddressSanitizer::init(shadow_region->vaddr().get()); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| PhysicalPageEntry& MemoryManager::get_physical_page_entry(PhysicalAddress physical_address) | PhysicalPageEntry& MemoryManager::get_physical_page_entry(PhysicalAddress physical_address) | ||||||
| { | { | ||||||
|     auto physical_page_entry_index = PhysicalAddress::physical_page_index(physical_address.get()); |     auto physical_page_entry_index = PhysicalAddress::physical_page_index(physical_address.get()); | ||||||
|  |  | ||||||
|  | @ -243,6 +243,10 @@ private: | ||||||
|     void initialize_physical_pages(); |     void initialize_physical_pages(); | ||||||
|     void register_reserved_ranges(); |     void register_reserved_ranges(); | ||||||
| 
 | 
 | ||||||
|  | #ifdef HAS_ADDRESS_SANITIZER | ||||||
|  |     void initialize_kasan_shadow_memory(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|     void unregister_kernel_region(Region&); |     void unregister_kernel_region(Region&); | ||||||
| 
 | 
 | ||||||
|     void protect_kernel_image(); |     void protect_kernel_image(); | ||||||
|  |  | ||||||
|  | @ -1,25 +1,249 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org> |  * Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org> | ||||||
|  |  * Copyright (c) 2023, Idan Horowitz <idan.horowitz@serenityos.org> | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #if defined(__SANITIZE_ADDRESS__) | #include <AK/Platform.h> | ||||||
| 
 | 
 | ||||||
|  | #include <Kernel/Arch/Processor.h> | ||||||
|  | #include <Kernel/Boot/BootInfo.h> | ||||||
|  | #include <Kernel/KSyms.h> | ||||||
|  | #include <Kernel/Library/StdLib.h> | ||||||
| #include <Kernel/Security/AddressSanitizer.h> | #include <Kernel/Security/AddressSanitizer.h> | ||||||
| 
 | 
 | ||||||
| void Kernel::AddressSanitizer::shadow_va_check_load(unsigned long address, size_t size, void* return_address) | static constexpr size_t kasan_shadow_scale_offset = 3; // We map each 8 real bytes to 1 shadow byte
 | ||||||
|  | static constexpr size_t kasan_shadow_scale = 1 << kasan_shadow_scale_offset; | ||||||
|  | static constexpr size_t kasan_shadow_mask = kasan_shadow_scale - 1; | ||||||
|  | 
 | ||||||
|  | // Defined in clang
 | ||||||
|  | static constexpr size_t kasan_alloca_redzone_size = 32; | ||||||
|  | 
 | ||||||
|  | namespace Kernel::AddressSanitizer { | ||||||
|  | 
 | ||||||
|  | enum class AccessType { | ||||||
|  |     Load, | ||||||
|  |     Store | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static constexpr StringView to_string(AccessType shadow_type) | ||||||
| { | { | ||||||
|     (void)address; |     switch (shadow_type) { | ||||||
|     (void)size; |     case AccessType::Load: | ||||||
|     (void)return_address; |         return "Load"sv; | ||||||
|  |     case AccessType::Store: | ||||||
|  |         return "Store"sv; | ||||||
|  |     default: | ||||||
|  |         return "Unknown"sv; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Kernel::AddressSanitizer::shadow_va_check_store(unsigned long address, size_t size, void* return_address) | static constexpr StringView to_string(ShadowType shadow_type) | ||||||
| { | { | ||||||
|     (void)address; |     switch (shadow_type) { | ||||||
|     (void)size; |     case ShadowType::Unpoisoned8Bytes: | ||||||
|     (void)return_address; |         return "8 Bytes Unpoisoned"sv; | ||||||
|  |     case ShadowType::Unpoisoned1Byte: | ||||||
|  |         return "1 Byte Unpoisoned | 7 Bytes Poisoned"sv; | ||||||
|  |     case ShadowType::Unpoisoned2Bytes: | ||||||
|  |         return "2 Bytes Unpoisoned | 6 Bytes Poisoned"sv; | ||||||
|  |     case ShadowType::Unpoisoned3Bytes: | ||||||
|  |         return "3 Bytes Unpoisoned | 5 Bytes Poisoned"sv; | ||||||
|  |     case ShadowType::Unpoisoned4Bytes: | ||||||
|  |         return "4 Bytes Unpoisoned | 4 Bytes Poisoned"sv; | ||||||
|  |     case ShadowType::Unpoisoned5Bytes: | ||||||
|  |         return "5 Bytes Unpoisoned | 3 Bytes Poisoned"sv; | ||||||
|  |     case ShadowType::Unpoisoned6Bytes: | ||||||
|  |         return "6 Bytes Unpoisoned | 2 Bytes Poisoned"sv; | ||||||
|  |     case ShadowType::Unpoisoned7Bytes: | ||||||
|  |         return "7 Bytes Unpoisoned | 1 Byte Poisoned"sv; | ||||||
|  |     case ShadowType::StackLeft: | ||||||
|  |         return "Stack Left Redzone"sv; | ||||||
|  |     case ShadowType::StackMiddle: | ||||||
|  |         return "Stack Middle Redzone"sv; | ||||||
|  |     case ShadowType::StackRight: | ||||||
|  |         return "Stack Right Redzone"sv; | ||||||
|  |     case ShadowType::UseAfterReturn: | ||||||
|  |         return "Use After Return"sv; | ||||||
|  |     case ShadowType::UseAfterScope: | ||||||
|  |         return "Use After Scope"sv; | ||||||
|  |     case ShadowType::Generic: | ||||||
|  |         return "Generic Redzone"sv; | ||||||
|  |     case ShadowType::Malloc: | ||||||
|  |         return "Malloc Redzone"sv; | ||||||
|  |     case ShadowType::Free: | ||||||
|  |         return "Freed Region"sv; | ||||||
|  |     default: | ||||||
|  |         return "Unknown"sv; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Atomic<bool> g_kasan_is_deadly { true }; | ||||||
|  | 
 | ||||||
|  | static void print_violation(FlatPtr address, size_t size, AccessType access_type, ShadowType shadow_type, void* return_address) | ||||||
|  | { | ||||||
|  |     critical_dmesgln("KASAN: Invalid {}-byte {} access to {}, which is marked as '{}' [at {:p}]", size, to_string(access_type), VirtualAddress(address), to_string(shadow_type), return_address); | ||||||
|  |     dump_backtrace(g_kasan_is_deadly ? PrintToScreen::Yes : PrintToScreen::No); | ||||||
|  |     if (g_kasan_is_deadly) { | ||||||
|  |         critical_dmesgln("KASAN is configured to be deadly, halting the system."); | ||||||
|  |         Processor::halt(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static FlatPtr kasan_shadow_base; | ||||||
|  | static FlatPtr kasan_shadow_offset; | ||||||
|  | static bool kasan_initialized = false; | ||||||
|  | 
 | ||||||
|  | void init(FlatPtr shadow_base) | ||||||
|  | { | ||||||
|  |     kasan_shadow_base = shadow_base; | ||||||
|  |     kasan_shadow_offset = shadow_base - (kernel_mapping_base >> kasan_shadow_scale_offset); | ||||||
|  |     kasan_initialized = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline ShadowType* va_to_shadow(FlatPtr address) | ||||||
|  | { | ||||||
|  |     return (ShadowType*)((address >> kasan_shadow_scale_offset) + kasan_shadow_offset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void fill_shadow(FlatPtr address, size_t size, ShadowType type) | ||||||
|  | { | ||||||
|  |     if (!kasan_initialized) [[unlikely]] | ||||||
|  |         return; | ||||||
|  |     VERIFY((address % kasan_shadow_scale) == 0); | ||||||
|  |     VERIFY((size % kasan_shadow_scale) == 0); | ||||||
|  |     auto* shadow = va_to_shadow(address); | ||||||
|  |     auto shadow_size = size >> kasan_shadow_scale_offset; | ||||||
|  |     memset(shadow, to_underlying(type), shadow_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mark_region(FlatPtr address, size_t valid_size, size_t total_size, ShadowType type) | ||||||
|  | { | ||||||
|  |     if (!kasan_initialized) [[unlikely]] | ||||||
|  |         return; | ||||||
|  |     VERIFY((address % kasan_shadow_scale) == 0); | ||||||
|  |     VERIFY((total_size % kasan_shadow_scale) == 0); | ||||||
|  |     auto* shadow = va_to_shadow(address); | ||||||
|  |     auto valid_shadow_size = valid_size >> kasan_shadow_scale_offset; | ||||||
|  |     memset(shadow, to_underlying(ShadowType::Unpoisoned8Bytes), valid_shadow_size); | ||||||
|  |     auto unaligned_size = valid_size & kasan_shadow_mask; | ||||||
|  |     if (unaligned_size) | ||||||
|  |         *(shadow + valid_shadow_size) = static_cast<ShadowType>(unaligned_size); | ||||||
|  |     auto poisoned_shadow_size = (total_size - round_up_to_power_of_two(valid_size, kasan_shadow_scale)) >> kasan_shadow_scale_offset; | ||||||
|  |     memset(shadow + valid_shadow_size + (unaligned_size != 0), to_underlying(type), poisoned_shadow_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool shadow_va_check_1b(FlatPtr address, ShadowType& shadow_type) | ||||||
|  | { | ||||||
|  |     auto const shadow = *va_to_shadow(address); | ||||||
|  |     i8 const minimal_valid_shadow = (address & kasan_shadow_mask) + 1; | ||||||
|  |     if (shadow == ShadowType::Unpoisoned8Bytes || (minimal_valid_shadow <= static_cast<i8>(shadow))) [[likely]] | ||||||
|  |         return true; | ||||||
|  |     shadow_type = shadow; | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool shadow_va_check_2b(FlatPtr address, ShadowType& shadow_type) | ||||||
|  | { | ||||||
|  |     // Check for unaligned access
 | ||||||
|  |     if ((address >> kasan_shadow_scale_offset) != (address + 1) >> kasan_shadow_scale_offset) [[unlikely]] | ||||||
|  |         return shadow_va_check_1b(address, shadow_type) && shadow_va_check_1b(address + 1, shadow_type); | ||||||
|  | 
 | ||||||
|  |     auto const shadow = *va_to_shadow(address); | ||||||
|  |     i8 const minimal_valid_shadow = ((address + 1) & kasan_shadow_mask) + 1; | ||||||
|  |     if (shadow == ShadowType::Unpoisoned8Bytes || (minimal_valid_shadow <= static_cast<i8>(shadow))) [[likely]] | ||||||
|  |         return true; | ||||||
|  |     shadow_type = shadow; | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool shadow_va_check_4b(FlatPtr address, ShadowType& shadow_type) | ||||||
|  | { | ||||||
|  |     // Check for unaligned access
 | ||||||
|  |     if ((address >> kasan_shadow_scale_offset) != (address + 3) >> kasan_shadow_scale_offset) [[unlikely]] | ||||||
|  |         return shadow_va_check_2b(address, shadow_type) && shadow_va_check_2b(address + 2, shadow_type); | ||||||
|  | 
 | ||||||
|  |     auto const shadow = *va_to_shadow(address); | ||||||
|  |     i8 const minimal_valid_shadow = ((address + 3) & kasan_shadow_mask) + 1; | ||||||
|  |     if (shadow == ShadowType::Unpoisoned8Bytes || (minimal_valid_shadow <= static_cast<i8>(shadow))) [[likely]] | ||||||
|  |         return true; | ||||||
|  |     shadow_type = shadow; | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool shadow_va_check_8b(FlatPtr address, ShadowType& shadow_type) | ||||||
|  | { | ||||||
|  |     // Check for unaligned access
 | ||||||
|  |     if ((address >> kasan_shadow_scale_offset) != (address + 7) >> kasan_shadow_scale_offset) [[unlikely]] | ||||||
|  |         return shadow_va_check_4b(address, shadow_type) && shadow_va_check_4b(address + 4, shadow_type); | ||||||
|  | 
 | ||||||
|  |     auto const shadow = *va_to_shadow(address); | ||||||
|  |     i8 const minimal_valid_shadow = ((address + 7) & kasan_shadow_mask) + 1; | ||||||
|  |     if (shadow == ShadowType::Unpoisoned8Bytes || (minimal_valid_shadow <= static_cast<i8>(shadow))) [[likely]] | ||||||
|  |         return true; | ||||||
|  |     shadow_type = shadow; | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool shadow_va_check_Nb(FlatPtr address, size_t n, ShadowType& shadow_type) | ||||||
|  | { | ||||||
|  |     while ((address % 8) && (n > 0)) { | ||||||
|  |         if (!shadow_va_check_1b(address, shadow_type)) [[unlikely]] | ||||||
|  |             return false; | ||||||
|  |         address++; | ||||||
|  |         n--; | ||||||
|  |     } | ||||||
|  |     while (n >= 8) { | ||||||
|  |         if (!shadow_va_check_8b(address, shadow_type)) | ||||||
|  |             return false; | ||||||
|  |         address += 8; | ||||||
|  |         n -= 8; | ||||||
|  |     } | ||||||
|  |     while (n > 0) { | ||||||
|  |         if (!shadow_va_check_1b(address, shadow_type)) [[unlikely]] | ||||||
|  |             return false; | ||||||
|  |         address++; | ||||||
|  |         n--; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void shadow_va_check(FlatPtr address, size_t size, AccessType access_type, void* return_address) | ||||||
|  | { | ||||||
|  |     if (size == 0) [[unlikely]] | ||||||
|  |         return; | ||||||
|  |     if (!kasan_initialized) [[unlikely]] | ||||||
|  |         return; | ||||||
|  |     if (address < kernel_mapping_base || address >= kasan_shadow_base) [[unlikely]] | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     bool valid = false; | ||||||
|  |     ShadowType shadow_type = ShadowType::Unpoisoned8Bytes; | ||||||
|  |     switch (size) { | ||||||
|  |     case 1: | ||||||
|  |         valid = shadow_va_check_1b(address, shadow_type); | ||||||
|  |         break; | ||||||
|  |     case 2: | ||||||
|  |         valid = shadow_va_check_2b(address, shadow_type); | ||||||
|  |         break; | ||||||
|  |     case 4: | ||||||
|  |         valid = shadow_va_check_4b(address, shadow_type); | ||||||
|  |         break; | ||||||
|  |     case 8: | ||||||
|  |         valid = shadow_va_check_8b(address, shadow_type); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         valid = shadow_va_check_Nb(address, size, shadow_type); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (valid) [[likely]] | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     print_violation(address, size, access_type, shadow_type, return_address); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| using namespace Kernel; | using namespace Kernel; | ||||||
|  | @ -31,25 +255,45 @@ extern "C" { | ||||||
| // the various sizes of data type.
 | // the various sizes of data type.
 | ||||||
| //
 | //
 | ||||||
| #define ADDRESS_SANITIZER_LOAD_STORE(size)                                                                   \ | #define ADDRESS_SANITIZER_LOAD_STORE(size)                                                                   \ | ||||||
|         void __asan_load##size(unsigned long);                                 \ |     void __asan_load##size(FlatPtr);                                                                         \ | ||||||
|         void __asan_load##size(unsigned long address)                          \ |     void __asan_load##size(FlatPtr address)                                                                  \ | ||||||
|     {                                                                                                        \ |     {                                                                                                        \ | ||||||
|             shadow_va_check_load(address, size, __builtin_return_address(0));  \ |         shadow_va_check(address, size, AccessType::Load, __builtin_return_address(0));                       \ | ||||||
|     }                                                                                                        \ |     }                                                                                                        \ | ||||||
|         void __asan_load##size##_noabort(unsigned long);                       \ |     void __asan_load##size##_noabort(FlatPtr);                                                               \ | ||||||
|         void __asan_load##size##_noabort(unsigned long address)                \ |     void __asan_load##size##_noabort(FlatPtr address)                                                        \ | ||||||
|     {                                                                                                        \ |     {                                                                                                        \ | ||||||
|             shadow_va_check_load(address, size, __builtin_return_address(0));  \ |         shadow_va_check(address, size, AccessType::Load, __builtin_return_address(0));                       \ | ||||||
|     }                                                                                                        \ |     }                                                                                                        \ | ||||||
|         void __asan_store##size(unsigned long);                                \ |     void __asan_store##size(FlatPtr);                                                                        \ | ||||||
|         void __asan_store##size(unsigned long address)                         \ |     void __asan_store##size(FlatPtr address)                                                                 \ | ||||||
|     {                                                                                                        \ |     {                                                                                                        \ | ||||||
|             shadow_va_check_store(address, size, __builtin_return_address(0)); \ |         shadow_va_check(address, size, AccessType::Store, __builtin_return_address(0));                      \ | ||||||
|     }                                                                                                        \ |     }                                                                                                        \ | ||||||
|         void __asan_store##size##_noabort(unsigned long);                      \ |     void __asan_store##size##_noabort(FlatPtr);                                                              \ | ||||||
|         void __asan_store##size##_noabort(unsigned long address)               \ |     void __asan_store##size##_noabort(FlatPtr address)                                                       \ | ||||||
|     {                                                                                                        \ |     {                                                                                                        \ | ||||||
|             shadow_va_check_store(address, size, __builtin_return_address(0)); \ |         shadow_va_check(address, size, AccessType::Store, __builtin_return_address(0));                      \ | ||||||
|  |     }                                                                                                        \ | ||||||
|  |     void __asan_report_load##size(FlatPtr);                                                                  \ | ||||||
|  |     void __asan_report_load##size(FlatPtr address)                                                           \ | ||||||
|  |     {                                                                                                        \ | ||||||
|  |         print_violation(address, size, AccessType::Load, ShadowType::Generic, __builtin_return_address(0));  \ | ||||||
|  |     }                                                                                                        \ | ||||||
|  |     void __asan_report_load##size##_noabort(FlatPtr);                                                        \ | ||||||
|  |     void __asan_report_load##size##_noabort(FlatPtr address)                                                 \ | ||||||
|  |     {                                                                                                        \ | ||||||
|  |         print_violation(address, size, AccessType::Load, ShadowType::Generic, __builtin_return_address(0));  \ | ||||||
|  |     }                                                                                                        \ | ||||||
|  |     void __asan_report_store##size(FlatPtr);                                                                 \ | ||||||
|  |     void __asan_report_store##size(FlatPtr address)                                                          \ | ||||||
|  |     {                                                                                                        \ | ||||||
|  |         print_violation(address, size, AccessType::Store, ShadowType::Generic, __builtin_return_address(0)); \ | ||||||
|  |     }                                                                                                        \ | ||||||
|  |     void __asan_report_store##size##_noabort(FlatPtr);                                                       \ | ||||||
|  |     void __asan_report_store##size##_noabort(FlatPtr address)                                                \ | ||||||
|  |     {                                                                                                        \ | ||||||
|  |         print_violation(address, size, AccessType::Store, ShadowType::Generic, __builtin_return_address(0)); \ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| ADDRESS_SANITIZER_LOAD_STORE(1); | ADDRESS_SANITIZER_LOAD_STORE(1); | ||||||
|  | @ -60,40 +304,123 @@ ADDRESS_SANITIZER_LOAD_STORE(16); | ||||||
| 
 | 
 | ||||||
| #undef ADDRESS_SANITIZER_LOAD_STORE | #undef ADDRESS_SANITIZER_LOAD_STORE | ||||||
| 
 | 
 | ||||||
| void __asan_loadN(unsigned long, size_t); | void __asan_loadN(FlatPtr, size_t); | ||||||
| void __asan_loadN(unsigned long address, size_t size) | void __asan_loadN(FlatPtr address, size_t size) | ||||||
| { | { | ||||||
|     shadow_va_check_load(address, size, __builtin_return_address(0)); |     shadow_va_check(address, size, AccessType::Load, __builtin_return_address(0)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void __asan_loadN_noabort(unsigned long, size_t); | void __asan_loadN_noabort(FlatPtr, size_t); | ||||||
| void __asan_loadN_noabort(unsigned long address, size_t size) | void __asan_loadN_noabort(FlatPtr address, size_t size) | ||||||
| { | { | ||||||
|     shadow_va_check_load(address, size, __builtin_return_address(0)); |     shadow_va_check(address, size, AccessType::Load, __builtin_return_address(0)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void __asan_storeN(unsigned long, size_t); | void __asan_storeN(FlatPtr, size_t); | ||||||
| void __asan_storeN(unsigned long address, size_t size) | void __asan_storeN(FlatPtr address, size_t size) | ||||||
| { | { | ||||||
|     shadow_va_check_store(address, size, __builtin_return_address(0)); |     shadow_va_check(address, size, AccessType::Store, __builtin_return_address(0)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void __asan_storeN_noabort(unsigned long, size_t); | void __asan_storeN_noabort(FlatPtr, size_t); | ||||||
| void __asan_storeN_noabort(unsigned long address, size_t size) | void __asan_storeN_noabort(FlatPtr address, size_t size) | ||||||
| { | { | ||||||
|     shadow_va_check_store(address, size, __builtin_return_address(0)); |     shadow_va_check(address, size, AccessType::Store, __builtin_return_address(0)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __asan_report_load_n(FlatPtr, size_t); | ||||||
|  | void __asan_report_load_n(FlatPtr address, size_t size) | ||||||
|  | { | ||||||
|  |     print_violation(address, size, AccessType::Load, ShadowType::Generic, __builtin_return_address(0)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __asan_report_load_n_noabort(FlatPtr, size_t); | ||||||
|  | void __asan_report_load_n_noabort(FlatPtr address, size_t size) | ||||||
|  | { | ||||||
|  |     print_violation(address, size, AccessType::Load, ShadowType::Generic, __builtin_return_address(0)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __asan_report_store_n(FlatPtr, size_t); | ||||||
|  | void __asan_report_store_n(FlatPtr address, size_t size) | ||||||
|  | { | ||||||
|  |     print_violation(address, size, AccessType::Store, ShadowType::Generic, __builtin_return_address(0)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __asan_report_store_n_noabort(FlatPtr, size_t); | ||||||
|  | void __asan_report_store_n_noabort(FlatPtr address, size_t size) | ||||||
|  | { | ||||||
|  |     print_violation(address, size, AccessType::Store, ShadowType::Generic, __builtin_return_address(0)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // As defined in the compiler
 | ||||||
|  | struct __asan_global_source_location { | ||||||
|  |     char const* filename; | ||||||
|  |     int line_number; | ||||||
|  |     int column_number; | ||||||
|  | }; | ||||||
|  | struct __asan_global { | ||||||
|  |     uintptr_t address; | ||||||
|  |     size_t valid_size; | ||||||
|  |     size_t total_size; | ||||||
|  |     char const* name; | ||||||
|  |     char const* module_name; | ||||||
|  |     size_t has_dynamic_init; | ||||||
|  |     struct __asan_global_source_location* location; | ||||||
|  |     size_t odr_indicator; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void __asan_register_globals(struct __asan_global*, size_t); | ||||||
|  | void __asan_register_globals(struct __asan_global* globals, size_t count) | ||||||
|  | { | ||||||
|  |     for (auto i = 0u; i < count; ++i) | ||||||
|  |         mark_region(globals[i].address, globals[i].valid_size, globals[i].total_size, ShadowType::Generic); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __asan_unregister_globals(struct __asan_global*, size_t); | ||||||
|  | void __asan_unregister_globals(struct __asan_global* globals, size_t count) | ||||||
|  | { | ||||||
|  |     for (auto i = 0u; i < count; ++i) | ||||||
|  |         mark_region(globals[i].address, globals[i].total_size, globals[i].total_size, ShadowType::Unpoisoned8Bytes); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __asan_alloca_poison(FlatPtr, size_t); | ||||||
|  | void __asan_alloca_poison(FlatPtr address, size_t size) | ||||||
|  | { | ||||||
|  |     VERIFY(address % kasan_alloca_redzone_size == 0); | ||||||
|  |     auto rounded_size = round_up_to_power_of_two(size, kasan_alloca_redzone_size); | ||||||
|  |     fill_shadow(address - kasan_alloca_redzone_size, kasan_alloca_redzone_size, ShadowType::StackLeft); | ||||||
|  |     mark_region(address, size, rounded_size, Kernel::AddressSanitizer::ShadowType::StackMiddle); | ||||||
|  |     fill_shadow(address + rounded_size, kasan_alloca_redzone_size, Kernel::AddressSanitizer::ShadowType::StackRight); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __asan_allocas_unpoison(FlatPtr, size_t); | ||||||
|  | void __asan_allocas_unpoison(FlatPtr start, size_t end) | ||||||
|  | { | ||||||
|  |     VERIFY(start >= end); | ||||||
|  |     auto size = end - start; | ||||||
|  |     VERIFY(size % kasan_shadow_scale == 0); | ||||||
|  |     fill_shadow(start, size, Kernel::AddressSanitizer::ShadowType::Unpoisoned8Bytes); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __asan_poison_stack_memory(FlatPtr, size_t); | ||||||
|  | void __asan_poison_stack_memory(FlatPtr address, size_t size) | ||||||
|  | { | ||||||
|  |     fill_shadow(address, round_up_to_power_of_two(size, kasan_shadow_scale), Kernel::AddressSanitizer::ShadowType::UseAfterScope); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __asan_unpoison_stack_memory(FlatPtr, size_t); | ||||||
|  | void __asan_unpoison_stack_memory(FlatPtr address, size_t size) | ||||||
|  | { | ||||||
|  |     fill_shadow(address, round_up_to_power_of_two(size, kasan_shadow_scale), Kernel::AddressSanitizer::ShadowType::Unpoisoned8Bytes); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // 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_handle_no_return(void) | void __asan_handle_no_return(void) | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void __asan_before_dynamic_init(char const*); | void __asan_before_dynamic_init(char const*); | ||||||
| void __asan_before_dynamic_init(char const* /* module_name */) | void __asan_before_dynamic_init(char const*) | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -102,5 +429,3 @@ void __asan_after_dynamic_init() | ||||||
| { | { | ||||||
| } | } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|  | @ -1,17 +1,40 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org> |  * Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org> | ||||||
|  |  * Copyright (c) 2023, Idan Horowitz <idan.horowitz@serenityos.org> | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <AK/Atomic.h> | ||||||
| #include <AK/Types.h> | #include <AK/Types.h> | ||||||
| 
 | 
 | ||||||
| namespace Kernel::AddressSanitizer { | namespace Kernel::AddressSanitizer { | ||||||
| 
 | 
 | ||||||
| void shadow_va_check_load(unsigned long address, size_t size, void* return_addr); | extern Atomic<bool> g_kasan_is_deadly; | ||||||
| 
 | 
 | ||||||
| void shadow_va_check_store(unsigned long address, size_t size, void* return_addr); | enum class ShadowType : u8 { | ||||||
|  |     Unpoisoned8Bytes = 0, | ||||||
|  |     Unpoisoned1Byte = 1, | ||||||
|  |     Unpoisoned2Bytes = 2, | ||||||
|  |     Unpoisoned3Bytes = 3, | ||||||
|  |     Unpoisoned4Bytes = 4, | ||||||
|  |     Unpoisoned5Bytes = 5, | ||||||
|  |     Unpoisoned6Bytes = 6, | ||||||
|  |     Unpoisoned7Bytes = 7, | ||||||
|  |     StackLeft = 0xF1, | ||||||
|  |     StackMiddle = 0xF2, | ||||||
|  |     StackRight = 0xF3, | ||||||
|  |     UseAfterReturn = 0xF5, | ||||||
|  |     UseAfterScope = 0xF8, | ||||||
|  |     Generic = 0xFA, | ||||||
|  |     Malloc = 0xFB, | ||||||
|  |     Free = 0xFC, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void init(FlatPtr shadow_base); | ||||||
|  | void fill_shadow(FlatPtr address, size_t size, ShadowType type); | ||||||
|  | void mark_region(FlatPtr address, size_t valid_size, size_t total_size, ShadowType type); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -145,7 +145,7 @@ new file mode 100644 | ||||||
| index 000000000..4fdf45a19
 | index 000000000..4fdf45a19
 | ||||||
| --- /dev/null
 | --- /dev/null
 | ||||||
| +++ b/clang/lib/Driver/ToolChains/Serenity.cpp
 | +++ b/clang/lib/Driver/ToolChains/Serenity.cpp
 | ||||||
| @@ -0,0 +1,336 @@
 | @@ -0,0 +1,340 @@
 | ||||||
| +//===---- Serenity.cpp - SerenityOS ToolChain Implementation ----*- C++ -*-===//
 | +//===---- Serenity.cpp - SerenityOS ToolChain Implementation ----*- C++ -*-===//
 | ||||||
| +//
 | +//
 | ||||||
| +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | ||||||
|  | @ -482,12 +482,16 @@ index 000000000..4fdf45a19 | ||||||
| +
 | +
 | ||||||
| +  return ToolChain::UNW_None;
 | +  return ToolChain::UNW_None;
 | ||||||
| +}
 | +}
 | ||||||
|  | +
 | ||||||
|  | +SanitizerMask Serenity::getSupportedSanitizers() const {
 | ||||||
|  | +  return ToolChain::getSupportedSanitizers() | SanitizerKind::KernelAddress;
 | ||||||
|  | +}
 | ||||||
| diff --git a/clang/lib/Driver/ToolChains/Serenity.h b/clang/lib/Driver/ToolChains/Serenity.h
 | diff --git a/clang/lib/Driver/ToolChains/Serenity.h b/clang/lib/Driver/ToolChains/Serenity.h
 | ||||||
| new file mode 100644 | new file mode 100644 | ||||||
| index 000000000..feb31a0d6
 | index 000000000..feb31a0d6
 | ||||||
| --- /dev/null
 | --- /dev/null
 | ||||||
| +++ b/clang/lib/Driver/ToolChains/Serenity.h
 | +++ b/clang/lib/Driver/ToolChains/Serenity.h
 | ||||||
| @@ -0,0 +1,100 @@
 | @@ -0,0 +1,102 @@
 | ||||||
| +//===---- Serenity.h - SerenityOS ToolChain Implementation ------*- C++ -*-===//
 | +//===---- Serenity.h - SerenityOS ToolChain Implementation ------*- C++ -*-===//
 | ||||||
| +//
 | +//
 | ||||||
| +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | ||||||
|  | @ -563,6 +567,8 @@ index 000000000..feb31a0d6 | ||||||
| +  bool isPIEDefault(const llvm::opt::ArgList&) const override { return false; }
 | +  bool isPIEDefault(const llvm::opt::ArgList&) const override { return false; }
 | ||||||
| +  bool isPICDefaultForced() const override { return false; }
 | +  bool isPICDefaultForced() const override { return false; }
 | ||||||
| +
 | +
 | ||||||
|  | +  SanitizerMask getSupportedSanitizers() const override;
 | ||||||
|  | +
 | ||||||
| +  bool IsMathErrnoDefault() const override { return false; }
 | +  bool IsMathErrnoDefault() const override { return false; }
 | ||||||
| +
 | +
 | ||||||
| +  UnwindTableLevel
 | +  UnwindTableLevel
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Idan Horowitz
						Idan Horowitz