Index: lib/ubsan/ubsan_checks.inc =================================================================== --- /dev/null +++ lib/ubsan/ubsan_checks.inc @@ -0,0 +1,53 @@ +//===-- ubsan_checks.inc ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// List of checks handled by UBSan runtime. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_CHECK +# error "Define UBSAN_CHECK prior to including this file!" +#endif + +// UBSAN_CHECK(Name, SummaryKind, FlagName) +// SummaryKind and FlagName should be string literals. + +UBSAN_CHECK(GenericUB, "undefined-behavior", "-fsanitize=undefined") +UBSAN_CHECK(NullPointerUse, "null-pointer-use", "-fsanitize=null") +UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", + "-fsanitize=alignment") +UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size", + "-fsanitize=object-size") +UBSAN_CHECK(SignedIntegerOverflow, "signed-integer-overflow", + "-fsanitize=signed-integer-overflow") +UBSAN_CHECK(UnsignedIntegerOverflow, "unsigned-integer-overflow", + "-fsanitize=unsigned-integer-overflow") +UBSAN_CHECK(IntegerDivideByZero, "integer-divide-by-zero", + "-fsanitize=integer-divide-by-zero") +UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero", + "-fsanitize=float-divide-by-zero") +UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "-fsanitize=shift-base") +UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent", + "-fsanitize=shift-exponent") +UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "-fsanitize=bounds") +UBSAN_CHECK(UnreachableCall, "unreachable-call", "-fsanitize=unreachable") +UBSAN_CHECK(MissingReturn, "missing-return", "-fsanitize=return") +UBSAN_CHECK(NonPositiveVLAIndex, "non-positive-vla-index", + "-fsanitize=vla-bound") +UBSAN_CHECK(FloatCastOverflow, "float-cast-overflow", + "-fsanitize=float-cast-overflow") +UBSAN_CHECK(InvalidBoolLoad, "invalid-bool-load", "-fsanitize=bool") +UBSAN_CHECK(InvalidEnumLoad, "invalid-enum-load", "-fsanitize=enum") +UBSAN_CHECK(FunctionTypeMismatch, "function-type-mismatch", + "-fsanitize=function") +UBSAN_CHECK(InvalidNullReturn, "invalid-null-return", + "-fsanitize=returns-nonnull-attribute") +UBSAN_CHECK(InvalidNullArgument, "invalid-null-argument", + "-fsanitize=nonnull-attribute") +UBSAN_CHECK(DynamicTypeMismatch, "dynamic-type-mismatch", "-fsanitize=vptr") +UBSAN_CHECK(CFIBadType, "cfi-bad-type", "-fsanitize=cfi") Index: lib/ubsan/ubsan_diag.h =================================================================== --- lib/ubsan/ubsan_diag.h +++ lib/ubsan/ubsan_diag.h @@ -219,6 +219,12 @@ uptr bp; }; +enum class ErrorType { +#define UBSAN_CHECK(Name, SummaryKind, FlagName) Name, +#include "ubsan_checks.inc" +#undef UBSAN_CHECK +}; + #define GET_REPORT_OPTIONS(die_after_report) \ GET_CALLER_PC_BP; \ ReportOptions Opts = {die_after_report, pc, bp} @@ -229,9 +235,12 @@ class ScopedReport { ReportOptions Opts; Location SummaryLoc; + ErrorType Type; public: - ScopedReport(ReportOptions Opts, Location SummaryLoc); + ScopedReport(ReportOptions Opts, Location SummaryLoc, + ErrorType Type = ErrorType::GenericUB); + void setErrorType(ErrorType T) { Type = T; } ~ScopedReport(); }; Index: lib/ubsan/ubsan_diag.cc =================================================================== --- lib/ubsan/ubsan_diag.cc +++ lib/ubsan/ubsan_diag.cc @@ -43,10 +43,23 @@ stack.Print(); } -static void MaybeReportErrorSummary(Location Loc) { +static const char *ConvertTypeToString(ErrorType Type) { + switch (Type) { +#define UBSAN_CHECK(Name, SummaryKind, FlagName) \ + case ErrorType::Name: \ + return SummaryKind; +#include "ubsan_checks.inc" +#undef UBSAN_CHECK + } + UNREACHABLE("unknown ErrorType!"); +} + +static void MaybeReportErrorSummary(Location Loc, ErrorType Type) { if (!common_flags()->print_summary) return; - const char *ErrorType = "undefined-behavior"; + if (!flags()->report_error_type) + Type = ErrorType::GenericUB; + const char *ErrorKind = ConvertTypeToString(Type); if (Loc.isSourceLocation()) { SourceLocation SLoc = Loc.getSourceLocation(); if (!SLoc.isInvalid()) { @@ -55,16 +68,16 @@ AI.line = SLoc.getLine(); AI.column = SLoc.getColumn(); AI.function = internal_strdup(""); // Avoid printing ?? as function name. - ReportErrorSummary(ErrorType, AI); + ReportErrorSummary(ErrorKind, AI); AI.Clear(); return; } } else if (Loc.isSymbolizedStack()) { const AddressInfo &AI = Loc.getSymbolizedStack()->info; - ReportErrorSummary(ErrorType, AI); + ReportErrorSummary(ErrorKind, AI); return; } - ReportErrorSummary(ErrorType); + ReportErrorSummary(ErrorKind); } namespace { @@ -341,15 +354,16 @@ NumRanges, Args); } -ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc) - : Opts(Opts), SummaryLoc(SummaryLoc) { +ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc, + ErrorType Type) + : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) { InitAsStandaloneIfNecessary(); CommonSanitizerReportMutex.Lock(); } ScopedReport::~ScopedReport() { MaybePrintStackTrace(Opts.pc, Opts.bp); - MaybeReportErrorSummary(SummaryLoc); + MaybeReportErrorSummary(SummaryLoc, Type); CommonSanitizerReportMutex.Unlock(); if (Opts.DieAfterReport || flags()->halt_on_error) Die(); Index: lib/ubsan/ubsan_flags.inc =================================================================== --- lib/ubsan/ubsan_flags.inc +++ lib/ubsan/ubsan_flags.inc @@ -22,4 +22,5 @@ UBSAN_FLAG(bool, print_stacktrace, false, "Include full stacktrace into an error report") UBSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") - +UBSAN_FLAG(bool, report_error_type, false, + "Print specific error type instead of 'undefined-behavior' in summary.") Index: lib/ubsan/ubsan_handlers.cc =================================================================== --- lib/ubsan/ubsan_handlers.cc +++ lib/ubsan/ubsan_handlers.cc @@ -53,18 +53,22 @@ ScopedReport R(Opts, Loc); - if (!Pointer) + if (!Pointer) { + R.setErrorType(ErrorType::NullPointerUse); Diag(Loc, DL_Error, "%0 null pointer of type %1") << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; - else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) + } else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) { + R.setErrorType(ErrorType::MisalignedPointerUse); Diag(Loc, DL_Error, "%0 misaligned address %1 for type %3, " "which requires %2 byte alignment") << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Alignment << Data->Type; - else + } else { + R.setErrorType(ErrorType::InsufficientObjectSize); Diag(Loc, DL_Error, "%0 address %1 with insufficient space " "for an object of type %2") << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; + } if (Pointer) Diag(Pointer, DL_Note, "pointer points here"); } @@ -90,11 +94,13 @@ if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + bool IsSigned = Data->Type.isSignedIntegerTy(); + ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow + : ErrorType::UnsignedIntegerOverflow); Diag(Loc, DL_Error, "%0 integer overflow: " "%1 %2 %3 cannot be represented in type %4") - << (Data->Type.isSignedIntegerTy() ? "signed" : "unsigned") + << (IsSigned ? "signed" : "unsigned") << Value(Data->Type, LHS) << Operator << RHS << Data->Type; } @@ -119,17 +125,18 @@ if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + bool IsSigned = Data->Type.isSignedIntegerTy(); + ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow + : ErrorType::UnsignedIntegerOverflow); - if (Data->Type.isSignedIntegerTy()) + if (IsSigned) Diag(Loc, DL_Error, "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; + << Value(Data->Type, OldVal) << Data->Type; else - Diag(Loc, DL_Error, - "negation of %0 cannot be represented in type %1") - << Value(Data->Type, OldVal) << Data->Type; + Diag(Loc, DL_Error, "negation of %0 cannot be represented in type %1") + << Value(Data->Type, OldVal) << Data->Type; } void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data, @@ -154,12 +161,16 @@ Value LHSVal(Data->Type, LHS); Value RHSVal(Data->Type, RHS); - if (RHSVal.isMinusOne()) + if (RHSVal.isMinusOne()) { + R.setErrorType(ErrorType::SignedIntegerOverflow); Diag(Loc, DL_Error, "division of %0 by -1 cannot be represented in type %1") << LHSVal << Data->Type; - else + } else { + R.setErrorType(Data->Type.isIntegerTy() ? ErrorType::IntegerDivideByZero + : ErrorType::FloatDivideByZero); Diag(Loc, DL_Error, "division by zero"); + } } void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data, @@ -186,18 +197,23 @@ Value LHSVal(Data->LHSType, LHS); Value RHSVal(Data->RHSType, RHS); - if (RHSVal.isNegative()) + if (RHSVal.isNegative()) { + R.setErrorType(ErrorType::InvalidShiftExponent); Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal; - else if (RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth()) - Diag(Loc, DL_Error, - "shift exponent %0 is too large for %1-bit type %2") - << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType; - else if (LHSVal.isNegative()) + } else if (RHSVal.getPositiveIntValue() >= + Data->LHSType.getIntegerBitWidth()) { + R.setErrorType(ErrorType::InvalidShiftExponent); + Diag(Loc, DL_Error, "shift exponent %0 is too large for %1-bit type %2") + << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType; + } else if (LHSVal.isNegative()) { + R.setErrorType(ErrorType::InvalidShiftBase); Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal; - else + } else { + R.setErrorType(ErrorType::InvalidShiftBase); Diag(Loc, DL_Error, "left shift of %0 by %1 places cannot be represented in type %2") - << LHSVal << RHSVal << Data->LHSType; + << LHSVal << RHSVal << Data->LHSType; + } } void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data, @@ -221,7 +237,7 @@ if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::OutOfBoundsIndex); Value IndexVal(Data->IndexType, Index); Diag(Loc, DL_Error, "index %0 out of bounds for type %1") @@ -242,7 +258,7 @@ static void handleBuiltinUnreachableImpl(UnreachableData *Data, ReportOptions Opts) { - ScopedReport R(Opts, Data->Loc); + ScopedReport R(Opts, Data->Loc, ErrorType::UnreachableCall); Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call"); } @@ -253,7 +269,7 @@ } static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) { - ScopedReport R(Opts, Data->Loc); + ScopedReport R(Opts, Data->Loc, ErrorType::MissingReturn); Diag(Data->Loc, DL_Error, "execution reached the end of a value-returning function " "without returning a value"); @@ -271,7 +287,7 @@ if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::NonPositiveVLAIndex); Diag(Loc, DL_Error, "variable length array bound evaluates to " "non-positive value %0") @@ -328,7 +344,7 @@ ToType = &Data->ToType; } - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::FloatCastOverflow); Diag(Loc, DL_Error, "value %0 is outside the range of representable values of type %2") @@ -352,7 +368,11 @@ if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + // 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); Diag(Loc, DL_Error, "load of value %0, which is not a valid value for type %1") @@ -378,7 +398,7 @@ if (ignoreReport(CallLoc, Opts)) return; - ScopedReport R(Opts, CallLoc); + ScopedReport R(Opts, CallLoc, ErrorType::FunctionTypeMismatch); SymbolizedStackHolder FLoc(getSymbolizedLocation(Function)); const char *FName = FLoc.get()->info.function; @@ -410,7 +430,7 @@ if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::InvalidNullReturn); Diag(Loc, DL_Error, "null pointer returned from function declared to never " "return null"); @@ -434,7 +454,7 @@ if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::InvalidNullArgument); Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to " "never be null") << Data->ArgIndex; Index: lib/ubsan/ubsan_handlers_cxx.cc =================================================================== --- lib/ubsan/ubsan_handlers_cxx.cc +++ lib/ubsan/ubsan_handlers_cxx.cc @@ -45,7 +45,7 @@ if (Loc.isDisabled()) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::DynamicTypeMismatch); Diag(Loc, DL_Error, "%0 address %1 which does not point to an object of type %2") @@ -85,7 +85,7 @@ static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable, ReportOptions Opts) { SourceLocation Loc = Data->Loc.acquire(); - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::CFIBadType); DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void*)Vtable); static const char *TypeCheckKinds[] = { Index: test/ubsan/TestCases/Float/cast-overflow.cpp =================================================================== --- test/ubsan/TestCases/Float/cast-overflow.cpp +++ test/ubsan/TestCases/Float/cast-overflow.cpp @@ -1,6 +1,6 @@ // RUN: %clangxx -fsanitize=float-cast-overflow %s -o %t // RUN: %run %t _ -// RUN: env UBSAN_OPTIONS=print_summary=1 %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0 +// RUN: env UBSAN_OPTIONS=print_summary=1:report_error_type=1 %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0 // RUN: %run %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-1 // RUN: %run %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-2 // RUN: %run %t 3 2>&1 | FileCheck %s --check-prefix=CHECK-3 @@ -88,7 +88,7 @@ // successfully round-trip, depending on the rounding mode. // CHECK-0: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: value 2.14748{{.*}} is outside the range of representable values of type 'int' static int test_int = MaxFloatRepresentableAsInt + 0x80; - // CHECK-0: SUMMARY: {{.*}}Sanitizer: undefined-behavior {{.*}}cast-overflow.cpp:[[@LINE-1]] + // CHECK-0: SUMMARY: {{.*}}Sanitizer: float-cast-overflow {{.*}}cast-overflow.cpp:[[@LINE-1]] return 0; } case '1': { Index: test/ubsan/TestCases/Integer/summary.cpp =================================================================== --- test/ubsan/TestCases/Integer/summary.cpp +++ test/ubsan/TestCases/Integer/summary.cpp @@ -1,10 +1,13 @@ -// RUN: %clangxx -fsanitize=integer %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer %s -o %t +// RUN: %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOTYPE +// RUN: env UBSAN_OPTIONS=report_error_type=1 %t 2>&1 | FileCheck %s --check-prefix=CHECK-TYPE // REQUIRES: ubsan-asan #include int main() { (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); - // CHECK: SUMMARY: AddressSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:44 + // CHECK-NOTYPE: SUMMARY: AddressSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:44 + // CHECK-TYPE: SUMMARY: AddressSanitizer: unsigned-integer-overflow {{.*}}summary.cpp:[[@LINE-2]]:44 return 0; } Index: test/ubsan/TestCases/Misc/bool.cpp =================================================================== --- test/ubsan/TestCases/Misc/bool.cpp +++ test/ubsan/TestCases/Misc/bool.cpp @@ -1,10 +1,13 @@ -// RUN: %clangxx -fsanitize=bool %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=bool %s -O3 -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: env UBSAN_OPTIONS=print_summary=1:report_error_type=1 not %run %t 2>&1 | FileCheck %s --check-prefix=SUMMARY unsigned char NotABool = 123; int main(int argc, char **argv) { bool *p = (bool*)&NotABool; - // CHECK: bool.cpp:9:10: runtime error: load of value 123, which is not a valid value for type 'bool' + // CHECK: bool.cpp:[[@LINE+1]]:10: runtime error: load of value 123, which is not a valid value for type 'bool' return *p; + // SUMMARY: SUMMARY: {{.*}}Sanitizer: invalid-bool-load {{.*}}bool.cpp:[[@LINE-1]] }