Kernel: Add ubsan

My brain has been melting since I'm getting very random bugs.
I hope I can debug them better with ubsan :)
This commit is contained in:
Bananymous 2023-03-06 23:38:05 +02:00
parent e480f9c195
commit 06db890d49
8 changed files with 128 additions and 12 deletions

View File

@ -18,6 +18,8 @@ export INCLUDEDIR=$PREFIX/include
export CFLAGS='-O2 -g' export CFLAGS='-O2 -g'
export CPPFLAGS='--std=c++20' export CPPFLAGS='--std=c++20'
export UBSAN=1
# Configure the cross-compiler to use the desired system root. # Configure the cross-compiler to use the desired system root.
export SYSROOT="$(pwd)/sysroot" export SYSROOT="$(pwd)/sysroot"
export CC="$CC --sysroot=$SYSROOT" export CC="$CC --sysroot=$SYSROOT"

View File

@ -27,6 +27,10 @@ CPPFLAGS:=$(CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS)
LDFLAGS:=$(LDFLAGS) $(KERNEL_ARCH_LDFLAGS) LDFLAGS:=$(LDFLAGS) $(KERNEL_ARCH_LDFLAGS)
LIBS:=$(LIBS) $(KERNEL_ARCH_LIBS) LIBS:=$(LIBS) $(KERNEL_ARCH_LIBS)
ifeq ($(UBSAN), 1)
CFLAGS:=$(CFLAGS) -fsanitize=undefined
endif
BUILDDIR=$(abspath build) BUILDDIR=$(abspath build)
KERNEL_OBJS= \ KERNEL_OBJS= \
@ -58,6 +62,7 @@ kernel/Thread.o \
kernel/TTY.o \ kernel/TTY.o \
kernel/VesaTerminalDriver.o \ kernel/VesaTerminalDriver.o \
icxxabi.o \ icxxabi.o \
ubsan.o \
OBJS= \ OBJS= \
$(ARCHDIR)/crti.o \ $(ARCHDIR)/crti.o \

View File

@ -47,9 +47,9 @@ MMU::MMU()
m_highest_paging_struct[i] = (uint64_t)page_directory | Flags::Present; 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); 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(); uint64_t* page_table = allocate_page_aligned_page();
for (uint64_t j = 0; j < 512; j++) for (uint64_t j = 0; j < 512; j++)

View File

@ -118,9 +118,10 @@ enable_sse:
ret ret
initialize_paging: initialize_paging:
# identity map first 4 MiB # identity map first 6 MiB
movl $(0x00000000 + 0x83), boot_page_directory1 + 0 movl $(0x00000000 + 0x83), boot_page_directory1 + 0
movl $(0x00200000 + 0x83), boot_page_directory1 + 8 movl $(0x00200000 + 0x83), boot_page_directory1 + 8
movl $(0x00400000 + 0x83), boot_page_directory1 + 16
movl $(boot_page_directory1 + 0x01), boot_page_directory_pointer_table movl $(boot_page_directory1 + 0x01), boot_page_directory_pointer_table
# enable PAE # enable PAE

View File

@ -35,7 +35,7 @@ static uint64_t* allocate_page_aligned_page()
MMU::MMU() MMU::MMU()
{ {
// Identity map from 4 KiB -> 4 MiB // Identity map from 4 KiB -> 6 MiB
m_highest_paging_struct = allocate_page_aligned_page(); m_highest_paging_struct = allocate_page_aligned_page();
uint64_t* pdpt = allocate_page_aligned_page(); uint64_t* pdpt = allocate_page_aligned_page();
@ -44,7 +44,7 @@ MMU::MMU()
uint64_t* pd = allocate_page_aligned_page(); uint64_t* pd = allocate_page_aligned_page();
pdpt[0] = (uint64_t)pd | Flags::ReadWrite | Flags::Present; 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(); uint64_t* pt = allocate_page_aligned_page();
for (uint64_t j = 0; j < 512; j++) for (uint64_t j = 0; j < 512; j++)

View File

@ -41,9 +41,9 @@
# we will identity map first 4 MiB # we will identity map first 4 MiB
# 0 MiB -> 1 MiB: bootloader stuff # 0 MiB -> 1 MiB: bootloader stuff
# 1 MiB -> 2 MiB: kernel # 1 MiB -> 4 MiB: kernel
# 2 MiB -> 3 MiB: kmalloc # 4 MiB -> 5 MiB: kmalloc
# 3 MiB -> 4 MiB: kmalloc_fixed # 5 MiB -> 6 MiB: kmalloc_fixed
.align 4096 .align 4096
boot_pml4: boot_pml4:
.skip 512 * 8 .skip 512 * 8
@ -127,9 +127,10 @@ enable_sse:
ret ret
initialize_paging: 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 $(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 $(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 # set pdpte1 and pml4e1
movl $(boot_pd1 + PG_READ_WRITE + PG_PRESENT), boot_pdpt1 movl $(boot_pd1 + PG_READ_WRITE + PG_PRESENT), boot_pdpt1

View File

@ -65,7 +65,7 @@ private:
static_assert(sizeof(kmalloc_node) == s_kmalloc_min_align); static_assert(sizeof(kmalloc_node) == s_kmalloc_min_align);
struct kmalloc_info struct kmalloc_info
{ {
static constexpr uintptr_t base = 0x00200000; static constexpr uintptr_t base = 0x00400000;
static constexpr size_t size = 1 * MB; static constexpr size_t size = 1 * MB;
static constexpr uintptr_t end = base + size; 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) if (size == 0 || size >= info.size)
return nullptr; return nullptr;
ASSERT(align);
ASSERT(is_power_of_two(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 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()) if (void* result = kmalloc_fixed())
return result; return result;

108
kernel/ubsan.cpp Normal file
View File

@ -0,0 +1,108 @@
#include <kernel/Debug.h>
#include <kernel/Panic.h>
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);
}
}