[ubsan] Add support for reporting diagnostics to a monitor process
Add support to the ubsan runtime for reporting diagnostics to a monitor
process (e.g a debugger).
The Xcode IDE uses this by setting a breakpoint on __ubsan_on_report and
collecting diagnostic information via __ubsan_get_current_report_data,
which it then surfaces to users in the editor UI.
Testing for this functionality already exists in upstream lldb, here:
lldb/packages/Python/lldbsuite/test/functionalities/ubsan
Apart from that, this is `ninja check-{a,ub}san` clean.
Differential Revision: https://reviews.llvm.org/D48446
llvm-svn: 335371
This commit is contained in:
parent
22d1db122a
commit
059d20360a
|
|
@ -5,6 +5,7 @@ set(UBSAN_SOURCES
|
||||||
ubsan_init.cc
|
ubsan_init.cc
|
||||||
ubsan_flags.cc
|
ubsan_flags.cc
|
||||||
ubsan_handlers.cc
|
ubsan_handlers.cc
|
||||||
|
ubsan_monitor.cc
|
||||||
ubsan_value.cc
|
ubsan_value.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include "ubsan_diag.h"
|
#include "ubsan_diag.h"
|
||||||
#include "ubsan_init.h"
|
#include "ubsan_init.h"
|
||||||
#include "ubsan_flags.h"
|
#include "ubsan_flags.h"
|
||||||
|
#include "ubsan_monitor.h"
|
||||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||||
#include "sanitizer_common/sanitizer_report_decorator.h"
|
#include "sanitizer_common/sanitizer_report_decorator.h"
|
||||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||||
|
|
@ -340,6 +341,13 @@ Diag::~Diag() {
|
||||||
Decorator Decor;
|
Decorator Decor;
|
||||||
InternalScopedString Buffer(1024);
|
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());
|
Buffer.append(Decor.Bold());
|
||||||
RenderLocation(&Buffer, Loc);
|
RenderLocation(&Buffer, Loc);
|
||||||
Buffer.append(":");
|
Buffer.append(":");
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,12 @@ public:
|
||||||
const char *getName() const { return Name; }
|
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.
|
/// \brief Representation of an in-flight diagnostic.
|
||||||
///
|
///
|
||||||
/// Temporary \c Diag instances are created by the handler routines to
|
/// Temporary \c Diag instances are created by the handler routines to
|
||||||
|
|
@ -133,6 +139,9 @@ class Diag {
|
||||||
/// The diagnostic level.
|
/// The diagnostic level.
|
||||||
DiagLevel Level;
|
DiagLevel Level;
|
||||||
|
|
||||||
|
/// The error type.
|
||||||
|
ErrorType ET;
|
||||||
|
|
||||||
/// The message which will be emitted, with %0, %1, ... placeholders for
|
/// The message which will be emitted, with %0, %1, ... placeholders for
|
||||||
/// arguments.
|
/// arguments.
|
||||||
const char *Message;
|
const char *Message;
|
||||||
|
|
@ -197,8 +206,9 @@ private:
|
||||||
Diag &operator=(const Diag &);
|
Diag &operator=(const Diag &);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Diag(Location Loc, DiagLevel Level, const char *Message)
|
Diag(Location Loc, DiagLevel Level, ErrorType ET, const char *Message)
|
||||||
: Loc(Loc), Level(Level), Message(Message), NumArgs(0), NumRanges(0) {}
|
: Loc(Loc), Level(Level), ET(ET), Message(Message), NumArgs(0),
|
||||||
|
NumRanges(0) {}
|
||||||
~Diag();
|
~Diag();
|
||||||
|
|
||||||
Diag &operator<<(const char *Str) { return AddArg(Str); }
|
Diag &operator<<(const char *Str) { return AddArg(Str); }
|
||||||
|
|
@ -219,12 +229,6 @@ struct ReportOptions {
|
||||||
uptr bp;
|
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);
|
bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET);
|
||||||
|
|
||||||
#define GET_REPORT_OPTIONS(unrecoverable_handler) \
|
#define GET_REPORT_OPTIONS(unrecoverable_handler) \
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#if CAN_SANITIZE_UB
|
#if CAN_SANITIZE_UB
|
||||||
#include "ubsan_handlers.h"
|
#include "ubsan_handlers.h"
|
||||||
#include "ubsan_diag.h"
|
#include "ubsan_diag.h"
|
||||||
|
#include "ubsan_monitor.h"
|
||||||
|
|
||||||
#include "sanitizer_common/sanitizer_common.h"
|
#include "sanitizer_common/sanitizer_common.h"
|
||||||
|
|
||||||
|
|
@ -70,17 +71,17 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
|
||||||
|
|
||||||
switch (ET) {
|
switch (ET) {
|
||||||
case ErrorType::NullPointerUse:
|
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;
|
<< TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
|
||||||
break;
|
break;
|
||||||
case ErrorType::MisalignedPointerUse:
|
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")
|
"which requires %2 byte alignment")
|
||||||
<< TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Alignment
|
<< TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Alignment
|
||||||
<< Data->Type;
|
<< Data->Type;
|
||||||
break;
|
break;
|
||||||
case ErrorType::InsufficientObjectSize:
|
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")
|
"for an object of type %2")
|
||||||
<< TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type;
|
<< TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type;
|
||||||
break;
|
break;
|
||||||
|
|
@ -89,7 +90,7 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Pointer)
|
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,
|
void __ubsan::__ubsan_handle_type_mismatch_v1(TypeMismatchData *Data,
|
||||||
|
|
@ -119,10 +120,10 @@ static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
|
||||||
|
|
||||||
ScopedReport R(Opts, Loc, ET);
|
ScopedReport R(Opts, Loc, ET);
|
||||||
|
|
||||||
Diag(Loc, DL_Error, "%0 integer overflow: "
|
Diag(Loc, DL_Error, ET, "%0 integer overflow: "
|
||||||
"%1 %2 %3 cannot be represented in type %4")
|
"%1 %2 %3 cannot be represented in type %4")
|
||||||
<< (IsSigned ? "signed" : "unsigned")
|
<< (IsSigned ? "signed" : "unsigned") << Value(Data->Type, LHS)
|
||||||
<< Value(Data->Type, LHS) << Operator << RHS << Data->Type;
|
<< Operator << RHS << Data->Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \
|
#define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \
|
||||||
|
|
@ -154,12 +155,12 @@ static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal,
|
||||||
ScopedReport R(Opts, Loc, ET);
|
ScopedReport R(Opts, Loc, ET);
|
||||||
|
|
||||||
if (IsSigned)
|
if (IsSigned)
|
||||||
Diag(Loc, DL_Error,
|
Diag(Loc, DL_Error, ET,
|
||||||
"negation of %0 cannot be represented in type %1; "
|
"negation of %0 cannot be represented in type %1; "
|
||||||
"cast to an unsigned type to negate this value to itself")
|
"cast to an unsigned type to negate this value to itself")
|
||||||
<< Value(Data->Type, OldVal) << Data->Type;
|
<< Value(Data->Type, OldVal) << Data->Type;
|
||||||
else
|
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;
|
<< Value(Data->Type, OldVal) << Data->Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,11 +197,12 @@ static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
|
||||||
|
|
||||||
switch (ET) {
|
switch (ET) {
|
||||||
case ErrorType::SignedIntegerOverflow:
|
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;
|
<< LHSVal << Data->Type;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Diag(Loc, DL_Error, "division by zero");
|
Diag(Loc, DL_Error, ET, "division by zero");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -239,15 +241,16 @@ static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
|
||||||
|
|
||||||
if (ET == ErrorType::InvalidShiftExponent) {
|
if (ET == ErrorType::InvalidShiftExponent) {
|
||||||
if (RHSVal.isNegative())
|
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
|
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;
|
<< RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
|
||||||
} else {
|
} else {
|
||||||
if (LHSVal.isNegative())
|
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
|
else
|
||||||
Diag(Loc, DL_Error,
|
Diag(Loc, DL_Error, ET,
|
||||||
"left shift of %0 by %1 places cannot be represented in type %2")
|
"left shift of %0 by %1 places cannot be represented in type %2")
|
||||||
<< LHSVal << RHSVal << Data->LHSType;
|
<< LHSVal << RHSVal << Data->LHSType;
|
||||||
}
|
}
|
||||||
|
|
@ -279,7 +282,7 @@ static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index,
|
||||||
ScopedReport R(Opts, Loc, ET);
|
ScopedReport R(Opts, Loc, ET);
|
||||||
|
|
||||||
Value IndexVal(Data->IndexType, Index);
|
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;
|
<< IndexVal << Data->ArrayType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -297,8 +300,10 @@ void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data,
|
||||||
|
|
||||||
static void handleBuiltinUnreachableImpl(UnreachableData *Data,
|
static void handleBuiltinUnreachableImpl(UnreachableData *Data,
|
||||||
ReportOptions Opts) {
|
ReportOptions Opts) {
|
||||||
ScopedReport R(Opts, Data->Loc, ErrorType::UnreachableCall);
|
ErrorType ET = ErrorType::UnreachableCall;
|
||||||
Diag(Data->Loc, DL_Error, "execution reached an unreachable program point");
|
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) {
|
void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) {
|
||||||
|
|
@ -308,8 +313,9 @@ void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) {
|
static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) {
|
||||||
ScopedReport R(Opts, Data->Loc, ErrorType::MissingReturn);
|
ErrorType ET = ErrorType::MissingReturn;
|
||||||
Diag(Data->Loc, DL_Error,
|
ScopedReport R(Opts, Data->Loc, ET);
|
||||||
|
Diag(Data->Loc, DL_Error, ET,
|
||||||
"execution reached the end of a value-returning function "
|
"execution reached the end of a value-returning function "
|
||||||
"without returning a value");
|
"without returning a value");
|
||||||
}
|
}
|
||||||
|
|
@ -330,7 +336,7 @@ static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
|
||||||
|
|
||||||
ScopedReport R(Opts, Loc, ET);
|
ScopedReport R(Opts, Loc, ET);
|
||||||
|
|
||||||
Diag(Loc, DL_Error, "variable length array bound evaluates to "
|
Diag(Loc, DL_Error, ET, "variable length array bound evaluates to "
|
||||||
"non-positive value %0")
|
"non-positive value %0")
|
||||||
<< Value(Data->Type, Bound);
|
<< Value(Data->Type, Bound);
|
||||||
}
|
}
|
||||||
|
|
@ -390,7 +396,7 @@ static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
|
||||||
|
|
||||||
ScopedReport R(Opts, Loc, ET);
|
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")
|
"%0 is outside the range of representable values of type %2")
|
||||||
<< Value(*FromType, From) << *FromType << *ToType;
|
<< Value(*FromType, From) << *FromType << *ToType;
|
||||||
}
|
}
|
||||||
|
|
@ -421,7 +427,7 @@ static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val,
|
||||||
|
|
||||||
ScopedReport R(Opts, Loc, ET);
|
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")
|
"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;
|
||||||
}
|
}
|
||||||
|
|
@ -447,7 +453,7 @@ static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) {
|
||||||
|
|
||||||
ScopedReport R(Opts, Loc, ET);
|
ScopedReport R(Opts, Loc, ET);
|
||||||
|
|
||||||
Diag(Loc, DL_Error,
|
Diag(Loc, DL_Error, ET,
|
||||||
"passing zero to %0, which is not a valid argument")
|
"passing zero to %0, which is not a valid argument")
|
||||||
<< ((Data->Kind == BCK_CTZPassedZero) ? "ctz()" : "clz()");
|
<< ((Data->Kind == BCK_CTZPassedZero) ? "ctz()" : "clz()");
|
||||||
}
|
}
|
||||||
|
|
@ -478,10 +484,10 @@ static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
|
||||||
if (!FName)
|
if (!FName)
|
||||||
FName = "(unknown)";
|
FName = "(unknown)";
|
||||||
|
|
||||||
Diag(CallLoc, DL_Error,
|
Diag(CallLoc, DL_Error, ET,
|
||||||
"call to function %0 through pointer to incorrect function type %1")
|
"call to function %0 through pointer to incorrect function type %1")
|
||||||
<< FName << Data->Type;
|
<< FName << Data->Type;
|
||||||
Diag(FLoc, DL_Note, "%0 defined here") << FName;
|
Diag(FLoc, DL_Note, ET, "%0 defined here") << FName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -511,10 +517,10 @@ static void handleNonNullReturn(NonNullReturnData *Data, SourceLocation *LocPtr,
|
||||||
|
|
||||||
ScopedReport R(Opts, Loc, ET);
|
ScopedReport R(Opts, Loc, ET);
|
||||||
|
|
||||||
Diag(Loc, DL_Error, "null pointer returned from function declared to never "
|
Diag(Loc, DL_Error, ET,
|
||||||
"return null");
|
"null pointer returned from function declared to never return null");
|
||||||
if (!Data->AttrLoc.isInvalid())
|
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"
|
<< (IsAttr ? "returns_nonnull attribute"
|
||||||
: "_Nonnull return type annotation");
|
: "_Nonnull return type annotation");
|
||||||
}
|
}
|
||||||
|
|
@ -555,12 +561,12 @@ static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts,
|
||||||
|
|
||||||
ScopedReport R(Opts, Loc, ET);
|
ScopedReport R(Opts, Loc, ET);
|
||||||
|
|
||||||
Diag(Loc, DL_Error,
|
Diag(Loc, DL_Error, ET,
|
||||||
"null pointer passed as argument %0, which is declared to "
|
"null pointer passed as argument %0, which is declared to "
|
||||||
"never be null")
|
"never be null")
|
||||||
<< Data->ArgIndex;
|
<< Data->ArgIndex;
|
||||||
if (!Data->AttrLoc.isInvalid())
|
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");
|
<< (IsAttr ? "nonnull attribute" : "_Nonnull type annotation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -600,14 +606,15 @@ static void handlePointerOverflowImpl(PointerOverflowData *Data,
|
||||||
|
|
||||||
if ((sptr(Base) >= 0) == (sptr(Result) >= 0)) {
|
if ((sptr(Base) >= 0) == (sptr(Result) >= 0)) {
|
||||||
if (Base > Result)
|
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;
|
<< (void *)Base << (void *)Result;
|
||||||
else
|
else
|
||||||
Diag(Loc, DL_Error,
|
Diag(Loc, DL_Error, ET,
|
||||||
"subtraction of unsigned offset from %0 overflowed to %1")
|
"subtraction of unsigned offset from %0 overflowed to %1")
|
||||||
<< (void *)Base << (void *)Result;
|
<< (void *)Base << (void *)Result;
|
||||||
} else {
|
} else {
|
||||||
Diag(Loc, DL_Error,
|
Diag(Loc, DL_Error, ET,
|
||||||
"pointer index expression with base %0 overflowed to %1")
|
"pointer index expression with base %0 overflowed to %1")
|
||||||
<< (void *)Base << (void *)Result;
|
<< (void *)Base << (void *)Result;
|
||||||
}
|
}
|
||||||
|
|
@ -641,7 +648,8 @@ static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function,
|
||||||
|
|
||||||
ScopedReport R(Opts, Loc, ET);
|
ScopedReport R(Opts, Loc, ET);
|
||||||
|
|
||||||
Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
|
Diag(Loc, DL_Error, ET,
|
||||||
|
"control flow integrity check for type %0 failed during "
|
||||||
"indirect function call")
|
"indirect function call")
|
||||||
<< Data->Type;
|
<< Data->Type;
|
||||||
|
|
||||||
|
|
@ -649,7 +657,7 @@ static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function,
|
||||||
const char *FName = FLoc.get()->info.function;
|
const char *FName = FLoc.get()->info.function;
|
||||||
if (!FName)
|
if (!FName)
|
||||||
FName = "(unknown)";
|
FName = "(unknown)";
|
||||||
Diag(FLoc, DL_Note, "%0 defined here") << FName;
|
Diag(FLoc, DL_Note, ET, "%0 defined here") << FName;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace __ubsan {
|
namespace __ubsan {
|
||||||
|
|
|
||||||
|
|
@ -50,29 +50,30 @@ static bool HandleDynamicTypeCacheMiss(
|
||||||
|
|
||||||
ScopedReport R(Opts, Loc, ET);
|
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")
|
"%0 address %1 which does not point to an object of type %2")
|
||||||
<< TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
|
<< TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
|
||||||
|
|
||||||
// If possible, say what type it actually points to.
|
// If possible, say what type it actually points to.
|
||||||
if (!DTI.isValid()) {
|
if (!DTI.isValid()) {
|
||||||
if (DTI.getOffset() < -VptrMaxOffsetToTop || DTI.getOffset() > VptrMaxOffsetToTop) {
|
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())
|
<< TypeName(DTI.getMostDerivedTypeName())
|
||||||
<< Range(Pointer, Pointer + sizeof(uptr), "possibly invalid vptr");
|
<< Range(Pointer, Pointer + sizeof(uptr), "possibly invalid vptr");
|
||||||
} else {
|
} else {
|
||||||
Diag(Pointer, DL_Note, "object has invalid vptr")
|
Diag(Pointer, DL_Note, ET, "object has invalid vptr")
|
||||||
<< TypeName(DTI.getMostDerivedTypeName())
|
<< TypeName(DTI.getMostDerivedTypeName())
|
||||||
<< Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
|
<< Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
|
||||||
}
|
}
|
||||||
} else if (!DTI.getOffset())
|
} 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())
|
<< TypeName(DTI.getMostDerivedTypeName())
|
||||||
<< Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
|
<< Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
|
||||||
else
|
else
|
||||||
// FIXME: Find the type at the specified offset, and include that
|
// FIXME: Find the type at the specified offset, and include that
|
||||||
// in the note.
|
// 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")
|
"object is base class subobject at offset %0 within object of type %1")
|
||||||
<< DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName())
|
<< DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName())
|
||||||
<< TypeName(DTI.getSubobjectTypeName())
|
<< TypeName(DTI.getSubobjectTypeName())
|
||||||
|
|
@ -126,7 +127,8 @@ void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
|
||||||
Die();
|
Die();
|
||||||
}
|
}
|
||||||
|
|
||||||
Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
|
Diag(Loc, DL_Error, ET,
|
||||||
|
"control flow integrity check for type %0 failed during "
|
||||||
"%1 (vtable address %2)")
|
"%1 (vtable address %2)")
|
||||||
<< Data->Type << CheckKindStr << (void *)Vtable;
|
<< Data->Type << CheckKindStr << (void *)Vtable;
|
||||||
|
|
||||||
|
|
@ -134,11 +136,11 @@ void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
|
||||||
if (!DTI.isValid()) {
|
if (!DTI.isValid()) {
|
||||||
const char *module = Symbolizer::GetOrInit()->GetModuleNameForPc(Vtable);
|
const char *module = Symbolizer::GetOrInit()->GetModuleNameForPc(Vtable);
|
||||||
if (module)
|
if (module)
|
||||||
Diag(Vtable, DL_Note, "invalid vtable in module %0") << module;
|
Diag(Vtable, DL_Note, ET, "invalid vtable in module %0") << module;
|
||||||
else
|
else
|
||||||
Diag(Vtable, DL_Note, "invalid vtable");
|
Diag(Vtable, DL_Note, ET, "invalid vtable");
|
||||||
} else {
|
} else {
|
||||||
Diag(Vtable, DL_Note, "vtable is of type %0")
|
Diag(Vtable, DL_Note, ET, "vtable is of type %0")
|
||||||
<< TypeName(DTI.getMostDerivedTypeName());
|
<< TypeName(DTI.getMostDerivedTypeName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,3 +52,5 @@ INTERFACE_FUNCTION(__ubsan_handle_type_mismatch_v1_abort)
|
||||||
INTERFACE_FUNCTION(__ubsan_handle_vla_bound_not_positive)
|
INTERFACE_FUNCTION(__ubsan_handle_vla_bound_not_positive)
|
||||||
INTERFACE_FUNCTION(__ubsan_handle_vla_bound_not_positive_abort)
|
INTERFACE_FUNCTION(__ubsan_handle_vla_bound_not_positive_abort)
|
||||||
INTERFACE_WEAK_FUNCTION(__ubsan_default_options)
|
INTERFACE_WEAK_FUNCTION(__ubsan_default_options)
|
||||||
|
INTERFACE_WEAK_FUNCTION(__ubsan_on_report)
|
||||||
|
INTERFACE_FUNCTION(__ubsan_get_current_report_data)
|
||||||
|
|
|
||||||
|
|
@ -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 = "<unknown>";
|
||||||
|
*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;
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// RUN: %clangxx -w -fsanitize=bool %s -o %t
|
||||||
|
// RUN: %run %t 2>&1 | FileCheck %s
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue