diff --git a/config.sh b/config.sh index 37826ed99..2eea05d6d 100644 --- a/config.sh +++ b/config.sh @@ -18,6 +18,8 @@ export INCLUDEDIR=$PREFIX/include export CFLAGS='-O2 -g' export CPPFLAGS='--std=c++20' +export UBSAN=1 + # Configure the cross-compiler to use the desired system root. export SYSROOT="$(pwd)/sysroot" export CC="$CC --sysroot=$SYSROOT" diff --git a/kernel/Makefile b/kernel/Makefile index 4087db1a4..e6cc174f5 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -27,6 +27,10 @@ CPPFLAGS:=$(CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS) LDFLAGS:=$(LDFLAGS) $(KERNEL_ARCH_LDFLAGS) LIBS:=$(LIBS) $(KERNEL_ARCH_LIBS) +ifeq ($(UBSAN), 1) +CFLAGS:=$(CFLAGS) -fsanitize=undefined +endif + BUILDDIR=$(abspath build) KERNEL_OBJS= \ @@ -58,6 +62,7 @@ kernel/Thread.o \ kernel/TTY.o \ kernel/VesaTerminalDriver.o \ icxxabi.o \ +ubsan.o \ OBJS= \ $(ARCHDIR)/crti.o \ diff --git a/kernel/arch/i386/MMU.cpp b/kernel/arch/i386/MMU.cpp index 9a192485f..655883027 100644 --- a/kernel/arch/i386/MMU.cpp +++ b/kernel/arch/i386/MMU.cpp @@ -47,9 +47,9 @@ MMU::MMU() m_highest_paging_struct[i] = (uint64_t)page_directory | Flags::Present; } - // create and identity map first 4 MiB + // create and identity map first 6 MiB uint64_t* page_directory1 = (uint64_t*)(m_highest_paging_struct[0] & PAGE_MASK); - for (uint64_t i = 0; i < 2; i++) + for (uint64_t i = 0; i < 3; i++) { uint64_t* page_table = allocate_page_aligned_page(); for (uint64_t j = 0; j < 512; j++) diff --git a/kernel/arch/i386/boot.S b/kernel/arch/i386/boot.S index 0dab850da..8dcfa41dc 100644 --- a/kernel/arch/i386/boot.S +++ b/kernel/arch/i386/boot.S @@ -118,9 +118,10 @@ enable_sse: ret initialize_paging: - # identity map first 4 MiB + # identity map first 6 MiB movl $(0x00000000 + 0x83), boot_page_directory1 + 0 movl $(0x00200000 + 0x83), boot_page_directory1 + 8 + movl $(0x00400000 + 0x83), boot_page_directory1 + 16 movl $(boot_page_directory1 + 0x01), boot_page_directory_pointer_table # enable PAE diff --git a/kernel/arch/x86_64/MMU.cpp b/kernel/arch/x86_64/MMU.cpp index 54c6fd61a..6d3c99185 100644 --- a/kernel/arch/x86_64/MMU.cpp +++ b/kernel/arch/x86_64/MMU.cpp @@ -35,7 +35,7 @@ static uint64_t* allocate_page_aligned_page() MMU::MMU() { - // Identity map from 4 KiB -> 4 MiB + // Identity map from 4 KiB -> 6 MiB m_highest_paging_struct = allocate_page_aligned_page(); uint64_t* pdpt = allocate_page_aligned_page(); @@ -44,7 +44,7 @@ MMU::MMU() uint64_t* pd = allocate_page_aligned_page(); pdpt[0] = (uint64_t)pd | Flags::ReadWrite | Flags::Present; - for (uint32_t i = 0; i < 2; i++) + for (uint32_t i = 0; i < 3; i++) { uint64_t* pt = allocate_page_aligned_page(); for (uint64_t j = 0; j < 512; j++) diff --git a/kernel/arch/x86_64/boot.S b/kernel/arch/x86_64/boot.S index ed810323e..78eb26c9f 100644 --- a/kernel/arch/x86_64/boot.S +++ b/kernel/arch/x86_64/boot.S @@ -41,9 +41,9 @@ # we will identity map first 4 MiB # 0 MiB -> 1 MiB: bootloader stuff - # 1 MiB -> 2 MiB: kernel - # 2 MiB -> 3 MiB: kmalloc - # 3 MiB -> 4 MiB: kmalloc_fixed + # 1 MiB -> 4 MiB: kernel + # 4 MiB -> 5 MiB: kmalloc + # 5 MiB -> 6 MiB: kmalloc_fixed .align 4096 boot_pml4: .skip 512 * 8 @@ -127,9 +127,10 @@ enable_sse: ret initialize_paging: - # identity map first 4 MiB + # identity map first 6 MiB movl $(0x00000000 + PG_PAGE_SIZE + PG_READ_WRITE + PG_PRESENT), boot_pd1 + 0 movl $(0x00200000 + PG_PAGE_SIZE + PG_READ_WRITE + PG_PRESENT), boot_pd1 + 8 + movl $(0x00400000 + PG_PAGE_SIZE + PG_READ_WRITE + PG_PRESENT), boot_pd1 + 16 # set pdpte1 and pml4e1 movl $(boot_pd1 + PG_READ_WRITE + PG_PRESENT), boot_pdpt1 diff --git a/kernel/kernel/kmalloc.cpp b/kernel/kernel/kmalloc.cpp index 5ea6993da..c24a1a91d 100644 --- a/kernel/kernel/kmalloc.cpp +++ b/kernel/kernel/kmalloc.cpp @@ -65,7 +65,7 @@ private: static_assert(sizeof(kmalloc_node) == s_kmalloc_min_align); struct kmalloc_info { - static constexpr uintptr_t base = 0x00200000; + static constexpr uintptr_t base = 0x00400000; static constexpr size_t size = 1 * MB; static constexpr uintptr_t end = base + size; @@ -313,11 +313,10 @@ void* kmalloc(size_t size, size_t align) if (size == 0 || size >= info.size) return nullptr; - ASSERT(align); ASSERT(is_power_of_two(align)); // if the size fits into fixed node, we will try to use that since it is faster - if (align == s_kmalloc_min_align && size < sizeof(kmalloc_fixed_info::node::data)) + if (align == s_kmalloc_min_align && size <= sizeof(kmalloc_fixed_info::node::data)) if (void* result = kmalloc_fixed()) return result; diff --git a/kernel/ubsan.cpp b/kernel/ubsan.cpp new file mode 100644 index 000000000..d74cf9977 --- /dev/null +++ b/kernel/ubsan.cpp @@ -0,0 +1,108 @@ +#include +#include + +extern "C" +{ + + struct source_location + { + const char *file; + uint32_t line; + uint32_t column; + }; + + struct type_descriptor + { + uint16_t kind; + uint16_t info; + char name[1]; + }; + + struct type_mismatch_data + { + source_location location; + const type_descriptor& type; + uint8_t alignment; + uint8_t type_check_kind; + }; + + struct out_of_bounds_data + { + source_location location; + const type_descriptor& left_type; + const type_descriptor& right_type; + }; + + struct shift_out_of_bounds_data + { + source_location location; + const type_descriptor& left_type; + const type_descriptor& right_type; + }; + + struct pointer_overflow_data + { + source_location location; + }; + + struct invalid_value_data + { + source_location location; + const type_descriptor& type; + }; + + struct overflow_data + { + source_location location; + const type_descriptor& type; + }; + + using value_handle = uintptr_t; + + static const char* type_check_kinds[] = { + "load of", "store to", "reference binding to", "member access within", + "member call on", "constructor call on", "downcast of", "downcast of", + "upcast of", "cast to virtual base of", "_Nonnull binding to", + "dynamic operation on" + }; + + #define HANDLER(handler, fatal, ...) \ + void handler(__VA_ARGS__) \ + { \ + if constexpr(fatal) \ + Kernel::panic("{}:{} {}", data->location.file, data->location.line, __FUNCTION__); \ + else \ + derrorln("{}:{} {}", data->location.file, data->location.line, __FUNCTION__); \ + } + + static void print_location_impl(const source_location& location, const char* function) + { + derrorln("{}:{} {}", location.file, location.line, function); + } + #define print_location(location) print_location_impl(location, __FUNCTION__) + + HANDLER(__ubsan_handle_pointer_overflow, false, pointer_overflow_data* data, value_handle, value_handle) + HANDLER(__ubsan_handle_load_invalid_value, false, invalid_value_data* data, value_handle) + HANDLER(__ubsan_handle_out_of_bounds, false, out_of_bounds_data* data, value_handle) + HANDLER(__ubsan_handle_shift_out_of_bounds, false, shift_out_of_bounds_data* data, value_handle, value_handle) + + HANDLER(__ubsan_handle_add_overflow, false, overflow_data* data, value_handle, value_handle) + HANDLER(__ubsan_handle_sub_overflow, false, overflow_data* data, value_handle, value_handle) + HANDLER(__ubsan_handle_mul_overflow, false, overflow_data* data, value_handle, value_handle) + HANDLER(__ubsan_handle_divrem_overflow, false, overflow_data* data, value_handle, value_handle) + HANDLER(__ubsan_handle_negate_overflow, false, overflow_data* data, value_handle) + + void __ubsan_handle_type_mismatch_v1(type_mismatch_data* data, value_handle pointer) + { + print_location(data->location); + const char* kind = type_check_kinds[data->type_check_kind]; + uintptr_t alignment = (uintptr_t)1 << data->alignment; + if (!pointer) + derrorln("{} null pointer of type {}", kind, data->type.name); + else if (pointer & (alignment - 1)) + derrorln("{} misaligned address {} for type {}, which requires {} byte alignment", kind, (void*)pointer, data->type.name, alignment); + else + derrorln("{} address {} with insufficient space for an object of type {}", kind, (void*)pointer, data->type.name); + } + +} \ No newline at end of file