Skip to content

Commit

Permalink
[UBSan] Implement runtime suppressions (PR25066).
Browse files Browse the repository at this point in the history
Summary:
Add the ability to suppress UBSan reports for files/functions/modules
at runtime. The user can now pass UBSAN_OPTIONS=suppressions=supp.txt
with the contents of the form:

signed-integer-overflow:file-with-known-overflow.cpp
alignment:function_doing_unaligned_access
vptr:shared_object_with_vptr_failures.so

Suppression categories match the arguments passed to -fsanitize=
flag (although, see below). There is no overhead if suppressions are
not provided. Otherwise there is extra overhead for symbolization.

Limitations:
1) sometimes suppressions need debug info / symbol table to function
   properly (although sometimes frontend generates enough info to
   do the match).
2) it's only possible to suppress recoverable UB kinds - if you've
   built the code with -fno-sanitize-recover=undefined, suppressions
   will not work.
3) categories are fine-grained check kinds, not groups like "undefined"
   or "integer", so you can't write "undefined:file_with_ub.cc".

Reviewers: rsmith, kcc

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D15363

llvm-svn: 256018
  • Loading branch information
vonosmas committed Dec 18, 2015
1 parent 9805253 commit 081a24e
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 51 deletions.
2 changes: 1 addition & 1 deletion compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h
Expand Up @@ -43,7 +43,7 @@ class SuppressionContext {
void GetMatched(InternalMmapVector<Suppression *> *matched);

private:
static const int kMaxSuppressionTypes = 16;
static const int kMaxSuppressionTypes = 32;
const char **const suppression_types_;
const int suppression_types_num_;

Expand Down
42 changes: 41 additions & 1 deletion compiler-rt/lib/ubsan/ubsan_diag.cc
Expand Up @@ -54,6 +54,17 @@ static const char *ConvertTypeToString(ErrorType Type) {
UNREACHABLE("unknown ErrorType!");
}

static const char *ConvertTypeToFlagName(ErrorType Type) {
switch (Type) {
#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \
case ErrorType::Name: \
return FSanitizeFlagName;
#include "ubsan_checks.inc"
#undef UBSAN_CHECK
}
UNREACHABLE("unknown ErrorType!");
}

static void MaybeReportErrorSummary(Location Loc, ErrorType Type) {
if (!common_flags()->print_summary)
return;
Expand Down Expand Up @@ -372,7 +383,12 @@ ScopedReport::~ScopedReport() {
ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
static SuppressionContext *suppression_ctx = nullptr;
static const char kVptrCheck[] = "vptr_check";
static const char *kSuppressionTypes[] = { kVptrCheck };
static const char *kSuppressionTypes[] = {
#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName,
#include "ubsan_checks.inc"
#undef UBSAN_CHECK
kVptrCheck,
};

void __ubsan::InitializeSuppressions() {
CHECK_EQ(nullptr, suppression_ctx);
Expand All @@ -388,4 +404,28 @@ bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
return suppression_ctx->Match(TypeName, kVptrCheck, &s);
}

bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) {
InitAsStandaloneIfNecessary();
CHECK(suppression_ctx);
const char *SuppType = ConvertTypeToFlagName(ET);
// Fast path: don't symbolize PC if there is no suppressions for given UB
// type.
if (!suppression_ctx->HasSuppressionType(SuppType))
return false;
Suppression *s = nullptr;
// Suppress by file name known to runtime.
if (Filename != nullptr && suppression_ctx->Match(Filename, SuppType, &s))
return true;
// Suppress by module name.
if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(PC)) {
if (suppression_ctx->Match(Module, SuppType, &s))
return true;
}
// Suppress by function or source file name from debug info.
SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC));
const AddressInfo &AI = Stack.get()->info;
return suppression_ctx->Match(AI.function, SuppType, &s) ||
suppression_ctx->Match(AI.file, SuppType, &s);
}

#endif // CAN_SANITIZE_UB
5 changes: 4 additions & 1 deletion compiler-rt/lib/ubsan/ubsan_diag.h
Expand Up @@ -225,7 +225,7 @@ enum class ErrorType {
#undef UBSAN_CHECK
};

bool ignoreReport(SourceLocation SLoc, ReportOptions Opts);
bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET);

#define GET_REPORT_OPTIONS(unrecoverable_handler) \
GET_CALLER_PC_BP; \
Expand All @@ -246,6 +246,9 @@ class ScopedReport {

void InitializeSuppressions();
bool IsVptrCheckSuppressed(const char *TypeName);
// Sometimes UBSan runtime can know filename from handlers arguments, even if
// debug info is missing.
bool IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename);

} // namespace __ubsan

Expand Down
109 changes: 65 additions & 44 deletions compiler-rt/lib/ubsan/ubsan_handlers.cc
Expand Up @@ -22,7 +22,7 @@ using namespace __sanitizer;
using namespace __ubsan;

