From d82b3f809ba99c92a84e363c5f0930b699ef0189 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 25 Feb 2023 17:37:38 +0100 Subject: [PATCH] luna: Add UB sanitizer, for both kernel-space and userspace --- luna/CMakeLists.txt | 1 + luna/include/luna/UBSAN.h | 74 +++++++++++++++++++++++ luna/src/UBSAN.cpp | 121 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 luna/include/luna/UBSAN.h create mode 100644 luna/src/UBSAN.cpp diff --git a/luna/CMakeLists.txt b/luna/CMakeLists.txt index 652c341c..b3eba9a3 100644 --- a/luna/CMakeLists.txt +++ b/luna/CMakeLists.txt @@ -17,6 +17,7 @@ set(FREESTANDING_SOURCES src/DebugLog.cpp src/Heap.cpp src/Spinlock.cpp + src/UBSAN.cpp ) set(SOURCES diff --git a/luna/include/luna/UBSAN.h b/luna/include/luna/UBSAN.h new file mode 100644 index 00000000..2e9202f5 --- /dev/null +++ b/luna/include/luna/UBSAN.h @@ -0,0 +1,74 @@ +#pragma once +#include + +namespace UBSAN +{ + struct SourceLocation + { + const char* file; + u32 line; + u32 column; + }; + + struct TypeDescriptor + { + u16 kind; + u16 info; + char name[]; + }; + + namespace UBInfo + { + struct TypeMismatchInfo + { + SourceLocation location; + TypeDescriptor* type; + usize alignment; + u8 type_check_kind; + }; + + struct TypeMismatchInfo_v1 + { + SourceLocation location; + TypeDescriptor* type; + u8 log_alignment; + u8 type_check_kind; + }; + + struct OverflowInfo + { + SourceLocation location; + TypeDescriptor* type; + }; + + struct UnreachableInfo + { + SourceLocation location; + }; + + struct OutOfBoundsInfo + { + SourceLocation location; + TypeDescriptor* array_type; + TypeDescriptor* index_type; + }; + + struct InvalidValueInfo + { + SourceLocation location; + TypeDescriptor* type; + }; + + struct ShiftOutOfBoundsInfo + { + SourceLocation location; + TypeDescriptor* lhs_type; + TypeDescriptor* rhs_type; + }; + + struct PointerOverflowInfo + { + SourceLocation location; + }; + } +} diff --git a/luna/src/UBSAN.cpp b/luna/src/UBSAN.cpp new file mode 100644 index 00000000..15e52676 --- /dev/null +++ b/luna/src/UBSAN.cpp @@ -0,0 +1,121 @@ +#include +#include +#include +#include + +using namespace UBSAN::UBInfo; + +[[noreturn]] void ub_panic(SourceLocation caller = SourceLocation::current()) +{ + expect_at(false, caller, "Undefined behavior"); +} + +#define DISPLAY(loc) loc.file, loc.line, loc.column + +extern "C" +{ + void __ubsan_handle_builtin_unreachable(UnreachableInfo* info) + { + const auto& location = info->location; + dbgln("ubsan: __builtin_unreachable reached at %s:%d:%d", DISPLAY(location)); + ub_panic(); + } + + void __ubsan_handle_pointer_overflow(PointerOverflowInfo* info) + { + const auto& location = info->location; + dbgln("ubsan: pointer overflow occurred at %s:%d:%d", DISPLAY(location)); + ub_panic(); + } + + void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsInfo* info) + { + const auto& location = info->location; + dbgln("ubsan: shift out of bounds for type %s at %s:%d:%d", info->lhs_type->name, DISPLAY(location)); + ub_panic(); + } + + void __ubsan_handle_load_invalid_value(InvalidValueInfo* info) + { + const auto& location = info->location; + dbgln("ubsan: load invalid value for type %s at %s:%d:%d", info->type->name, DISPLAY(location)); + ub_panic(); + } + + void __ubsan_handle_out_of_bounds(OutOfBoundsInfo* info, usize index) + { + const auto& location = info->location; + dbgln("ubsan: out of bounds array (of type %s) access (index %zu of type %s) at %s:%d:%d", + info->array_type->name, index, info->index_type->name, DISPLAY(location)); + ub_panic(); + } + + void ubsan_handle_generic_overflow(OverflowInfo* info, const char* overflow_type) + { + const auto& location = info->location; + dbgln("ubsan: %s overflow (value cannot fit into type %s) at %s:%d:%d", overflow_type, info->type->name, + DISPLAY(location)); + ub_panic(); + } + +#define UBSAN_OVERFLOW_BINARY(operation) \ + void __ubsan_handle_##operation##_overflow(OverflowInfo* info, usize, usize) \ + { \ + ubsan_handle_generic_overflow(info, #operation); \ + } + +#define UBSAN_OVERFLOW_UNARY(operation) \ + void __ubsan_handle_##operation##_overflow(OverflowInfo* info, usize) \ + { \ + ubsan_handle_generic_overflow(info, #operation); \ + } + + UBSAN_OVERFLOW_BINARY(add); + UBSAN_OVERFLOW_BINARY(sub); + UBSAN_OVERFLOW_BINARY(mul); + UBSAN_OVERFLOW_UNARY(negate); + UBSAN_OVERFLOW_BINARY(divrem); + +#define is_aligned(value, alignment) !(value & (alignment - 1)) + + const char* g_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", + }; + + void __ubsan_handle_type_mismatch(TypeMismatchInfo* info, usize pointer) + { + const auto& location = info->location; + + if (pointer == 0) { dbgln("ubsan: null pointer access at %s:%d:%d", DISPLAY(location)); } + else if (info->alignment != 0 && is_aligned(pointer, info->alignment)) + { + dbgln("ubsan: unaligned pointer access (address %p) at %s:%d:%d", (void*)pointer, DISPLAY(location)); + } + else + { + dbgln("ubsan: %s address %p with insufficient space for object of type %s at %s:%d:%d", + g_type_check_kinds[info->type_check_kind], (void*)pointer, info->type->name, DISPLAY(location)); + } + ub_panic(); + } + + void __ubsan_handle_type_mismatch_v1(TypeMismatchInfo_v1* v1_info, usize pointer) + { + TypeMismatchInfo info = { + .location = v1_info->location, + .type = v1_info->type, + .alignment = 1UL << v1_info->log_alignment, + .type_check_kind = v1_info->type_check_kind, + }; + __ubsan_handle_type_mismatch(&info, pointer); + } +}