From 9b6d60d9d23471e3293f1e65629170eca4cc4740 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Mon, 24 Nov 2014 17:08:58 +0100 Subject: [PATCH] Add undefined behavior sanitizer support. --- libc/Makefile | 1 + libc/ubsan/ubsan.cpp | 413 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 414 insertions(+) create mode 100644 libc/ubsan/ubsan.cpp diff --git a/libc/Makefile b/libc/Makefile index 53ccb5d4..206b830f 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -243,6 +243,7 @@ timespec/timespec.o \ time/strftime_l.o \ time/strftime.o \ time/timegm.o \ +ubsan/ubsan.o \ wchar/btowc.o \ wchar/mbrlen.o \ wchar/mbrtowc.o \ diff --git a/libc/ubsan/ubsan.cpp b/libc/ubsan/ubsan.cpp new file mode 100644 index 00000000..34e9d4f6 --- /dev/null +++ b/libc/ubsan/ubsan.cpp @@ -0,0 +1,413 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2014, 2015. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + ubsan/ubsan.cpp + Undefined behavior sanitizer runtime support. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__is_sortix_kernel) +#include +#include +#endif + +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 = +{ + "", + 0, + 0, +}; + +__attribute__((noreturn)) +static void ubsan_abort(const struct ubsan_source_location* location, + const char* violation) +{ + if ( !location || !location->filename ) + location = &unknown_location; +#if __STDC_HOSTED__ + struct scram_undefined_behavior info; + info.filename = location->filename; + info.line = location->line; + info.column = location->column; + info.violation = violation; + scram(SCRAM_UNDEFINED_BEHAVIOR, &info); +#elif defined(__is_sortix_kernel) + Sortix::PanicF("Undefined behavior: %s at %s:%u:%u", + violation, + location->filename, + location->line, + location->column); +#else + #warning "You need to implement a undefined-behavior reporting mechanism" + (void) location; + (void) violation; + abort(); +#endif +} + +#define ABORT_VARIANT(name, params, call) \ +extern "C" __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_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; + uintptr_t alignment; + unsigned char type_check_kind; +}; + +extern "C" +void __ubsan_handle_type_mismatch(void* data_raw, + void* pointer_raw) +{ + struct ubsan_type_mismatch_data* data = + (struct ubsan_type_mismatch_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); + +struct ubsan_overflow_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; +}; + +extern "C" +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); + +extern "C" +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); + +extern "C" +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); + +extern "C" +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); + +extern "C" +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; +}; + +extern "C" +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; +}; + +extern "C" +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; +}; + +extern "C" __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"); +} + +extern "C" __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; +}; + +extern "C" +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; +}; + +extern "C" +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; +}; + +extern "C" +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_data +{ + struct ubsan_source_location location; + struct ubsan_type_descriptor* type; +}; + +extern "C" +void __ubsan_handle_function_type_mismatch(void* data_raw, + void* value_raw) +{ + struct ubsan_function_type_mismatch_data* data = + (struct ubsan_function_type_mismatch_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); + +struct ubsan_nonnull_return_data +{ + struct ubsan_source_location location; + struct ubsan_source_location attr_location; +}; + +extern "C" +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. +extern "C" +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; +}; + +extern "C" +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);