#include #include #define FMT(obj) format_object((obj), opts, formatter) #define LOG_NAME_LOC(name, loc) "ubsan: " name " at " << loc << "\n " #define LOG_LHS_RHS(lhs, rhs) "LHS = " << (lhs) << ", RHS = " << (rhs) struct SourceLocation { const char *filename; uint32_t line; uint32_t column; }; template void format_object(const SourceLocation &loc, frg::format_options opts, F &formatter) { FMT(loc.filename); FMT(":"); FMT(loc.line); FMT(":"); FMT(loc.column); } using ValueHandle = uintptr_t; struct TypeDescriptor { enum class Kind : uint16_t { Integer = 0x0000, Float = 0x0001, Unknown = 0xffff } kind; uint16_t info; char name[]; unsigned bitWidthInt() const { return 1 << (info >> 1); } bool isInlineInt() const { if (kind != Kind::Integer) return false; auto inlineBits = sizeof(ValueHandle) * CHAR_BIT; auto valueBits = bitWidthInt(); return inlineBits <= valueBits; } bool isSigned() const { return info & 1; } }; template void format_object(const TypeDescriptor &type, frg::format_options opts, F &formatter) { FMT(type.name); } struct Value { const TypeDescriptor &type; ValueHandle val; Value(const TypeDescriptor &type, ValueHandle val) : type(type), val(val) {} }; template void format_object(const Value &val, frg::format_options opts, F &formatter) { if (val.type.isInlineInt() && val.type.isSigned()) { auto signedValue = static_cast(val.val); FMT(signedValue); } else if (val.type.isInlineInt() && !val.type.isSigned()) { auto unsignedValue = static_cast(val.val); FMT(unsignedValue); } FMT(" ("); FMT(val.type); FMT(")"); } // --- Hook implementations --- struct TypeMismatch { SourceLocation loc; const TypeDescriptor &type; unsigned char logAlignment; unsigned char kind; }; extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_type_mismatch_v1(TypeMismatch *tm, ValueHandle pointer) { // TODO: Make this print more information. mlibc::panicLogger() << LOG_NAME_LOC("type mismatch", tm->loc) << "accessed address " << (void *)pointer << " but type " << tm->type << " requires alignment " << (1 << tm->logAlignment) << frg::endlog; } struct PointerOverflowData { SourceLocation loc; }; extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_pointer_overflow(PointerOverflowData *pod, ValueHandle base, ValueHandle result) { (void)base; (void)result; mlibc::panicLogger() << LOG_NAME_LOC("pointer overflow", pod->loc) << frg::endlog; } struct InvalidValueData { SourceLocation loc; const TypeDescriptor &type; }; extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_load_invalid_value(InvalidValueData *ivd, ValueHandle value) { (void)value; mlibc::panicLogger() << LOG_NAME_LOC("load of invalid value", ivd->loc) << frg::endlog; } struct OverflowData { SourceLocation loc; const TypeDescriptor &type; }; extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_add_overflow(OverflowData *od, ValueHandle lhs, ValueHandle rhs) { mlibc::panicLogger() << LOG_NAME_LOC("add overflowed ", od->loc) << LOG_LHS_RHS(Value(od->type, lhs), Value(od->type, rhs)) << frg::endlog; } extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_sub_overflow(OverflowData *od, ValueHandle lhs, ValueHandle rhs) { mlibc::panicLogger() << LOG_NAME_LOC("sub overflowed", od->loc) << LOG_LHS_RHS(Value(od->type, lhs), Value(od->type, rhs)) << frg::endlog; } extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_mul_overflow(OverflowData *od, ValueHandle lhs, ValueHandle rhs) { mlibc::panicLogger() << LOG_NAME_LOC("mul overflowed", od->loc) << LOG_LHS_RHS(Value(od->type, lhs), Value(od->type, rhs)) << frg::endlog; } extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_divrem_overflow(OverflowData *od, ValueHandle lhs, ValueHandle rhs) { mlibc::panicLogger() << LOG_NAME_LOC("divrem overflowed", od->loc) << LOG_LHS_RHS(Value(od->type, lhs), Value(od->type, rhs)) << frg::endlog; } extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_negate_overflow(OverflowData *od, ValueHandle lhs, ValueHandle rhs) { mlibc::panicLogger() << LOG_NAME_LOC("negate overflowed", od->loc) << LOG_LHS_RHS(Value(od->type, lhs), Value(od->type, rhs)) << frg::endlog; } struct ShiftOutOfBoundsData { SourceLocation loc; const TypeDescriptor &lhsType; const TypeDescriptor &rhsType; }; extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *soob, ValueHandle lhs, ValueHandle rhs) { mlibc::panicLogger() << LOG_NAME_LOC("shift out of bounds", soob->loc) << LOG_LHS_RHS(Value(soob->lhsType, lhs), Value(soob->rhsType, rhs)) << frg::endlog; } struct OutOfBoundsData { SourceLocation loc; const TypeDescriptor &arrayType; const TypeDescriptor &indexType; }; extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_out_of_bounds(OutOfBoundsData *oobd, ValueHandle data) { (void)data; mlibc::panicLogger() << LOG_NAME_LOC("out of bounds access", oobd->loc) << frg::endlog; } struct UnreachableData { SourceLocation loc; }; extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_builtin_unreachable(UnreachableData *ubd) { mlibc::panicLogger() << LOG_NAME_LOC("reached __builtin_unreachable()", ubd->loc) << frg::endlog; } struct InvalidBuiltinData { SourceLocation loc; unsigned char kind; }; extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_invalid_builtin(InvalidBuiltinData *ibd) { mlibc::panicLogger() << LOG_NAME_LOC("reached invalid builtin", ibd->loc) << frg::endlog; } struct VLABoundData { SourceLocation loc; const TypeDescriptor &type; }; extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_vla_bound_not_positive(VLABoundData *vlabd) { mlibc::panicLogger() << LOG_NAME_LOC("VLA bound not positive", vlabd->loc) << frg::endlog; } extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_missing_return(UnreachableData *data) { mlibc::panicLogger() << LOG_NAME_LOC("reached end of a value-returning function without returning a value", data->loc) << frg::endlog; } struct NonNullArgData { SourceLocation loc; SourceLocation attr_loc; int arg_index; }; extern "C" [[gnu::visibility("hidden")]] void __ubsan_handle_nonnull_arg(NonNullArgData *data) { mlibc::panicLogger() << LOG_NAME_LOC("null pointer passed to non-null argument", data->loc) << "argument " << data->arg_index << " is required to be non-null in " << data->attr_loc << frg::endlog; }