diff options
| author | Aleksa Vuckovic <aleksav013@gmail.com> | 2022-11-25 23:44:40 +0100 |
|---|---|---|
| committer | Aleksa Vuckovic <aleksav013@gmail.com> | 2022-11-25 23:44:40 +0100 |
| commit | 5d56d1a5b4d52702eb4e4ea6f05e4b6eebf41ca8 (patch) | |
| tree | 1ddc17cf4524254edf322da50e5c12261715b648 | |
| parent | 3ec35b5abeb9765c419cf896338de7d95143bc9b (diff) | |
UBSAN - FINALLY FIXING UNDEFINED BEHAVIOUR
| -rw-r--r-- | Makefile | 5 | ||||
| -rw-r--r-- | kernel/Makefile | 2 | ||||
| -rw-r--r-- | kernel/include/libk/serial_stdio.h | 12 | ||||
| -rw-r--r-- | kernel/include/ubsan.h | 5 | ||||
| -rw-r--r-- | kernel/src/boot/multiboot2.c | 2 | ||||
| -rw-r--r-- | kernel/src/cpu/gdt.c | 2 | ||||
| -rw-r--r-- | kernel/src/libk/serial_stdio.c | 61 | ||||
| -rw-r--r-- | kernel/src/ubsan/ubsan.c | 387 |
8 files changed, 472 insertions, 4 deletions
@@ -7,8 +7,8 @@ LD = $(ARCH)ld OBJDUMP = $(ARCH)objcopy OBJCOPY = $(ARCH)objdump -W := -Wall -Werror -Wextra -pedantic -Wshadow -Wpointer-arith -Wcast-align -W += -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations +W := -Wall -Werror -Wextra -Wshadow -Wpointer-arith -Wcast-align +W += -Wwrite-strings W += -Wredundant-decls -Wnested-externs -Winline -Wno-long-long -Wconversion W += -Wstrict-prototypes WNO := -Wno-error=unused-parameter -Wno-error=unused-variable @@ -19,6 +19,7 @@ CFLAGS += -mgeneral-regs-only # disables SIMD instructions CFLAGS += -MD -O3 -ffreestanding -nostdlib CFLAGS += -fno-common -fno-stack-protector CFLAGS += -fno-pie -no-pie -fno-pic +CFLAGS += -g -fsanitize=undefined LDFLAGS = -z max-page-size=4096 MAKE:=$(MAKE) -s diff --git a/kernel/Makefile b/kernel/Makefile index 981af74..a8fd434 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -19,6 +19,7 @@ OBJS = \ src/fs/ext2.o \ src/libk/list.o \ src/libk/math.o \ + src/libk/serial_stdio.o \ src/libk/stdio.o \ src/libk/string.o \ src/main.o \ @@ -31,6 +32,7 @@ OBJS = \ src/sys/syscall.o \ src/sys/userspace_asm.o \ src/sys/userspace.o \ + src/ubsan/ubsan.o \ all: kernel.bin diff --git a/kernel/include/libk/serial_stdio.h b/kernel/include/libk/serial_stdio.h new file mode 100644 index 0000000..17317d0 --- /dev/null +++ b/kernel/include/libk/serial_stdio.h @@ -0,0 +1,12 @@ +#ifndef SERIAL_STDIO_H +#define SERIAL_STDIO_H + +#include <types.h> + +void serial_print_char(char c); +void serial_print_string(const char* s); +void serial_print_int(uint64_t num); +void serial_print_hex(uint64_t num); +void serial_printf(const char *s, ...); + +#endif diff --git a/kernel/include/ubsan.h b/kernel/include/ubsan.h new file mode 100644 index 0000000..733cca0 --- /dev/null +++ b/kernel/include/ubsan.h @@ -0,0 +1,5 @@ +#ifndef UBSAN_H +#define UBSAN_H + + +#endif diff --git a/kernel/src/boot/multiboot2.c b/kernel/src/boot/multiboot2.c index cdc0ae4..9410273 100644 --- a/kernel/src/boot/multiboot2.c +++ b/kernel/src/boot/multiboot2.c @@ -77,7 +77,7 @@ void read_mb2(mb2_tag_header* multiboot_bootinfo, uint32_t multiboot_magic) static mb2_tag_mmap* tag_mmap; // skip first 8 bytes (total_size + reserved) - mb2_tag_header* tag_header = (mb2_tag_header*)((char*)multiboot_bootinfo + 8 + KERNEL_VMA); + mb2_tag_header* tag_header = (mb2_tag_header*)((uint64_t)multiboot_bootinfo + 8 + KERNEL_VMA); while (tag_header->type != MB2_TAG_END) { // process tag_type diff --git a/kernel/src/cpu/gdt.c b/kernel/src/cpu/gdt.c index 743b629..fe7e080 100644 --- a/kernel/src/cpu/gdt.c +++ b/kernel/src/cpu/gdt.c @@ -12,7 +12,7 @@ void add_gdt_entry(uint32_t num, uint32_t offset, uint32_t limit, uint8_t access gdt[num].limit = limit & 0xffff; gdt[num].limitflags = (limit >> 16) & 0xf; gdt[num].access = access; - gdt[num].limitflags = flags << 4; + gdt[num].limitflags = (uint8_t)(flags << 4); } void add_gdt_tss(uint32_t num, uint64_t offset, uint32_t limit, uint8_t access, uint8_t flags) diff --git a/kernel/src/libk/serial_stdio.c b/kernel/src/libk/serial_stdio.c new file mode 100644 index 0000000..a56750b --- /dev/null +++ b/kernel/src/libk/serial_stdio.c @@ -0,0 +1,61 @@ +#include <types.h> +#include <libk/serial_stdio.h> +#include <stdarg.h> +#include <libk/string.h> +#include <graphics.h> +#include <serial.h> + +void serial_print_char(char c) +{ + write_serial(c); +} + +void serial_print_string(const char* s) +{ + for (size_t i = 0; i < strlen(s); i++) { + serial_print_char(s[i]); + } +} + +void serial_print_int(uint64_t num) +{ + char a[100]; + itos(num, a); + serial_print_string(a); +} + +void serial_print_hex(uint64_t num) +{ + char a[100]; + itoh(num, a); + serial_print_string(a); +} + +void serial_printf(const char *s, ...) +{ + size_t count = 0; + for(size_t i = 0; i < strlen(s); i++) if(s[i] == '%') count++; + + va_list list; + va_start(list, s); + + for(size_t i = 0; i < strlen(s); i++) + { + if(s[i] == '%') + { + i++; + if(s[i] == 'c') serial_print_char((char)va_arg(list, uint32_t)); + else if(s[i] == 's') serial_print_string(va_arg(list, char*)); + else if(s[i] == 'd') serial_print_int((uint64_t)va_arg(list, uint64_t)); + else if(s[i] == 'x') serial_print_hex((uint64_t)va_arg(list, uint64_t)); + else + { + serial_print_string("Wrong format using printf\n"); + return; + } + } + else serial_print_char(s[i]); + } + + va_end(list); +} diff --git a/kernel/src/ubsan/ubsan.c b/kernel/src/ubsan/ubsan.c new file mode 100644 index 0000000..84283f7 --- /dev/null +++ b/kernel/src/ubsan/ubsan.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2014, 2015 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * ubsan/ubsan.c + * Undefined behavior sanitizer runtime support. + */ + +#include <stdint.h> +#include <libk/stdio.h> +#include <libk/serial_stdio.h> +#include <ubsan.h> + +struct ubsan_source_location +{ + const char* filename; + uint32_t line; + uint32_t column; +}; + +struct ubsan_type_descriptor +{ + uint16_t type_kind; + uint16_t type_info; + char type_name[]; +}; + +typedef uintptr_t ubsan_value_handle_t; + +static const struct ubsan_source_location unknown_location = +{ + "<unknown file>", + 0, + 0, +}; + +__attribute__((noreturn)) +static void ubsan_abort(const struct ubsan_source_location* location, + const char* violation) +{ + if ( !location || !location->filename ) + location = &unknown_location; + + serial_printf( + "filename = %s; line = %d; column = %d; violation = %s;\n", + location->filename, location->line, location->column, violation); + while (true) { + + } +} + +#define ABORT_VARIANT(name, params, call) \ +__attribute__((noreturn)) \ +void __ubsan_handle_##name##_abort params \ +{ \ + __ubsan_handle_##name call; \ + __builtin_unreachable(); \ +} + +#define ABORT_VARIANT_VP(name) \ +ABORT_VARIANT(name, (void* a), (a)) +#define ABORT_VARIANT_VP_VP(name) \ +ABORT_VARIANT(name, (void* a, void* b), (a, b)) +#define ABORT_VARIANT_VP_IP(name) \ +ABORT_VARIANT(name, (void* a, intptr_t b), (a, b)) +#define ABORT_VARIANT_VP_VP_VP(name) \ +ABORT_VARIANT(name, (void* a, void* b, void* c), (a, b, c)) + +struct ubsan_type_mismatch_v1_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; + uintptr_t alignment; + unsigned char type_check_kind; +}; + +void __ubsan_handle_type_mismatch_v1(void* data_raw, + void* pointer_raw) +{ + struct ubsan_type_mismatch_v1_data* data = + (struct ubsan_type_mismatch_v1_data*) data_raw; + ubsan_value_handle_t pointer = (ubsan_value_handle_t) pointer_raw; + const char* violation = "type mismatch"; + if ( !pointer ) + violation = "null pointer access"; + else if ( data->alignment && (pointer & (data->alignment - 1)) ) + violation = "unaligned access"; + ubsan_abort(&data->location, violation); +} + +ABORT_VARIANT_VP_VP(type_mismatch_v1); + +struct ubsan_overflow_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; +}; + +void __ubsan_handle_pointer_overflow(void* data_raw, + void* lhs_raw, + void* rhs_raw) +{ + struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw; + ubsan_value_handle_t lhs = (ubsan_value_handle_t) lhs_raw; + ubsan_value_handle_t rhs = (ubsan_value_handle_t) rhs_raw; + (void) lhs; + (void) rhs; + ubsan_abort(&data->location, "pointer overflow"); +} + +ABORT_VARIANT_VP_VP_VP(pointer_overflow); + +void __ubsan_handle_add_overflow(void* data_raw, + void* lhs_raw, + void* rhs_raw) +{ + struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw; + ubsan_value_handle_t lhs = (ubsan_value_handle_t) lhs_raw; + ubsan_value_handle_t rhs = (ubsan_value_handle_t) rhs_raw; + (void) lhs; + (void) rhs; + ubsan_abort(&data->location, "addition overflow"); +} + +ABORT_VARIANT_VP_VP_VP(add_overflow); + +void __ubsan_handle_sub_overflow(void* data_raw, + void* lhs_raw, + void* rhs_raw) +{ + struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw; + ubsan_value_handle_t lhs = (ubsan_value_handle_t) lhs_raw; + ubsan_value_handle_t rhs = (ubsan_value_handle_t) rhs_raw; + (void) lhs; + (void) rhs; + ubsan_abort(&data->location, "subtraction overflow"); +} + +ABORT_VARIANT_VP_VP_VP(sub_overflow); + +void __ubsan_handle_mul_overflow(void* data_raw, + void* lhs_raw, + void* rhs_raw) +{ + struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw; + ubsan_value_handle_t lhs = (ubsan_value_handle_t) lhs_raw; + ubsan_value_handle_t rhs = (ubsan_value_handle_t) rhs_raw; + (void) lhs; + (void) rhs; + ubsan_abort(&data->location, "multiplication overflow"); +} + +ABORT_VARIANT_VP_VP_VP(mul_overflow); + +void __ubsan_handle_negate_overflow(void* data_raw, + void* old_value_raw) +{ + struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw; + ubsan_value_handle_t old_value = (ubsan_value_handle_t) old_value_raw; + (void) old_value; + ubsan_abort(&data->location, "negation overflow"); +} + +ABORT_VARIANT_VP_VP(negate_overflow); + +void __ubsan_handle_divrem_overflow(void* data_raw, + void* lhs_raw, + void* rhs_raw) +{ + struct ubsan_overflow_data* data = (struct ubsan_overflow_data*) data_raw; + ubsan_value_handle_t lhs = (ubsan_value_handle_t) lhs_raw; + ubsan_value_handle_t rhs = (ubsan_value_handle_t) rhs_raw; + (void) lhs; + (void) rhs; + ubsan_abort(&data->location, "division remainder overflow"); +} + +ABORT_VARIANT_VP_VP_VP(divrem_overflow); + +struct ubsan_shift_out_of_bounds_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* lhs_type; + struct ubsan_type_descriptor* rhs_type; +}; + +void __ubsan_handle_shift_out_of_bounds(void* data_raw, + void* lhs_raw, + void* rhs_raw) +{ + struct ubsan_shift_out_of_bounds_data* data = + (struct ubsan_shift_out_of_bounds_data*) data_raw; + ubsan_value_handle_t lhs = (ubsan_value_handle_t) lhs_raw; + ubsan_value_handle_t rhs = (ubsan_value_handle_t) rhs_raw; + (void) lhs; + (void) rhs; + ubsan_abort(&data->location, "shift out of bounds"); +} + +ABORT_VARIANT_VP_VP_VP(shift_out_of_bounds); + +struct ubsan_out_of_bounds_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* array_type; + struct ubsan_type_descriptor* index_type; +}; + +void __ubsan_handle_out_of_bounds(void* data_raw, + void* index_raw) +{ + struct ubsan_out_of_bounds_data* data = + (struct ubsan_out_of_bounds_data*) data_raw; + ubsan_value_handle_t index = (ubsan_value_handle_t) index_raw; + (void) index; + ubsan_abort(&data->location, "out of bounds"); +} + +ABORT_VARIANT_VP_VP(out_of_bounds); + +struct ubsan_unreachable_data +{ + struct ubsan_source_location location; +}; + +__attribute__((noreturn)) +void __ubsan_handle_builtin_unreachable(void* data_raw) +{ + struct ubsan_unreachable_data* data = + (struct ubsan_unreachable_data*) data_raw; + ubsan_abort(&data->location, "reached unreachable"); +} + +__attribute__((noreturn)) +void __ubsan_handle_missing_return(void* data_raw) +{ + struct ubsan_unreachable_data* data = + (struct ubsan_unreachable_data*) data_raw; + ubsan_abort(&data->location, "missing return"); +} + +struct ubsan_vla_bound_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; +}; + +void __ubsan_handle_vla_bound_not_positive(void* data_raw, + void* bound_raw) +{ + struct ubsan_vla_bound_data* data = (struct ubsan_vla_bound_data*) data_raw; + ubsan_value_handle_t bound = (ubsan_value_handle_t) bound_raw; + (void) bound; + ubsan_abort(&data->location, "negative variable array length"); +} + +ABORT_VARIANT_VP_VP(vla_bound_not_positive); + +struct ubsan_float_cast_overflow_data +{ + // TODO: Remove this GCC 5.x compatibility after switching to GCC 6.x. The + // GCC developers accidentally forgot the source location. Their + // libubsan probes to see if it looks like a path, but we don't need + // to maintain compatibility with multiple gcc releases. See below. +#if !(defined(__GNUC__) && __GNUC__< 6) + struct ubsan_source_location location; +#endif + struct ubsan_type_descriptor* from_type; + struct ubsan_type_descriptor* to_type; +}; + +void __ubsan_handle_float_cast_overflow(void* data_raw, + void* from_raw) +{ + struct ubsan_float_cast_overflow_data* data = + (struct ubsan_float_cast_overflow_data*) data_raw; + ubsan_value_handle_t from = (ubsan_value_handle_t) from_raw; + (void) from; +#if !(defined(__GNUC__) && __GNUC__< 6) + ubsan_abort(&data->location, "float cast overflow"); +#else + ubsan_abort(((void) data, &unknown_location), "float cast overflow"); +#endif +} + +ABORT_VARIANT_VP_VP(float_cast_overflow); + +struct ubsan_invalid_value_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; +}; + +void __ubsan_handle_load_invalid_value(void* data_raw, + void* value_raw) +{ + struct ubsan_invalid_value_data* data = + (struct ubsan_invalid_value_data*) data_raw; + ubsan_value_handle_t value = (ubsan_value_handle_t) value_raw; + (void) value; + ubsan_abort(&data->location, "invalid value load"); +} + +ABORT_VARIANT_VP_VP(load_invalid_value); + +struct ubsan_function_type_mismatch_v1_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; +}; + +void __ubsan_handle_function_type_mismatch_v1(void* data_raw, + void* value_raw) +{ + struct ubsan_function_type_mismatch_v1_data* data = + (struct ubsan_function_type_mismatch_v1_data*) data_raw; + ubsan_value_handle_t value = (ubsan_value_handle_t) value_raw; + (void) value; + ubsan_abort(&data->location, "function type mismatch"); +} + +ABORT_VARIANT_VP_VP(function_type_mismatch_v1); + +struct ubsan_nonnull_return_data +{ + struct ubsan_source_location location; + struct ubsan_source_location attr_location; +}; + +void __ubsan_handle_nonnull_return(void* data_raw) +{ + struct ubsan_nonnull_return_data* data = + (struct ubsan_nonnull_return_data*) data_raw; + ubsan_abort(&data->location, "null return"); +} + +ABORT_VARIANT_VP(nonnull_return); + +struct ubsan_nonnull_arg_data +{ + struct ubsan_source_location location; + struct ubsan_source_location attr_location; +}; + +// TODO: GCC's libubsan does not have the second parameter, but its builtin +// somehow has it and conflict if we don't match it. +void __ubsan_handle_nonnull_arg(void* data_raw, + intptr_t index_raw) +{ + struct ubsan_nonnull_arg_data* data = + (struct ubsan_nonnull_arg_data*) data_raw; + ubsan_value_handle_t index = (ubsan_value_handle_t) index_raw; + (void) index; + ubsan_abort(&data->location, "null argument"); +} + +ABORT_VARIANT_VP_IP(nonnull_arg); + +struct ubsan_cfi_bad_icall_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; +}; + +void __ubsan_handle_cfi_bad_icall(void* data_raw, + void* value_raw) +{ + struct ubsan_cfi_bad_icall_data* data = + (struct ubsan_cfi_bad_icall_data*) data_raw; + ubsan_value_handle_t value = (ubsan_value_handle_t) value_raw; + (void) value; + ubsan_abort(&data->location, + "control flow integrity check failure during indirect call"); +} + +ABORT_VARIANT_VP_VP(cfi_bad_icall); |
