diff --git a/Userland/Libraries/LibSanitizer/UBSanitizer.cpp b/Userland/Libraries/LibSanitizer/UBSanitizer.cpp index aabc809ff7..d4caf0a88a 100644 --- a/Userland/Libraries/LibSanitizer/UBSanitizer.cpp +++ b/Userland/Libraries/LibSanitizer/UBSanitizer.cpp @@ -21,8 +21,32 @@ Atomic AK::UBSanitizer::g_ubsan_is_deadly; abort(); \ } while (0) +// FIXME: Dump backtrace of this process (with symbols? without symbols?) in case the user wants non-deadly UBSAN +// Should probably go through the kernel for SC_dump_backtrace, then access the loader's symbol tables +// rather than going through the symbolizer service? +#define ABORT_IF_DEADLY() \ + do { \ + if (g_ubsan_is_deadly.load(AK::MemoryOrder::memory_order_acquire)) { \ + WARNLN_AND_DBGLN("UBSAN: UB is configured to be deadly"); \ + abort(); \ + } \ + } while (0) + extern "C" { +[[gnu::constructor]] static void init_ubsan_options() +{ + auto const* options_ptr = getenv("UBSAN_OPTIONS"); + auto options = options_ptr != NULL ? StringView { options_ptr, strlen(options_ptr) } : StringView {}; + // FIXME: Parse more options and complain about invalid options + if (!options.is_null()) { + if (options.contains("halt_on_error=1"sv)) + g_ubsan_is_deadly.store(true, AK::MemoryOrder::memory_order_relaxed); + else if (options.contains("halt_on_error=0"sv)) + g_ubsan_is_deadly.store(false, AK::MemoryOrder::memory_order_relaxed); + } +} + static void print_location(SourceLocation const& location) { if (!location.filename()) { @@ -30,35 +54,13 @@ static void print_location(SourceLocation const& location) } else { WARNLN_AND_DBGLN("UBSAN: at {}, line {}, column: {}", location.filename(), location.line(), location.column()); } - // FIXME: Dump backtrace of this process (with symbols? without symbols?) in case the user wants non-deadly UBSAN - // Should probably go through the kernel for SC_dump_backtrace, then access the loader's symbol tables rather than - // going through the symbolizer service? - - static bool checked_env_for_deadly = false; - if (!checked_env_for_deadly) { - checked_env_for_deadly = true; - auto const* options_ptr = getenv("UBSAN_OPTIONS"); - auto options = options_ptr != NULL ? StringView { options_ptr, strlen(options_ptr) } : StringView {}; - // FIXME: Parse more options and complain about invalid options - if (!options.is_null()) { - if (options.contains("halt_on_error=1"sv)) - g_ubsan_is_deadly = true; - else if (options.contains("halt_on_error=0"sv)) - g_ubsan_is_deadly = false; - } - } - if (g_ubsan_is_deadly) { - WARNLN_AND_DBGLN("UB is configured to be deadly"); - VERIFY_NOT_REACHED(); - } } // Calls to these functions are automatically inserted by the compiler, // so there is no point in declaring them in a header. #pragma GCC diagnostic ignored "-Wmissing-declarations" -void __ubsan_handle_load_invalid_value(InvalidValueData&, ValueHandle) __attribute__((used)); -void __ubsan_handle_load_invalid_value(InvalidValueData& data, ValueHandle) +static void handle_load_invalid_value(InvalidValueData& data, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -66,9 +68,18 @@ void __ubsan_handle_load_invalid_value(InvalidValueData& data, ValueHandle) WARNLN_AND_DBGLN("UBSAN: load-invalid-value: {} ({}-bit)", data.type.name(), data.type.bit_width()); print_location(location); } +[[gnu::used]] void __ubsan_handle_load_invalid_value(InvalidValueData& data, ValueHandle handle) +{ + handle_load_invalid_value(data, handle); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_load_invalid_value_abort(InvalidValueData& data, ValueHandle handle) +{ + handle_load_invalid_value(data, handle); + ABORT_ALWAYS(); +} -void __ubsan_handle_nonnull_arg(NonnullArgData&) __attribute__((used)); -void __ubsan_handle_nonnull_arg(NonnullArgData& data) +static void handle_nonnull_arg(NonnullArgData& data) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -77,8 +88,18 @@ void __ubsan_handle_nonnull_arg(NonnullArgData& data) print_location(location); } -void __ubsan_handle_nullability_arg(NonnullArgData&) __attribute__((used)); -void __ubsan_handle_nullability_arg(NonnullArgData& data) +[[gnu::used]] void __ubsan_handle_nonnull_arg(NonnullArgData& data) +{ + handle_nonnull_arg(data); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_nonnull_arg_abort(NonnullArgData& data) +{ + handle_nonnull_arg(data); + ABORT_ALWAYS(); +} + +static void handle_nullability_arg(NonnullArgData& data) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -86,9 +107,18 @@ void __ubsan_handle_nullability_arg(NonnullArgData& data) WARNLN_AND_DBGLN("UBSAN: null pointer passed as argument {}, which is declared to never be null", data.argument_index); print_location(location); } +[[gnu::used]] void __ubsan_handle_nullability_arg(NonnullArgData& data) +{ + handle_nullability_arg(data); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_nullability_arg_abort(NonnullArgData& data) +{ + handle_nullability_arg(data); + ABORT_ALWAYS(); +} -void __ubsan_handle_nonnull_return_v1(NonnullReturnData const&, SourceLocation&) __attribute__((used)); -void __ubsan_handle_nonnull_return_v1(NonnullReturnData const&, SourceLocation& location) +static void handle_nonnull_return_v1(NonnullReturnData const&, SourceLocation& location) { auto loc = location.permanently_clear(); if (!loc.needs_logging()) @@ -96,9 +126,18 @@ void __ubsan_handle_nonnull_return_v1(NonnullReturnData const&, SourceLocation& WARNLN_AND_DBGLN("UBSAN: null pointer return from function declared to never return null"); print_location(loc); } +[[gnu::used]] void __ubsan_handle_nonnull_return_v1(NonnullReturnData const& data, SourceLocation& location) +{ + handle_nonnull_return_v1(data, location); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_nonnull_return_v1_abort(NonnullReturnData const& data, SourceLocation& location) +{ + handle_nonnull_return_v1(data, location); + ABORT_ALWAYS(); +} -void __ubsan_handle_nullability_return_v1(NonnullReturnData const& data, SourceLocation& location) __attribute__((used)); -void __ubsan_handle_nullability_return_v1(NonnullReturnData const&, SourceLocation& location) +static void handle_nullability_return_v1(NonnullReturnData const&, SourceLocation& location) { auto loc = location.permanently_clear(); if (!loc.needs_logging()) @@ -106,9 +145,18 @@ void __ubsan_handle_nullability_return_v1(NonnullReturnData const&, SourceLocati WARNLN_AND_DBGLN("UBSAN: null pointer return from function declared to never return null"); print_location(loc); } +[[gnu::used]] void __ubsan_handle_nullability_return_v1(NonnullReturnData const& data, SourceLocation& location) +{ + handle_nullability_return_v1(data, location); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_nullability_return_v1_abort(NonnullReturnData const& data, SourceLocation& location) +{ + handle_nullability_return_v1(data, location); + ABORT_ALWAYS(); +} -void __ubsan_handle_vla_bound_not_positive(VLABoundData&, ValueHandle) __attribute__((used)); -void __ubsan_handle_vla_bound_not_positive(VLABoundData& data, ValueHandle) +static void handle_vla_bound_not_positive(VLABoundData& data, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -116,9 +164,18 @@ void __ubsan_handle_vla_bound_not_positive(VLABoundData& data, ValueHandle) WARNLN_AND_DBGLN("UBSAN: VLA bound not positive {} ({}-bit)", data.type.name(), data.type.bit_width()); print_location(location); } +[[gnu::used]] void __ubsan_handle_vla_bound_not_positive(VLABoundData& data, ValueHandle handle) +{ + handle_vla_bound_not_positive(data, handle); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_vla_bound_not_positive_abort(VLABoundData& data, ValueHandle handle) +{ + handle_vla_bound_not_positive(data, handle); + ABORT_ALWAYS(); +} -void __ubsan_handle_add_overflow(OverflowData&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_add_overflow(OverflowData& data, ValueHandle, ValueHandle) +static void handle_add_overflow(OverflowData& data, ValueHandle, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -126,9 +183,18 @@ void __ubsan_handle_add_overflow(OverflowData& data, ValueHandle, ValueHandle) WARNLN_AND_DBGLN("UBSAN: addition overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); print_location(location); } +[[gnu::used]] void __ubsan_handle_add_overflow(OverflowData& data, ValueHandle lhs, ValueHandle rhs) +{ + handle_add_overflow(data, lhs, rhs); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_add_overflow_abort(OverflowData& data, ValueHandle lhs, ValueHandle rhs) +{ + handle_add_overflow(data, lhs, rhs); + ABORT_ALWAYS(); +} -void __ubsan_handle_sub_overflow(OverflowData&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_sub_overflow(OverflowData& data, ValueHandle, ValueHandle) +static void handle_sub_overflow(OverflowData& data, ValueHandle, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -136,9 +202,18 @@ void __ubsan_handle_sub_overflow(OverflowData& data, ValueHandle, ValueHandle) WARNLN_AND_DBGLN("UBSAN: subtraction overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); print_location(location); } +[[gnu::used]] void __ubsan_handle_sub_overflow(OverflowData& data, ValueHandle lhs, ValueHandle rhs) +{ + handle_sub_overflow(data, lhs, rhs); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_sub_overflow_abort(OverflowData& data, ValueHandle lhs, ValueHandle rhs) +{ + handle_sub_overflow(data, lhs, rhs); + ABORT_ALWAYS(); +} -void __ubsan_handle_negate_overflow(OverflowData&, ValueHandle) __attribute__((used)); -void __ubsan_handle_negate_overflow(OverflowData& data, ValueHandle) +static void handle_negate_overflow(OverflowData& data, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -146,9 +221,18 @@ void __ubsan_handle_negate_overflow(OverflowData& data, ValueHandle) WARNLN_AND_DBGLN("UBSAN: negation overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); print_location(location); } +[[gnu::used]] void __ubsan_handle_negate_overflow(OverflowData& data, ValueHandle value) +{ + handle_negate_overflow(data, value); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_negate_overflow_abort(OverflowData& data, ValueHandle value) +{ + handle_negate_overflow(data, value); + ABORT_ALWAYS(); +} -void __ubsan_handle_mul_overflow(OverflowData&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_mul_overflow(OverflowData& data, ValueHandle, ValueHandle) +static void handle_mul_overflow(OverflowData& data, ValueHandle, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -156,9 +240,18 @@ void __ubsan_handle_mul_overflow(OverflowData& data, ValueHandle, ValueHandle) WARNLN_AND_DBGLN("UBSAN: multiplication overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); print_location(location); } +[[gnu::used]] void __ubsan_handle_mul_overflow(OverflowData& data, ValueHandle lhs, ValueHandle rhs) +{ + handle_mul_overflow(data, lhs, rhs); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_mul_overflow_abort(OverflowData& data, ValueHandle lhs, ValueHandle rhs) +{ + handle_mul_overflow(data, lhs, rhs); + ABORT_ALWAYS(); +} -void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData& data, ValueHandle, ValueHandle) +static void handle_shift_out_of_bounds(ShiftOutOfBoundsData& data, ValueHandle, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -166,9 +259,18 @@ void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData& data, ValueHandle, WARNLN_AND_DBGLN("UBSAN: shift out of bounds, {} ({}-bit) shifted by {} ({}-bit)", data.lhs_type.name(), data.lhs_type.bit_width(), data.rhs_type.name(), data.rhs_type.bit_width()); print_location(location); } +[[gnu::used]] void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData& data, ValueHandle lhs, ValueHandle rhs) +{ + handle_shift_out_of_bounds(data, lhs, rhs); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_shift_out_of_bounds_abort(ShiftOutOfBoundsData& data, ValueHandle lhs, ValueHandle rhs) +{ + handle_shift_out_of_bounds(data, lhs, rhs); + ABORT_ALWAYS(); +} -void __ubsan_handle_divrem_overflow(OverflowData&, ValueHandle lhs, ValueHandle rhs) __attribute__((used)); -void __ubsan_handle_divrem_overflow(OverflowData& data, ValueHandle, ValueHandle) +static void handle_divrem_overflow(OverflowData& data, ValueHandle, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -176,9 +278,18 @@ void __ubsan_handle_divrem_overflow(OverflowData& data, ValueHandle, ValueHandle WARNLN_AND_DBGLN("UBSAN: divrem overflow, {} ({}-bit)", data.type.name(), data.type.bit_width()); print_location(location); } +[[gnu::used]] void __ubsan_handle_divrem_overflow(OverflowData& data, ValueHandle lhs, ValueHandle rhs) +{ + handle_divrem_overflow(data, lhs, rhs); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_divrem_overflow_abort(OverflowData& data, ValueHandle lhs, ValueHandle rhs) +{ + handle_divrem_overflow(data, lhs, rhs); + ABORT_ALWAYS(); +} -void __ubsan_handle_out_of_bounds(OutOfBoundsData&, ValueHandle) __attribute__((used)); -void __ubsan_handle_out_of_bounds(OutOfBoundsData& data, ValueHandle) +static void handle_out_of_bounds(OutOfBoundsData& data, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -186,9 +297,18 @@ void __ubsan_handle_out_of_bounds(OutOfBoundsData& data, ValueHandle) WARNLN_AND_DBGLN("UBSAN: out of bounds access into array of {} ({}-bit), index type {} ({}-bit)", data.array_type.name(), data.array_type.bit_width(), data.index_type.name(), data.index_type.bit_width()); print_location(location); } +[[gnu::used]] void __ubsan_handle_out_of_bounds(OutOfBoundsData& data, ValueHandle index) +{ + handle_out_of_bounds(data, index); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_out_of_bounds_abort(OutOfBoundsData& data, ValueHandle index) +{ + handle_out_of_bounds(data, index); + ABORT_ALWAYS(); +} -void __ubsan_handle_type_mismatch_v1(TypeMismatchData&, ValueHandle) __attribute__((used)); -void __ubsan_handle_type_mismatch_v1(TypeMismatchData& data, ValueHandle ptr) +static void handle_type_mismatch_v1(TypeMismatchData& data, ValueHandle ptr) { constexpr StringView kinds[] = { "load of"sv, @@ -222,9 +342,18 @@ void __ubsan_handle_type_mismatch_v1(TypeMismatchData& data, ValueHandle ptr) print_location(location); } +[[gnu::used]] void __ubsan_handle_type_mismatch_v1(TypeMismatchData& data, ValueHandle ptr) +{ + handle_type_mismatch_v1(data, ptr); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_type_mismatch_v1_abort(TypeMismatchData& data, ValueHandle ptr) +{ + handle_type_mismatch_v1(data, ptr); + ABORT_ALWAYS(); +} -void __ubsan_handle_alignment_assumption(AlignmentAssumptionData&, ValueHandle, ValueHandle, ValueHandle) __attribute__((used)); -void __ubsan_handle_alignment_assumption(AlignmentAssumptionData& data, ValueHandle pointer, ValueHandle alignment, ValueHandle offset) +static void handle_alignment_assumption(AlignmentAssumptionData& data, ValueHandle pointer, ValueHandle alignment, ValueHandle offset) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -242,6 +371,16 @@ void __ubsan_handle_alignment_assumption(AlignmentAssumptionData& data, ValueHan print_location(location); } +[[gnu::used]] void __ubsan_handle_alignment_assumption(AlignmentAssumptionData& data, ValueHandle pointer, ValueHandle alignment, ValueHandle offset) +{ + handle_alignment_assumption(data, pointer, alignment, offset); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_alignment_assumption_abort(AlignmentAssumptionData& data, ValueHandle pointer, ValueHandle alignment, ValueHandle offset) +{ + handle_alignment_assumption(data, pointer, alignment, offset); + ABORT_ALWAYS(); +} [[gnu::used, noreturn]] void __ubsan_handle_builtin_unreachable(UnreachableData& data) { @@ -257,8 +396,7 @@ void __ubsan_handle_alignment_assumption(AlignmentAssumptionData& data, ValueHan ABORT_ALWAYS(); } -void __ubsan_handle_implicit_conversion(ImplicitConversionData&, ValueHandle, ValueHandle) __attribute__((used)); -void __ubsan_handle_implicit_conversion(ImplicitConversionData& data, ValueHandle, ValueHandle) +static void handle_implicit_conversion(ImplicitConversionData& data, ValueHandle, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -269,9 +407,18 @@ void __ubsan_handle_implicit_conversion(ImplicitConversionData& data, ValueHandl data.from_type.name(), data.from_type.bit_width(), src_signed, data.to_type.name(), data.to_type.bit_width(), dst_signed); print_location(location); } +[[gnu::used]] void __ubsan_handle_implicit_conversion(ImplicitConversionData& data, ValueHandle from, ValueHandle to) +{ + handle_implicit_conversion(data, from, to); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_implicit_conversion_abort(ImplicitConversionData& data, ValueHandle from, ValueHandle to) +{ + handle_implicit_conversion(data, from, to); + ABORT_ALWAYS(); +} -void __ubsan_handle_invalid_builtin(InvalidBuiltinData&) __attribute__((used)); -void __ubsan_handle_invalid_builtin(InvalidBuiltinData& data) +static void handle_invalid_builtin(InvalidBuiltinData& data) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -279,9 +426,18 @@ void __ubsan_handle_invalid_builtin(InvalidBuiltinData& data) WARNLN_AND_DBGLN("UBSAN: passing invalid argument"); print_location(location); } +[[gnu::used]] void __ubsan_handle_invalid_builtin(InvalidBuiltinData& data) +{ + handle_invalid_builtin(data); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_invalid_builtin_abort(InvalidBuiltinData& data) +{ + handle_invalid_builtin(data); + ABORT_ALWAYS(); +} -void __ubsan_handle_pointer_overflow(PointerOverflowData&, ValueHandle, ValueHandle) __attribute__((used)); -void __ubsan_handle_pointer_overflow(PointerOverflowData& data, ValueHandle base, ValueHandle result) +static void handle_pointer_overflow(PointerOverflowData& data, ValueHandle base, ValueHandle result) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -297,9 +453,18 @@ void __ubsan_handle_pointer_overflow(PointerOverflowData& data, ValueHandle base } print_location(location); } +[[gnu::used]] void __ubsan_handle_pointer_overflow(PointerOverflowData& data, ValueHandle base, ValueHandle result) +{ + handle_pointer_overflow(data, base, result); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_pointer_overflow_abort(PointerOverflowData& data, ValueHandle base, ValueHandle result) +{ + handle_pointer_overflow(data, base, result); + ABORT_ALWAYS(); +} -void __ubsan_handle_float_cast_overflow(FloatCastOverflowData&, ValueHandle) __attribute__((used)); -void __ubsan_handle_float_cast_overflow(FloatCastOverflowData& data, ValueHandle) +static void handle_float_cast_overflow(FloatCastOverflowData& data, ValueHandle) { auto location = data.location.permanently_clear(); if (!location.needs_logging()) @@ -307,5 +472,14 @@ void __ubsan_handle_float_cast_overflow(FloatCastOverflowData& data, ValueHandle WARNLN_AND_DBGLN("UBSAN: overflow when casting from {} to {}", data.from_type.name(), data.to_type.name()); print_location(location); } - +[[gnu::used]] void __ubsan_handle_float_cast_overflow(FloatCastOverflowData& data, ValueHandle value) +{ + handle_float_cast_overflow(data, value); + ABORT_IF_DEADLY(); +} +[[gnu::used, noreturn]] void __ubsan_handle_float_cast_overflow_abort(FloatCastOverflowData& data, ValueHandle value) +{ + handle_float_cast_overflow(data, value); + ABORT_ALWAYS(); +} } // extern "C"