[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_flags.cc
|
||||
ubsan_handlers.cc
|
||||
ubsan_monitor.cc
|
||||
ubsan_value.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 @@ Diag::~Diag() {
|
|||
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(":");
|
||||
|
|
|
|||
|
|
@ -121,6 +121,12 @@ public:
|
|||
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 @@ class Diag {
|
|||
/// 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 @@ private:
|
|||
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 @@ struct ReportOptions {
|
|||
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) \
|
||||
|
|
|
|||
|
|
@ -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 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
|
|||
|
||||
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 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle 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,
|
||||
|
|
@ -119,10 +120,10 @@ static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
|
|||
|
||||
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")
|
||||
<< (IsSigned ? "signed" : "unsigned")
|
||||
<< Value(Data->Type, LHS) << Operator << RHS << Data->Type;
|
||||
<< (IsSigned ? "signed" : "unsigned") << Value(Data->Type, LHS)
|
||||
<< Operator << RHS << Data->Type;
|
||||
}
|
||||
|
||||
#define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \
|
||||
|
|
@ -154,12 +155,12 @@ static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal,
|
|||
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 @@ static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
|
|||
|
||||
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 @@ static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
|
|||
|
||||
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 @@ static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index,
|
|||
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 @@ void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data,
|
|||
|
||||
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 @@ void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) {
|
|||
}
|
||||
|
||||
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,7 +336,7 @@ static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
|
|||
|
||||
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")
|
||||
<< Value(Data->Type, Bound);
|
||||
}
|
||||
|
|
@ -390,7 +396,7 @@ static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
|
|||
|
||||
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,7 +427,7 @@ static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val,
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
@ -447,7 +453,7 @@ static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) {
|
|||
|
||||
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 @@ static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
|
|||
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 @@ static void handleNonNullReturn(NonNullReturnData *Data, SourceLocation *LocPtr,
|
|||
|
||||
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 @@ static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts,
|
|||
|
||||
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 @@ static void handlePointerOverflowImpl(PointerOverflowData *Data,
|
|||
|
||||
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,7 +648,8 @@ static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function,
|
|||
|
||||
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")
|
||||
<< Data->Type;
|
||||
|
||||
|
|
@ -649,7 +657,7 @@ static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle 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 {
|
||||
|
|
|
|||
|
|
@ -50,29 +50,30 @@ static bool HandleDynamicTypeCacheMiss(
|
|||
|
||||
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,7 +127,8 @@ void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
|
|||
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)")
|
||||
<< Data->Type << CheckKindStr << (void *)Vtable;
|
||||
|
||||
|
|
@ -134,11 +136,11 @@ void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
|
|||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_abort)
|
||||
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