Index: lib/ubsan/CMakeLists.txt =================================================================== --- lib/ubsan/CMakeLists.txt +++ lib/ubsan/CMakeLists.txt @@ -5,6 +5,7 @@ ubsan_init.cc ubsan_flags.cc ubsan_handlers.cc + ubsan_monitor.cc ubsan_value.cc ) Index: lib/ubsan/ubsan_diag.h =================================================================== --- lib/ubsan/ubsan_diag.h +++ lib/ubsan/ubsan_diag.h @@ -121,6 +121,12 @@ const char *getName() const { return Name; } }; +enum class ErrorType { +#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) Name, +#include "ubsan_checks.inc" +#undef UBSAN_CHECK +}; + /// \brief Representation of an in-flight diagnostic. /// /// Temporary \c Diag instances are created by the handler routines to @@ -133,6 +139,9 @@ /// The diagnostic level. DiagLevel Level; + /// The error type. + ErrorType ET; + /// The message which will be emitted, with %0, %1, ... placeholders for /// arguments. const char *Message; @@ -197,8 +206,9 @@ Diag &operator=(const Diag &); public: - Diag(Location Loc, DiagLevel Level, const char *Message) - : Loc(Loc), Level(Level), Message(Message), NumArgs(0), NumRanges(0) {} + Diag(Location Loc, DiagLevel Level, ErrorType ET, const char *Message) + : Loc(Loc), Level(Level), ET(ET), Message(Message), NumArgs(0), + NumRanges(0) {} ~Diag(); Diag &operator<<(const char *Str) { return AddArg(Str); } @@ -219,12 +229,6 @@ uptr bp; }; -enum class ErrorType { -#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) Name, -#include "ubsan_checks.inc" -#undef UBSAN_CHECK -}; - bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET); #define GET_REPORT_OPTIONS(unrecoverable_handler) \ Index: lib/ubsan/ubsan_diag.cc =================================================================== --- lib/ubsan/ubsan_diag.cc +++ lib/ubsan/ubsan_diag.cc @@ -16,6 +16,7 @@ #include "ubsan_diag.h" #include "ubsan_init.h" #include "ubsan_flags.h" +#include "ubsan_monitor.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stacktrace.h" @@ -340,6 +341,13 @@ Decorator Decor; InternalScopedString Buffer(1024); + // Prepare a report that a monitor process can inspect. + if (Level == DL_Error) { + RenderText(&Buffer, Message, Args); + UndefinedBehaviorReport UBR{ConvertTypeToString(ET), Loc, Buffer}; + Buffer.clear(); + } + Buffer.append(Decor.Bold()); RenderLocation(&Buffer, Loc); Buffer.append(":"); Index: lib/ubsan/ubsan_handlers.cc =================================================================== --- lib/ubsan/ubsan_handlers.cc +++ lib/ubsan/ubsan_handlers.cc @@ -15,6 +15,7 @@ #if CAN_SANITIZE_UB #include "ubsan_handlers.h" #include "ubsan_diag.h" +#include "ubsan_monitor.h" #include "sanitizer_common/sanitizer_common.h" @@ -70,17 +71,17 @@ switch (ET) { case ErrorType::NullPointerUse: - Diag(Loc, DL_Error, "%0 null pointer of type %1") + Diag(Loc, DL_Error, ET, "%0 null pointer of type %1") << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; break; case ErrorType::MisalignedPointerUse: - Diag(Loc, DL_Error, "%0 misaligned address %1 for type %3, " + Diag(Loc, DL_Error, ET, "%0 misaligned address %1 for type %3, " "which requires %2 byte alignment") << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Alignment << Data->Type; break; case ErrorType::InsufficientObjectSize: - Diag(Loc, DL_Error, "%0 address %1 with insufficient space " + Diag(Loc, DL_Error, ET, "%0 address %1 with insufficient space " "for an object of type %2") << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type; break; @@ -89,7 +90,7 @@ } if (Pointer) - Diag(Pointer, DL_Note, "pointer points here"); + Diag(Pointer, DL_Note, ET, "pointer points here"); } void __ubsan::__ubsan_handle_type_mismatch_v1(TypeMismatchData *Data, @@ -119,10 +120,10 @@ ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, "%0 integer overflow: " - "%1 %2 %3 cannot be represented in type %4") - << (IsSigned ? "signed" : "unsigned") - << Value(Data->Type, LHS) << Operator << RHS << Data->Type; + Diag(Loc, DL_Error, ET, "%0 integer overflow: " + "%1 %2 %3 cannot be represented in type %4") + << (IsSigned ? "signed" : "unsigned") << Value(Data->Type, LHS) + << Operator << RHS << Data->Type; } #define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \ @@ -154,12 +155,12 @@ ScopedReport R(Opts, Loc, ET); if (IsSigned) - Diag(Loc, DL_Error, + Diag(Loc, DL_Error, ET, "negation of %0 cannot be represented in type %1; " "cast to an unsigned type to negate this value to itself") << Value(Data->Type, OldVal) << Data->Type; else - Diag(Loc, DL_Error, "negation of %0 cannot be represented in type %1") + Diag(Loc, DL_Error, ET, "negation of %0 cannot be represented in type %1") << Value(Data->Type, OldVal) << Data->Type; } @@ -196,11 +197,12 @@ switch (ET) { case ErrorType::SignedIntegerOverflow: - Diag(Loc, DL_Error, "division of %0 by -1 cannot be represented in type %1") + Diag(Loc, DL_Error, ET, + "division of %0 by -1 cannot be represented in type %1") << LHSVal << Data->Type; break; default: - Diag(Loc, DL_Error, "division by zero"); + Diag(Loc, DL_Error, ET, "division by zero"); break; } } @@ -239,15 +241,16 @@ if (ET == ErrorType::InvalidShiftExponent) { if (RHSVal.isNegative()) - Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal; + Diag(Loc, DL_Error, ET, "shift exponent %0 is negative") << RHSVal; else - Diag(Loc, DL_Error, "shift exponent %0 is too large for %1-bit type %2") + Diag(Loc, DL_Error, ET, + "shift exponent %0 is too large for %1-bit type %2") << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType; } else { if (LHSVal.isNegative()) - Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal; + Diag(Loc, DL_Error, ET, "left shift of negative value %0") << LHSVal; else - Diag(Loc, DL_Error, + Diag(Loc, DL_Error, ET, "left shift of %0 by %1 places cannot be represented in type %2") << LHSVal << RHSVal << Data->LHSType; } @@ -279,7 +282,7 @@ ScopedReport R(Opts, Loc, ET); Value IndexVal(Data->IndexType, Index); - Diag(Loc, DL_Error, "index %0 out of bounds for type %1") + Diag(Loc, DL_Error, ET, "index %0 out of bounds for type %1") << IndexVal << Data->ArrayType; } @@ -297,8 +300,10 @@ static void handleBuiltinUnreachableImpl(UnreachableData *Data, ReportOptions Opts) { - ScopedReport R(Opts, Data->Loc, ErrorType::UnreachableCall); - Diag(Data->Loc, DL_Error, "execution reached an unreachable program point"); + ErrorType ET = ErrorType::UnreachableCall; + ScopedReport R(Opts, Data->Loc, ET); + Diag(Data->Loc, DL_Error, ET, + "execution reached an unreachable program point"); } void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) { @@ -308,8 +313,9 @@ } static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) { - ScopedReport R(Opts, Data->Loc, ErrorType::MissingReturn); - Diag(Data->Loc, DL_Error, + ErrorType ET = ErrorType::MissingReturn; + ScopedReport R(Opts, Data->Loc, ET); + Diag(Data->Loc, DL_Error, ET, "execution reached the end of a value-returning function " "without returning a value"); } @@ -330,9 +336,9 @@ ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, "variable length array bound evaluates to " - "non-positive value %0") - << Value(Data->Type, Bound); + Diag(Loc, DL_Error, ET, "variable length array bound evaluates to " + "non-positive value %0") + << Value(Data->Type, Bound); } void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data, @@ -390,7 +396,7 @@ ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, + Diag(Loc, DL_Error, ET, "%0 is outside the range of representable values of type %2") << Value(*FromType, From) << *FromType << *ToType; } @@ -421,9 +427,9 @@ ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, + Diag(Loc, DL_Error, ET, "load of value %0, which is not a valid value for type %1") - << Value(Data->Type, Val) << Data->Type; + << Value(Data->Type, Val) << Data->Type; } void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data, @@ -447,7 +453,7 @@ ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, + Diag(Loc, DL_Error, ET, "passing zero to %0, which is not a valid argument") << ((Data->Kind == BCK_CTZPassedZero) ? "ctz()" : "clz()"); } @@ -478,10 +484,10 @@ if (!FName) FName = "(unknown)"; - Diag(CallLoc, DL_Error, + Diag(CallLoc, DL_Error, ET, "call to function %0 through pointer to incorrect function type %1") << FName << Data->Type; - Diag(FLoc, DL_Note, "%0 defined here") << FName; + Diag(FLoc, DL_Note, ET, "%0 defined here") << FName; } void @@ -511,10 +517,10 @@ ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, "null pointer returned from function declared to never " - "return null"); + Diag(Loc, DL_Error, ET, + "null pointer returned from function declared to never return null"); if (!Data->AttrLoc.isInvalid()) - Diag(Data->AttrLoc, DL_Note, "%0 specified here") + Diag(Data->AttrLoc, DL_Note, ET, "%0 specified here") << (IsAttr ? "returns_nonnull attribute" : "_Nonnull return type annotation"); } @@ -555,12 +561,12 @@ ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, + Diag(Loc, DL_Error, ET, "null pointer passed as argument %0, which is declared to " "never be null") << Data->ArgIndex; if (!Data->AttrLoc.isInvalid()) - Diag(Data->AttrLoc, DL_Note, "%0 specified here") + Diag(Data->AttrLoc, DL_Note, ET, "%0 specified here") << (IsAttr ? "nonnull attribute" : "_Nonnull type annotation"); } @@ -600,14 +606,15 @@ if ((sptr(Base) >= 0) == (sptr(Result) >= 0)) { if (Base > Result) - Diag(Loc, DL_Error, "addition of unsigned offset to %0 overflowed to %1") + Diag(Loc, DL_Error, ET, + "addition of unsigned offset to %0 overflowed to %1") << (void *)Base << (void *)Result; else - Diag(Loc, DL_Error, + Diag(Loc, DL_Error, ET, "subtraction of unsigned offset from %0 overflowed to %1") << (void *)Base << (void *)Result; } else { - Diag(Loc, DL_Error, + Diag(Loc, DL_Error, ET, "pointer index expression with base %0 overflowed to %1") << (void *)Base << (void *)Result; } @@ -641,15 +648,16 @@ ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during " - "indirect function call") + Diag(Loc, DL_Error, ET, + "control flow integrity check for type %0 failed during " + "indirect function call") << Data->Type; SymbolizedStackHolder FLoc(getSymbolizedLocation(Function)); const char *FName = FLoc.get()->info.function; if (!FName) FName = "(unknown)"; - Diag(FLoc, DL_Note, "%0 defined here") << FName; + Diag(FLoc, DL_Note, ET, "%0 defined here") << FName; } namespace __ubsan { Index: lib/ubsan/ubsan_handlers_cxx.cc =================================================================== --- lib/ubsan/ubsan_handlers_cxx.cc +++ lib/ubsan/ubsan_handlers_cxx.cc @@ -50,29 +50,30 @@ ScopedReport R(Opts, Loc, ET); - Diag(Loc, DL_Error, + Diag(Loc, DL_Error, ET, "%0 address %1 which does not point to an object of type %2") << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; // If possible, say what type it actually points to. if (!DTI.isValid()) { if (DTI.getOffset() < -VptrMaxOffsetToTop || DTI.getOffset() > VptrMaxOffsetToTop) { - Diag(Pointer, DL_Note, "object has a possibly invalid vptr: abs(offset to top) too big") + Diag(Pointer, DL_Note, ET, + "object has a possibly invalid vptr: abs(offset to top) too big") << TypeName(DTI.getMostDerivedTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "possibly invalid vptr"); } else { - Diag(Pointer, DL_Note, "object has invalid vptr") + Diag(Pointer, DL_Note, ET, "object has invalid vptr") << TypeName(DTI.getMostDerivedTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr"); } } else if (!DTI.getOffset()) - Diag(Pointer, DL_Note, "object is of type %0") + Diag(Pointer, DL_Note, ET, "object is of type %0") << TypeName(DTI.getMostDerivedTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0"); else // FIXME: Find the type at the specified offset, and include that // in the note. - Diag(Pointer - DTI.getOffset(), DL_Note, + Diag(Pointer - DTI.getOffset(), DL_Note, ET, "object is base class subobject at offset %0 within object of type %1") << DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName()) << TypeName(DTI.getSubobjectTypeName()) @@ -126,19 +127,20 @@ Die(); } - Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during " - "%1 (vtable address %2)") + Diag(Loc, DL_Error, ET, + "control flow integrity check for type %0 failed during " + "%1 (vtable address %2)") << Data->Type << CheckKindStr << (void *)Vtable; // If possible, say what type it actually points to. if (!DTI.isValid()) { const char *module = Symbolizer::GetOrInit()->GetModuleNameForPc(Vtable); if (module) - Diag(Vtable, DL_Note, "invalid vtable in module %0") << module; + Diag(Vtable, DL_Note, ET, "invalid vtable in module %0") << module; else - Diag(Vtable, DL_Note, "invalid vtable"); + Diag(Vtable, DL_Note, ET, "invalid vtable"); } else { - Diag(Vtable, DL_Note, "vtable is of type %0") + Diag(Vtable, DL_Note, ET, "vtable is of type %0") << TypeName(DTI.getMostDerivedTypeName()); } } Index: lib/ubsan/ubsan_interface.inc =================================================================== --- lib/ubsan/ubsan_interface.inc +++ lib/ubsan/ubsan_interface.inc @@ -52,3 +52,5 @@ INTERFACE_FUNCTION(__ubsan_handle_vla_bound_not_positive) INTERFACE_FUNCTION(__ubsan_handle_vla_bound_not_positive_abort) INTERFACE_WEAK_FUNCTION(__ubsan_default_options) +INTERFACE_WEAK_FUNCTION(__ubsan_on_report) +INTERFACE_FUNCTION(__ubsan_get_current_report_data) Index: lib/ubsan/ubsan_monitor.h =================================================================== --- /dev/null +++ lib/ubsan/ubsan_monitor.h @@ -0,0 +1,49 @@ +//===-- ubsan_monitor.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hooks which allow a monitor process to inspect UBSan's diagnostics. +// +//===----------------------------------------------------------------------===// + +#ifndef UBSAN_MONITOR_H +#define UBSAN_MONITOR_H + +#include "ubsan_diag.h" +#include "ubsan_value.h" + +namespace __ubsan { + +struct UndefinedBehaviorReport { + const char *IssueKind; + Location &Loc; + InternalScopedString Buffer; + + UndefinedBehaviorReport(const char *IssueKind, Location &Loc, + InternalScopedString &Msg); +}; + +SANITIZER_INTERFACE_ATTRIBUTE void +RegisterUndefinedBehaviorReport(UndefinedBehaviorReport *UBR); + +/// Called after a report is prepared. This serves to alert monitor processes +/// that a UB report is available. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __ubsan_on_report(void); + +/// Used by the monitor process to extract information from a UB report. The +/// data is only available until the next time __ubsan_on_report is called. The +/// caller is responsible for copying and preserving the data if needed. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__ubsan_get_current_report_data(const char **OutIssueKind, + const char **OutMessage, + const char **OutFilename, unsigned *OutLine, + unsigned *OutCol, char **OutMemoryAddr); + +} // end namespace __ubsan + +#endif // UBSAN_MONITOR_H Index: lib/ubsan/ubsan_monitor.cc =================================================================== --- /dev/null +++ lib/ubsan/ubsan_monitor.cc @@ -0,0 +1,76 @@ +//===-- ubsan_monitor.cc ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hooks which allow a monitor process to inspect UBSan's diagnostics. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_monitor.h" + +using namespace __ubsan; + +UndefinedBehaviorReport::UndefinedBehaviorReport(const char *IssueKind, + Location &Loc, + InternalScopedString &Msg) + : IssueKind(IssueKind), Loc(Loc), Buffer(Msg.length() + 1) { + // We have the common sanitizer reporting lock, so it's safe to register a + // new UB report. + RegisterUndefinedBehaviorReport(this); + + // Make a copy of the diagnostic. + Buffer.append("%s", Msg.data()); + + // Let the monitor know that a report is available. + __ubsan_on_report(); +} + +static UndefinedBehaviorReport *CurrentUBR; + +void __ubsan::RegisterUndefinedBehaviorReport(UndefinedBehaviorReport *UBR) { + CurrentUBR = UBR; +} + +SANITIZER_WEAK_DEFAULT_IMPL +void __ubsan::__ubsan_on_report(void) {} + +void __ubsan::__ubsan_get_current_report_data(const char **OutIssueKind, + const char **OutMessage, + const char **OutFilename, + unsigned *OutLine, + unsigned *OutCol, + char **OutMemoryAddr) { + if (!OutIssueKind || !OutMessage || !OutFilename || !OutLine || !OutCol || + !OutMemoryAddr) + UNREACHABLE("Invalid arguments passed to __ubsan_get_current_report_data"); + + InternalScopedString &Buf = CurrentUBR->Buffer; + + // Ensure that the first character of the diagnostic text can't start with a + // lowercase letter. + char FirstChar = Buf.data()[0]; + if (FirstChar >= 'a' && FirstChar <= 'z') + Buf.data()[0] = FirstChar - 'a' + 'A'; + + *OutIssueKind = CurrentUBR->IssueKind; + *OutMessage = Buf.data(); + if (!CurrentUBR->Loc.isSourceLocation()) { + *OutFilename = ""; + *OutLine = *OutCol = 0; + } else { + SourceLocation SL = CurrentUBR->Loc.getSourceLocation(); + *OutFilename = SL.getFilename(); + *OutLine = SL.getLine(); + *OutCol = SL.getColumn(); + } + + if (CurrentUBR->Loc.isMemoryLocation()) + *OutMemoryAddr = (char *)CurrentUBR->Loc.getMemoryLocation(); + else + *OutMemoryAddr = nullptr; +} Index: test/ubsan/TestCases/Misc/monitor.cpp =================================================================== --- /dev/null +++ test/ubsan/TestCases/Misc/monitor.cpp @@ -0,0 +1,37 @@ +// RUN: %clangxx -w -fsanitize=bool %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include + +extern "C" { +void __ubsan_get_current_report_data(const char **OutIssueKind, + const char **OutMessage, + const char **OutFilename, + unsigned *OutLine, unsigned *OutCol, + char **OutMemoryAddr); + +// Override the weak definition of __ubsan_on_report from the runtime, just +// for testing purposes. +void __ubsan_on_report(void) { + const char *IssueKind, *Message, *Filename; + unsigned Line, Col; + char *Addr; + __ubsan_get_current_report_data(&IssueKind, &Message, &Filename, &Line, &Col, + &Addr); + + std::cout << "Issue: " << IssueKind << "\n" + << "Location: " << Filename << ":" << Line << ":" << Col << "\n" + << "Message: " << Message << std::endl; + + (void)Addr; +} +} + +int main() { + char C = 3; + bool B = *(bool *)&C; + // CHECK: Issue: invalid-bool-load + // CHECK-NEXT: Location: {{.*}}monitor.cpp:[[@LINE-2]]:12 + // CHECK-NEXT: Message: Load of value 3, which is not a valid value for type 'bool' + return 0; +}