namespace __ubsan {
bool ignoreReport(SourceLocation SLoc, ReportOptions Opts) {
bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET) {
// We are not allowed to skip error report: if we are in unrecoverable
// handler, we have to terminate the program right now, and therefore
// have to print some diagnostic.
Expand All @@ -32,7 +32,7 @@ bool ignoreReport(SourceLocation SLoc, ReportOptions Opts) {
// thread could have acquired it, but not yet printed the report.
if (Opts.FromUnrecoverableHandler)
return false;
return SLoc.isDisabled();
return SLoc.isDisabled() || IsPCSuppressed(ET, Opts.pc, SLoc.getFilename());
}

const char *TypeCheckKinds[] = {
Expand All @@ -44,15 +44,6 @@ const char *TypeCheckKinds[] = {
static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
ReportOptions Opts) {
Location Loc = Data->Loc.acquire();
// Use the SourceLocation from Data to track deduplication, even if 'invalid'
if (ignoreReport(Loc.getSourceLocation(), Opts))
return;

SymbolizedStackHolder FallbackLoc;
if (Data->Loc.isInvalid()) {
FallbackLoc.reset(getCallerLocation(Opts.pc));
Loc = FallbackLoc;
}

ErrorType ET;
if (!Pointer)
Expand All @@ -62,6 +53,17 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
else
ET = ErrorType::InsufficientObjectSize;

// Use the SourceLocation from Data to track deduplication, even if it's
// invalid.
if (ignoreReport(Loc.getSourceLocation(), Opts, ET))
return;

SymbolizedStackHolder FallbackLoc;
if (Data->Loc.isInvalid()) {
FallbackLoc.reset(getCallerLocation(Opts.pc));
Loc = FallbackLoc;
}

ScopedReport R(Opts, Loc, ET);

switch (ET) {
Expand Down Expand Up @@ -106,12 +108,14 @@ static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
const char *Operator, T RHS,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
if (ignoreReport(Loc, Opts))
bool IsSigned = Data->Type.isSignedIntegerTy();
ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
: ErrorType::UnsignedIntegerOverflow;

if (ignoreReport(Loc, Opts, ET))
return;

bool IsSigned = Data->Type.isSignedIntegerTy();
ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow
: ErrorType::UnsignedIntegerOverflow);
ScopedReport R(Opts, Loc, ET);

Diag(Loc, DL_Error, "%0 integer overflow: "
"%1 %2 %3 cannot be represented in type %4")
Expand All @@ -138,12 +142,14 @@ UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort, "*", true)
static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
if (ignoreReport(Loc, Opts))
bool IsSigned = Data->Type.isSignedIntegerTy();
ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
: ErrorType::UnsignedIntegerOverflow;

if (ignoreReport(Loc, Opts, ET))
return;

bool IsSigned = Data->Type.isSignedIntegerTy();
ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow
: ErrorType::UnsignedIntegerOverflow);
ScopedReport R(Opts, Loc, ET);

if (IsSigned)
Diag(Loc, DL_Error,
Expand All @@ -170,9 +176,6 @@ void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data,
static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
ValueHandle RHS, ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
if (ignoreReport(Loc, Opts))
return;

Value LHSVal(Data->Type, LHS);
Value RHSVal(Data->Type, RHS);

Expand All @@ -184,6 +187,9 @@ static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
else
ET = ErrorType::FloatDivideByZero;

if (ignoreReport(Loc, Opts, ET))
return;

ScopedReport R(Opts, Loc, ET);

switch (ET) {
Expand Down Expand Up @@ -214,9 +220,6 @@ static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
ValueHandle LHS, ValueHandle RHS,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
if (ignoreReport(Loc, Opts))
return;

Value LHSVal(Data->LHSType, LHS);
Value RHSVal(Data->RHSType, RHS);

Expand All @@ -227,6 +230,9 @@ static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
else
ET = ErrorType::InvalidShiftBase;

if (ignoreReport(Loc, Opts, ET))
return;

ScopedReport R(Opts, Loc, ET);

if (ET == ErrorType::InvalidShiftExponent) {
Expand Down Expand Up @@ -263,10 +269,12 @@ void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
if (ignoreReport(Loc, Opts))
ErrorType ET = ErrorType::OutOfBoundsIndex;

if (ignoreReport(Loc, Opts, ET))
return;

ScopedReport R(Opts, Loc, ErrorType::OutOfBoundsIndex);
ScopedReport R(Opts, Loc, ET);

Value IndexVal(Data->IndexType, Index);
Diag(Loc, DL_Error, "index %0 out of bounds for type %1")
Expand Down Expand Up @@ -313,10 +321,12 @@ void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
if (ignoreReport(Loc, Opts))
ErrorType ET = ErrorType::NonPositiveVLAIndex;

if (ignoreReport(Loc, Opts, ET))
return;

ScopedReport R(Opts, Loc, ErrorType::NonPositiveVLAIndex);
ScopedReport R(Opts, Loc, ET);

Diag(Loc, DL_Error, "variable length array bound evaluates to "
"non-positive value %0")
Expand Down Expand Up @@ -358,6 +368,7 @@ static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
SymbolizedStackHolder CallerLoc;
Location Loc;
const TypeDescriptor *FromType, *ToType;
ErrorType ET = ErrorType::FloatCastOverflow;

if (looksLikeFloatCastOverflowDataV1(DataPtr)) {
auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr);
Expand All @@ -368,14 +379,14 @@ static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
} else {
auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr);
SourceLocation SLoc = Data->Loc.acquire();
if (ignoreReport(SLoc, Opts))
if (ignoreReport(SLoc, Opts, ET))
return;
Loc = SLoc;
FromType = &Data->FromType;
ToType = &Data->ToType;
}

ScopedReport R(Opts, Loc, ErrorType::FloatCastOverflow);
ScopedReport R(Opts, Loc, ET);

Diag(Loc, DL_Error,
"value %0 is outside the range of representable values of type %2")
Expand All @@ -396,14 +407,16 @@ void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data,
static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
if (ignoreReport(Loc, Opts))
return;

// This check could be more precise if we used different handlers for
// -fsanitize=bool and -fsanitize=enum.
bool IsBool = (0 == internal_strcmp(Data->Type.getTypeName(), "'bool'"));
ScopedReport R(Opts, Loc, IsBool ? ErrorType::InvalidBoolLoad
: ErrorType::InvalidEnumLoad);
ErrorType ET =
IsBool ? ErrorType::InvalidBoolLoad : ErrorType::InvalidEnumLoad;

if (ignoreReport(Loc, Opts, ET))
return;

ScopedReport R(Opts, Loc, ET);

Diag(Loc, DL_Error,
"load of value %0, which is not a valid value for type %1")
Expand All @@ -426,10 +439,12 @@ static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
ValueHandle Function,
ReportOptions Opts) {
SourceLocation CallLoc = Data->Loc.acquire();
if (ignoreReport(CallLoc, Opts))
ErrorType ET = ErrorType::FunctionTypeMismatch;

if (ignoreReport(CallLoc, Opts, ET))
return;

ScopedReport R(Opts, CallLoc, ErrorType::FunctionTypeMismatch);
ScopedReport R(Opts, CallLoc, ET);

SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
const char *FName = FLoc.get()->info.function;
Expand Down Expand Up @@ -458,10 +473,12 @@ void __ubsan::__ubsan_handle_function_type_mismatch_abort(

static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
if (ignoreReport(Loc, Opts))
ErrorType ET = ErrorType::InvalidNullReturn;

if (ignoreReport(Loc, Opts, ET))
return;

ScopedReport R(Opts, Loc, ErrorType::InvalidNullReturn);
ScopedReport R(Opts, Loc, ET);

Diag(Loc, DL_Error, "null pointer returned from function declared to never "
"return null");
Expand All @@ -482,10 +499,12 @@ void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) {

static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
if (ignoreReport(Loc, Opts))
ErrorType ET = ErrorType::InvalidNullArgument;

if (ignoreReport(Loc, Opts, ET))
return;

ScopedReport R(Opts, Loc, ErrorType::InvalidNullArgument);
ScopedReport R(Opts, Loc, ET);

Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to "
"never be null") << Data->ArgIndex;
Expand All @@ -507,10 +526,12 @@ void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
static void handleCFIBadIcall(CFIBadIcallData *Data, ValueHandle Function,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
if (ignoreReport(Loc, Opts))
ErrorType ET = ErrorType::CFIBadType;

if (ignoreReport(Loc, Opts, ET))
return;

ScopedReport R(Opts, Loc, ErrorType::CFIBadType);
ScopedReport R(Opts, Loc, ET);

Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
"indirect function call")
Expand Down
11 changes: 7 additions & 4 deletions compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc
Expand Up @@ -43,10 +43,11 @@ static bool HandleDynamicTypeCacheMiss(
return false;

SourceLocation Loc = Data->Loc.acquire();
if (Loc.isDisabled())
ErrorType ET = ErrorType::DynamicTypeMismatch;
if (ignoreReport(Loc, Opts, ET))
return false;

ScopedReport R(Opts, Loc, ErrorType::DynamicTypeMismatch);
ScopedReport R(Opts, Loc, ET);

Diag(Loc, DL_Error,
"%0 address %1 which does not point to an object of type %2")
Expand Down Expand Up @@ -89,10 +90,12 @@ void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
ErrorType ET = ErrorType::CFIBadType;

if (ignoreReport(Loc, Opts))
if (ignoreReport(Loc, Opts, ET))
return;
ScopedReport R(Opts, Loc, ErrorType::CFIBadType);

ScopedReport R(Opts, Loc, ET);
DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void*)Vtable);

static const char *TypeCheckKinds[] = {
Expand Down

0 comments on commit 081a24e

Please sign in to comment.