diff options
Diffstat (limited to 'compiler-rt/lib/ubsan')
-rw-r--r-- | compiler-rt/lib/ubsan/ubsan_checks.inc | 7 | ||||
-rw-r--r-- | compiler-rt/lib/ubsan/ubsan_handlers.cpp | 81 | ||||
-rw-r--r-- | compiler-rt/lib/ubsan/ubsan_handlers.h | 8 | ||||
-rw-r--r-- | compiler-rt/lib/ubsan/ubsan_init.cpp | 2 | ||||
-rw-r--r-- | compiler-rt/lib/ubsan/ubsan_interface.inc | 2 | ||||
-rw-r--r-- | compiler-rt/lib/ubsan/ubsan_type_hash_itanium.cpp | 2 | ||||
-rw-r--r-- | compiler-rt/lib/ubsan/ubsan_value.cpp | 48 | ||||
-rw-r--r-- | compiler-rt/lib/ubsan/ubsan_value.h | 3 |
8 files changed, 150 insertions, 3 deletions
diff --git a/compiler-rt/lib/ubsan/ubsan_checks.inc b/compiler-rt/lib/ubsan/ubsan_checks.inc index 33a8dfcde026..846cd89ee19f 100644 --- a/compiler-rt/lib/ubsan/ubsan_checks.inc +++ b/compiler-rt/lib/ubsan/ubsan_checks.inc @@ -18,6 +18,8 @@ UBSAN_CHECK(GenericUB, "undefined-behavior", "undefined") UBSAN_CHECK(NullPointerUse, "null-pointer-use", "null") +UBSAN_CHECK(NullPointerUseWithNullability, "null-pointer-use", + "nullability-assign") UBSAN_CHECK(NullptrWithOffset, "nullptr-with-offset", "pointer-overflow") UBSAN_CHECK(NullptrWithNonZeroOffset, "nullptr-with-nonzero-offset", "pointer-overflow") @@ -35,6 +37,7 @@ UBSAN_CHECK(IntegerDivideByZero, "integer-divide-by-zero", "integer-divide-by-zero") UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero", "float-divide-by-zero") UBSAN_CHECK(InvalidBuiltin, "invalid-builtin-use", "invalid-builtin-use") +UBSAN_CHECK(InvalidObjCCast, "invalid-objc-cast", "invalid-objc-cast") UBSAN_CHECK(ImplicitUnsignedIntegerTruncation, "implicit-unsigned-integer-truncation", "implicit-unsigned-integer-truncation") @@ -59,6 +62,10 @@ UBSAN_CHECK(InvalidEnumLoad, "invalid-enum-load", "enum") UBSAN_CHECK(FunctionTypeMismatch, "function-type-mismatch", "function") UBSAN_CHECK(InvalidNullReturn, "invalid-null-return", "returns-nonnull-attribute") +UBSAN_CHECK(InvalidNullReturnWithNullability, "invalid-null-return", + "nullability-return") UBSAN_CHECK(InvalidNullArgument, "invalid-null-argument", "nonnull-attribute") +UBSAN_CHECK(InvalidNullArgumentWithNullability, "invalid-null-argument", + "nullability-arg") UBSAN_CHECK(DynamicTypeMismatch, "dynamic-type-mismatch", "vptr") UBSAN_CHECK(CFIBadType, "cfi-bad-type", "cfi") diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cpp b/compiler-rt/lib/ubsan/ubsan_handlers.cpp index 3f9da75a12a8..e201e6bba220 100644 --- a/compiler-rt/lib/ubsan/ubsan_handlers.cpp +++ b/compiler-rt/lib/ubsan/ubsan_handlers.cpp @@ -16,6 +16,7 @@ #include "ubsan_diag.h" #include "ubsan_flags.h" #include "ubsan_monitor.h" +#include "ubsan_value.h" #include "sanitizer_common/sanitizer_common.h" @@ -36,6 +37,45 @@ bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET) { return SLoc.isDisabled() || IsPCSuppressed(ET, Opts.pc, SLoc.getFilename()); } +/// Situations in which we might emit a check for the suitability of a +/// pointer or glvalue. Needs to be kept in sync with CodeGenFunction.h in +/// clang. +enum TypeCheckKind { + /// Checking the operand of a load. Must be suitably sized and aligned. + TCK_Load, + /// Checking the destination of a store. Must be suitably sized and aligned. + TCK_Store, + /// Checking the bound value in a reference binding. Must be suitably sized + /// and aligned, but is not required to refer to an object (until the + /// reference is used), per core issue 453. + TCK_ReferenceBinding, + /// Checking the object expression in a non-static data member access. Must + /// be an object within its lifetime. + TCK_MemberAccess, + /// Checking the 'this' pointer for a call to a non-static member function. + /// Must be an object within its lifetime. + TCK_MemberCall, + /// Checking the 'this' pointer for a constructor call. + TCK_ConstructorCall, + /// Checking the operand of a static_cast to a derived pointer type. Must be + /// null or an object within its lifetime. + TCK_DowncastPointer, + /// Checking the operand of a static_cast to a derived reference type. Must + /// be an object within its lifetime. + TCK_DowncastReference, + /// Checking the operand of a cast to a base object. Must be suitably sized + /// and aligned. + TCK_Upcast, + /// Checking the operand of a cast to a virtual base object. Must be an + /// object within its lifetime. + TCK_UpcastToVirtualBase, + /// Checking the value assigned to a _Nonnull pointer. Must not be null. + TCK_NonnullAssign, + /// Checking the operand of a dynamic_cast or a typeid expression. Must be + /// null or an object within its lifetime. + TCK_DynamicOperation +}; + const char *TypeCheckKinds[] = { "load of", "store to", "reference binding to", "member access within", "member call on", "constructor call on", "downcast of", "downcast of", @@ -50,7 +90,9 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, uptr Alignment = (uptr)1 << Data->LogAlignment; ErrorType ET; if (!Pointer) - ET = ErrorType::NullPointerUse; + ET = (Data->TypeCheckKind == TCK_NonnullAssign) + ? ErrorType::NullPointerUseWithNullability + : ErrorType::NullPointerUse; else if (Pointer & (Alignment - 1)) ET = ErrorType::MisalignedPointerUse; else @@ -71,6 +113,7 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, switch (ET) { case ErrorType::NullPointerUse: + case ErrorType::NullPointerUseWithNullability: Diag(Loc, DL_Error, ET, "%0 null pointer of type %1") << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; break; @@ -598,13 +641,44 @@ void __ubsan::__ubsan_handle_invalid_builtin_abort(InvalidBuiltinData *Data) { Die(); } +static void handleInvalidObjCCast(InvalidObjCCast *Data, ValueHandle Pointer, + ReportOptions Opts) { + SourceLocation Loc = Data->Loc.acquire(); + ErrorType ET = ErrorType::InvalidObjCCast; + + if (ignoreReport(Loc, Opts, ET)) + return; + + ScopedReport R(Opts, Loc, ET); + + const char *GivenClass = getObjCClassName(Pointer); + const char *GivenClassStr = GivenClass ? GivenClass : "<unknown type>"; + + Diag(Loc, DL_Error, ET, + "invalid ObjC cast, object is a '%0', but expected a %1") + << GivenClassStr << Data->ExpectedType; +} + +void __ubsan::__ubsan_handle_invalid_objc_cast(InvalidObjCCast *Data, + ValueHandle Pointer) { + GET_REPORT_OPTIONS(false); + handleInvalidObjCCast(Data, Pointer, Opts); +} +void __ubsan::__ubsan_handle_invalid_objc_cast_abort(InvalidObjCCast *Data, + ValueHandle Pointer) { + GET_REPORT_OPTIONS(true); + handleInvalidObjCCast(Data, Pointer, Opts); + Die(); +} + static void handleNonNullReturn(NonNullReturnData *Data, SourceLocation *LocPtr, ReportOptions Opts, bool IsAttr) { if (!LocPtr) UNREACHABLE("source location pointer is null!"); SourceLocation Loc = LocPtr->acquire(); - ErrorType ET = ErrorType::InvalidNullReturn; + ErrorType ET = IsAttr ? ErrorType::InvalidNullReturn + : ErrorType::InvalidNullReturnWithNullability; if (ignoreReport(Loc, Opts, ET)) return; @@ -648,7 +722,8 @@ void __ubsan::__ubsan_handle_nullability_return_v1_abort( static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts, bool IsAttr) { SourceLocation Loc = Data->Loc.acquire(); - ErrorType ET = ErrorType::InvalidNullArgument; + ErrorType ET = IsAttr ? ErrorType::InvalidNullArgument + : ErrorType::InvalidNullArgumentWithNullability; if (ignoreReport(Loc, Opts, ET)) return; diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.h b/compiler-rt/lib/ubsan/ubsan_handlers.h index 22ca96422381..219fb15de55f 100644 --- a/compiler-rt/lib/ubsan/ubsan_handlers.h +++ b/compiler-rt/lib/ubsan/ubsan_handlers.h @@ -168,6 +168,14 @@ struct InvalidBuiltinData { /// Handle a builtin called in an invalid way. RECOVERABLE(invalid_builtin, InvalidBuiltinData *Data) +struct InvalidObjCCast { + SourceLocation Loc; + const TypeDescriptor &ExpectedType; +}; + +/// Handle an invalid ObjC cast. +RECOVERABLE(invalid_objc_cast, InvalidObjCCast *Data, ValueHandle Pointer) + struct NonNullReturnData { SourceLocation AttrLoc; }; diff --git a/compiler-rt/lib/ubsan/ubsan_init.cpp b/compiler-rt/lib/ubsan/ubsan_init.cpp index 1a3b7d372674..e0be5a72ec42 100644 --- a/compiler-rt/lib/ubsan/ubsan_init.cpp +++ b/compiler-rt/lib/ubsan/ubsan_init.cpp @@ -37,10 +37,12 @@ static void CommonStandaloneInit() { SanitizerToolName = GetSanititizerToolName(); CacheBinaryName(); InitializeFlags(); + __sanitizer::InitializePlatformEarly(); __sanitizer_set_report_path(common_flags()->log_path); AndroidLogInit(); InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); CommonInit(); + Symbolizer::LateInitialize(); } void __ubsan::InitAsStandalone() { diff --git a/compiler-rt/lib/ubsan/ubsan_interface.inc b/compiler-rt/lib/ubsan/ubsan_interface.inc index 1e44bc2171de..94337d85017b 100644 --- a/compiler-rt/lib/ubsan/ubsan_interface.inc +++ b/compiler-rt/lib/ubsan/ubsan_interface.inc @@ -27,6 +27,8 @@ INTERFACE_FUNCTION(__ubsan_handle_implicit_conversion) INTERFACE_FUNCTION(__ubsan_handle_implicit_conversion_abort) INTERFACE_FUNCTION(__ubsan_handle_invalid_builtin) INTERFACE_FUNCTION(__ubsan_handle_invalid_builtin_abort) +INTERFACE_FUNCTION(__ubsan_handle_invalid_objc_cast) +INTERFACE_FUNCTION(__ubsan_handle_invalid_objc_cast_abort) INTERFACE_FUNCTION(__ubsan_handle_load_invalid_value) INTERFACE_FUNCTION(__ubsan_handle_load_invalid_value_abort) INTERFACE_FUNCTION(__ubsan_handle_missing_return) diff --git a/compiler-rt/lib/ubsan/ubsan_type_hash_itanium.cpp b/compiler-rt/lib/ubsan/ubsan_type_hash_itanium.cpp index 97846d4dd434..4f1708ba1901 100644 --- a/compiler-rt/lib/ubsan/ubsan_type_hash_itanium.cpp +++ b/compiler-rt/lib/ubsan/ubsan_type_hash_itanium.cpp @@ -16,6 +16,7 @@ #include "ubsan_type_hash.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_ptrauth.h" // The following are intended to be binary compatible with the definitions // given in the Itanium ABI. We make no attempt to be ODR-compatible with @@ -194,6 +195,7 @@ struct VtablePrefix { std::type_info *TypeInfo; }; VtablePrefix *getVtablePrefix(void *Vtable) { + Vtable = ptrauth_auth_data(Vtable, ptrauth_key_cxx_vtable_pointer, 0); VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable); VtablePrefix *Prefix = Vptr - 1; if (!IsAccessibleMemoryRange((uptr)Prefix, sizeof(VtablePrefix))) diff --git a/compiler-rt/lib/ubsan/ubsan_value.cpp b/compiler-rt/lib/ubsan/ubsan_value.cpp index 60f0b5c99348..79c3ba991d39 100644 --- a/compiler-rt/lib/ubsan/ubsan_value.cpp +++ b/compiler-rt/lib/ubsan/ubsan_value.cpp @@ -16,9 +16,57 @@ #include "ubsan_value.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_mutex.h" + +// TODO(dliew): Prefer '__APPLE__' here over 'SANITIZER_MAC', as the latter is +// unclear. rdar://58124919 tracks using a more obviously portable guard. +#if defined(__APPLE__) +#include <dlfcn.h> +#endif using namespace __ubsan; +typedef const char *(*ObjCGetClassNameTy)(void *); + +const char *__ubsan::getObjCClassName(ValueHandle Pointer) { +#if defined(__APPLE__) + // We need to query the ObjC runtime for some information, but do not want + // to introduce a static dependency from the ubsan runtime onto ObjC. Try to + // grab a handle to the ObjC runtime used by the process. + static bool AttemptedDlopen = false; + static void *ObjCHandle = nullptr; + static void *ObjCObjectGetClassName = nullptr; + + // Prevent threads from racing to dlopen(). + static __sanitizer::StaticSpinMutex Lock; + { + __sanitizer::SpinMutexLock Guard(&Lock); + + if (!AttemptedDlopen) { + ObjCHandle = dlopen( + "/usr/lib/libobjc.A.dylib", + RTLD_LAZY // Only bind symbols when used. + | RTLD_LOCAL // Only make symbols available via the handle. + | RTLD_NOLOAD // Do not load the dylib, just grab a handle if the + // image is already loaded. + | RTLD_FIRST // Only search the image pointed-to by the handle. + ); + AttemptedDlopen = true; + if (!ObjCHandle) + return nullptr; + ObjCObjectGetClassName = dlsym(ObjCHandle, "object_getClassName"); + } + } + + if (!ObjCObjectGetClassName) + return nullptr; + + return ObjCGetClassNameTy(ObjCObjectGetClassName)((void *)Pointer); +#else + return nullptr; +#endif +} + SIntMax Value::getSIntValue() const { CHECK(getType().isSignedIntegerTy()); if (isInlineInt()) { diff --git a/compiler-rt/lib/ubsan/ubsan_value.h b/compiler-rt/lib/ubsan/ubsan_value.h index a216e3a147e9..e0957276dd24 100644 --- a/compiler-rt/lib/ubsan/ubsan_value.h +++ b/compiler-rt/lib/ubsan/ubsan_value.h @@ -135,6 +135,9 @@ public: /// \brief An opaque handle to a value. typedef uptr ValueHandle; +/// Returns the class name of the given ObjC object, or null if the name +/// cannot be found. +const char *getObjCClassName(ValueHandle Pointer); /// \brief Representation of an operand value provided by the instrumented code. /// |