diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-12-18 06:30:32 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-12-18 06:30:32 +0000 |
commit | 25ee97fe8ada76755c8bd1087fac9cc3cd03b28c (patch) | |
tree | 7eb8ac2d90d4635d1c727ebd835c7aa4c90d290b /lib/ubsan | |
parent | 01e9a38b8cfe8967efb259978b754c3a9f0c380c (diff) | |
download | compiler-rt-25ee97fe8ada76755c8bd1087fac9cc3cd03b28c.tar.gz |
ubsan: When diagnosing something wrong somewhere in memory, emit a note
pointing at the bad location and a snippet of nearby memory values. This is
strictly best-effort; reading these bytes to display the note could lead to a
seg fault, and that's explicitly OK.
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@170415 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/ubsan')
-rw-r--r-- | lib/ubsan/lit_tests/TypeCheck/misaligned.cpp | 49 | ||||
-rw-r--r-- | lib/ubsan/lit_tests/TypeCheck/vptr.cpp | 44 | ||||
-rw-r--r-- | lib/ubsan/ubsan_diag.cc | 154 | ||||
-rw-r--r-- | lib/ubsan/ubsan_diag.h | 41 | ||||
-rw-r--r-- | lib/ubsan/ubsan_handlers.cc | 71 | ||||
-rw-r--r-- | lib/ubsan/ubsan_handlers_cxx.cc | 42 | ||||
-rw-r--r-- | lib/ubsan/ubsan_type_hash.cc | 22 | ||||
-rw-r--r-- | lib/ubsan/ubsan_type_hash.h | 21 |
8 files changed, 348 insertions, 96 deletions
diff --git a/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp b/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp index 448ae81fa..3abacae8b 100644 --- a/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp +++ b/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp @@ -1,6 +1,6 @@ // RUN: %clang -fsanitize=alignment %s -O3 -o %t // RUN: %t l0 && %t s0 && %t r0 && %t m0 && %t f0 && %t n0 -// RUN: %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD +// RUN: %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --strict-whitespace // RUN: %t s1 2>&1 | FileCheck %s --check-prefix=CHECK-STORE // RUN: %t r1 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE // RUN: %t m1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER @@ -16,35 +16,58 @@ struct S { }; int main(int, char **argv) { - char c[5] __attribute__((aligned(4))) = {}; + char c[] __attribute__((aligned(8))) = { 0, 0, 0, 0, 1, 2, 3, 4, 5 }; // Pointer value may be unspecified here, but behavior is not undefined. - int *p = (int*)&c[argv[1][1] - '0']; + int *p = (int*)&c[4 + argv[1][1] - '0']; S *s = (S*)p; (void)*p; // ok! switch (argv[1][0]) { case 'l': - // CHECK-LOAD: misaligned.cpp:[[@LINE+1]]:12: runtime error: load of misaligned address 0x{{[0-9a-f]*}} for type 'int', which requires 4 byte alignment - return *p; + // CHECK-LOAD: misaligned.cpp:[[@LINE+4]]:12: runtime error: load of misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-LOAD-NEXT: [[PTR]]: note: pointer points here + // CHECK-LOAD-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-LOAD-NEXT: {{^ \^}} + return *p && 0; + case 's': - // CHECK-STORE: misaligned.cpp:[[@LINE+1]]:5: runtime error: store to misaligned address 0x{{[0-9a-f]*}} for type 'int', which requires 4 byte alignment + // CHECK-STORE: misaligned.cpp:[[@LINE+4]]:5: runtime error: store to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-STORE-NEXT: [[PTR]]: note: pointer points here + // CHECK-STORE-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-STORE-NEXT: {{^ \^}} *p = 1; break; + case 'r': - // CHECK-REFERENCE: misaligned.cpp:[[@LINE+1]]:15: runtime error: reference binding to misaligned address 0x{{[0-9a-f]*}} for type 'int', which requires 4 byte alignment + // CHECK-REFERENCE: misaligned.cpp:[[@LINE+4]]:15: runtime error: reference binding to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-REFERENCE-NEXT: [[PTR]]: note: pointer points here + // CHECK-REFERENCE-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-REFERENCE-NEXT: {{^ \^}} {int &r = *p;} break; + case 'm': - // CHECK-MEMBER: misaligned.cpp:[[@LINE+1]]:15: runtime error: member access within misaligned address 0x{{[0-9a-f]*}} for type 'S', which requires 4 byte alignment - return s->k; + // CHECK-MEMBER: misaligned.cpp:[[@LINE+4]]:15: runtime error: member access within misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-MEMBER-NEXT: [[PTR]]: note: pointer points here + // CHECK-MEMBER-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-MEMBER-NEXT: {{^ \^}} + return s->k && 0; + case 'f': - // CHECK-MEMFUN: misaligned.cpp:[[@LINE+1]]:12: runtime error: member call on misaligned address 0x{{[0-9a-f]*}} for type 'S', which requires 4 byte alignment - return s->f(); + // CHECK-MEMFUN: misaligned.cpp:[[@LINE+4]]:12: runtime error: member call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-MEMFUN-NEXT: [[PTR]]: note: pointer points here + // CHECK-MEMFUN-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-MEMFUN-NEXT: {{^ \^}} + return s->f() && 0; + case 'n': // FIXME: Provide a better source location here. - // CHECK-NEW: misaligned{{.*}}:0x{{[0-9a-f]*}}: runtime error: constructor call on misaligned address 0x{{[0-9a-f]*}} for type 'S', which requires 4 byte alignment - return (new (s) S)->k; + // CHECK-NEW: misaligned{{.*}}:0x{{[0-9a-f]*}}: runtime error: constructor call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-NEW-NEXT: [[PTR]]: note: pointer points here + // CHECK-NEW-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-NEW-NEXT: {{^ \^}} + return (new (s) S)->k && 0; } } diff --git a/lib/ubsan/lit_tests/TypeCheck/vptr.cpp b/lib/ubsan/lit_tests/TypeCheck/vptr.cpp index bce3fd934..a4f97baed 100644 --- a/lib/ubsan/lit_tests/TypeCheck/vptr.cpp +++ b/lib/ubsan/lit_tests/TypeCheck/vptr.cpp @@ -1,11 +1,13 @@ // RUN: %clang -ccc-cxx -fsanitize=vptr %s -O3 -o %t // RUN: %t rT && %t mT && %t fT // RUN: %t rU && %t mU && %t fU -// RUN: %t rS && %t rV -// RUN: %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER -// RUN: %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN -// RUN: %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER -// RUN: %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN +// RUN: %t rS && %t rV && %t oV +// RUN: %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace +// RUN: %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace +// RUN: %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace +// RUN: %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace // FIXME: This test produces linker errors on Darwin. // XFAIL: darwin @@ -46,7 +48,11 @@ int main(int, char **argv) { (void)((T&)u).S::v(); T *p = 0; + char Buffer[sizeof(U)] = {}; switch (argv[1][1]) { + case '0': + p = reinterpret_cast<T*>(Buffer); + break; case 'S': p = reinterpret_cast<T*>(new S); break; @@ -66,11 +72,35 @@ int main(int, char **argv) { // Binding a reference to storage of appropriate size and alignment is OK. {T &r = *p;} break; + case 'm': - // CHECK-MEMBER: vptr.cpp:[[@LINE+1]]:15: runtime error: member access within address 0x{{[0-9a-f]*}} which does not point to an object of type 'T' + // CHECK-MEMBER: vptr.cpp:[[@LINE+5]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-MEMBER-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:1S|1U]] + // CHECK-MEMBER-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-MEMBER-NEXT: {{^ vptr for}} [[DYN_TYPE]] return p->b; + + // CHECK-NULL-MEMBER: vptr.cpp:[[@LINE-2]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-NULL-MEMBER-NEXT: [[PTR]]: note: object has invalid vptr + // CHECK-NULL-MEMBER-NEXT: {{^ .. .. .. .. 00 00 00 00 00 00 00 00 }} + // CHECK-NULL-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-NULL-MEMBER-NEXT: {{^ invalid vptr}} + case 'f': - // CHECK-MEMFUN: vptr.cpp:[[@LINE+1]]:12: runtime error: member call on address 0x{{[0-9a-f]*}} which does not point to an object of type 'T' + // CHECK-MEMFUN: vptr.cpp:[[@LINE+5]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-MEMFUN-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:1S|1U]] + // CHECK-MEMFUN-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-MEMFUN-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-MEMFUN-NEXT: {{^ vptr for}} [[DYN_TYPE]] return p->g(); + + case 'o': + // CHECK-OFFSET: vptr.cpp:[[@LINE+5]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'U' + // CHECK-OFFSET-NEXT: 0x{{[0-9a-f]*}}: note: object is base class subobject at offset {{8|16}} within object of type [[DYN_TYPE:1U]] + // CHECK-OFFSET-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)~~~~~~~~~~~ *$}} + // CHECK-OFFSET-NEXT: {{^ ( )?vptr for}} [[DYN_TYPE]] + return reinterpret_cast<U*>(p)->v() - 2; } } diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc index b36efd2db..38dfc8512 100644 --- a/lib/ubsan/ubsan_diag.cc +++ b/lib/ubsan/ubsan_diag.cc @@ -89,22 +89,11 @@ static void renderLocation(Location Loc) { break; case Location::LK_Null: RawWrite("<unknown>:"); + break; } } -Diag::~Diag() { - bool UseAnsiColor = PrintsToTty(); - if (UseAnsiColor) - RawWrite("\033[1m"); - - renderLocation(Loc); - - if (UseAnsiColor) - RawWrite("\033[31m"); - - RawWrite(" runtime error: "); - if (UseAnsiColor) - RawWrite("\033[0;1m"); +static void renderText(const char *Message, const Diag::Arg *Args) { for (const char *Msg = Message; *Msg; ++Msg) { if (*Msg != '%') { char Buffer[64]; @@ -115,25 +104,25 @@ Diag::~Diag() { RawWrite(Buffer); Msg += I - 1; } else { - const Arg &A = Args[*++Msg - '0']; + const Diag::Arg &A = Args[*++Msg - '0']; switch (A.Kind) { - case AK_String: + case Diag::AK_String: Printf("%s", A.String); break; - case AK_SInt: + case Diag::AK_SInt: // 'long long' is guaranteed to be at least 64 bits wide. if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX) Printf("%lld", (long long)A.SInt); else PrintHex(A.SInt); break; - case AK_UInt: + case Diag::AK_UInt: if (A.UInt <= UINT64_MAX) Printf("%llu", (unsigned long long)A.UInt); else PrintHex(A.UInt); break; - case AK_Float: { + case Diag::AK_Float: { // FIXME: Support floating-point formatting in sanitizer_common's // printf, and stop using snprintf here. char Buffer[32]; @@ -141,13 +130,138 @@ Diag::~Diag() { Printf("%s", Buffer); break; } - case AK_Pointer: + case Diag::AK_Pointer: Printf("0x%zx", (uptr)A.Pointer); break; } } } +} + +/// Find the earliest-starting range in Ranges which ends after Loc. +static Range *upperBound(MemoryLocation Loc, Range *Ranges, + unsigned NumRanges) { + Range *Best = 0; + for (unsigned I = 0; I != NumRanges; ++I) + if (Ranges[I].getEnd().getMemoryLocation() > Loc && + (!Best || + Best->getStart().getMemoryLocation() > + Ranges[I].getStart().getMemoryLocation())) + Best = &Ranges[I]; + return Best; +} + +/// Render a snippet of the address space near a location. +static void renderMemorySnippet(MemoryLocation Loc, + Range *Ranges, unsigned NumRanges, + const Diag::Arg *Args) { + const unsigned BytesToShow = 32; + const unsigned MinBytesNearLoc = 4; + + // Show at least the 8 bytes surrounding Loc. + MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc; + for (unsigned I = 0; I < NumRanges; ++I) { + Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min); + Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max); + } + + // If we have too many interesting bytes, prefer to show bytes after Loc. + if (Max - Min > BytesToShow) + Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc); + Max = Min + BytesToShow; + + // Emit data. + for (uptr P = Min; P != Max; ++P) { + // FIXME: Check that the address is readable before printing it. + unsigned char C = *reinterpret_cast<const unsigned char*>(P); + Printf("%s%02x", (P % 8 == 0) ? " " : " ", C); + } + RawWrite("\n"); + + // Emit highlights. + Range *InRange = upperBound(Min, Ranges, NumRanges); + for (uptr P = Min; P != Max; ++P) { + char Pad = ' ', Byte = ' '; + if (InRange && InRange->getEnd().getMemoryLocation() == P) + InRange = upperBound(P, Ranges, NumRanges); + if (!InRange && P > Loc) + break; + if (InRange && InRange->getStart().getMemoryLocation() < P) + Pad = '~'; + if (InRange && InRange->getStart().getMemoryLocation() <= P) + Byte = '~'; + char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 }; + RawWrite((P % 8 == 0) ? Buffer : &Buffer[1]); + } RawWrite("\n"); + + // Go over the line again, and print names for the ranges. + InRange = 0; + unsigned Spaces = 0; + for (uptr P = Min; P != Max; ++P) { + if (!InRange || InRange->getEnd().getMemoryLocation() == P) + InRange = upperBound(P, Ranges, NumRanges); + if (!InRange) + break; + + Spaces += (P % 8) == 0 ? 2 : 1; + + if (InRange && InRange->getStart().getMemoryLocation() == P) { + while (Spaces--) + RawWrite(" "); + renderText(InRange->getText(), Args); + RawWrite("\n"); + // FIXME: We only support naming one range for now! + break; + } + + Spaces += 2; + } + + // FIXME: Print names for anything we can identify within the line: + // + // * If we can identify the memory itself as belonging to a particular + // global, stack variable, or dynamic allocation, then do so. + // + // * If we have a pointer-size, pointer-aligned range highlighted, + // determine whether the value of that range is a pointer to an + // entity which we can name, and if so, print that name. + // + // This needs an external symbolizer, or (preferably) ASan instrumentation. +} + +Diag::~Diag() { + bool UseAnsiColor = PrintsToTty(); + if (UseAnsiColor) + RawWrite("\033[1m"); + + renderLocation(Loc); + + switch (Level) { + case DL_Error: + if (UseAnsiColor) + RawWrite("\033[31m"); + RawWrite(" runtime error: "); + if (UseAnsiColor) + RawWrite("\033[0;1m"); + break; + + case DL_Note: + if (UseAnsiColor) + RawWrite("\033[30m"); + RawWrite(" note: "); + if (UseAnsiColor) + RawWrite("\033[0m"); + break; + } + + renderText(Message, Args); + if (UseAnsiColor) - Printf("\033[0m"); + RawWrite("\033[0m"); + + RawWrite("\n"); + + if (Loc.isMemoryLocation()) + renderMemorySnippet(Loc.getMemoryLocation(), Ranges, NumRanges, Args); } diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h index 0cd9a7a3b..a77c71b66 100644 --- a/lib/ubsan/ubsan_diag.h +++ b/lib/ubsan/ubsan_diag.h @@ -80,6 +80,26 @@ public: /// an invalid location or a module location for the caller. Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC()); +/// A diagnostic severity level. +enum DiagLevel { + DL_Error, ///< An error. + DL_Note ///< A note, attached to a prior diagnostic. +}; + +/// \brief Annotation for a range of locations in a diagnostic. +class Range { + Location Start, End; + const char *Text; + +public: + Range() : Start(), End(), Text() {} + Range(MemoryLocation Start, MemoryLocation End, const char *Text) + : Start(Start), End(End), Text(Text) {} + Location getStart() const { return Start; } + Location getEnd() const { return End; } + const char *getText() const { return Text; } +}; + /// \brief Representation of an in-flight diagnostic. /// /// Temporary \c Diag instances are created by the handler routines to @@ -89,10 +109,14 @@ class Diag { /// The location at which the problem occurred. Location Loc; + /// The diagnostic level. + DiagLevel Level; + /// The message which will be emitted, with %0, %1, ... placeholders for /// arguments. const char *Message; +public: /// Kinds of arguments, corresponding to members of \c Arg's union. enum ArgKind { AK_String, ///< A string argument, displayed as-is. @@ -121,25 +145,37 @@ class Diag { }; }; +private: static const unsigned MaxArgs = 5; + static const unsigned MaxRanges = 1; /// The arguments which have been added to this diagnostic so far. Arg Args[MaxArgs]; unsigned NumArgs; + /// The ranges which have been added to this diagnostic so far. + Range Ranges[MaxRanges]; + unsigned NumRanges; + Diag &AddArg(Arg A) { CHECK(NumArgs != MaxArgs); Args[NumArgs++] = A; return *this; } + Diag &AddRange(Range A) { + CHECK(NumRanges != MaxRanges); + Ranges[NumRanges++] = A; + return *this; + } + /// \c Diag objects are not copyable. Diag(const Diag &); // NOT IMPLEMENTED Diag &operator=(const Diag &); public: - Diag(Location Loc, const char *Message) - : Loc(Loc), Message(Message), NumArgs(0) {} + Diag(Location Loc, DiagLevel Level, const char *Message) + : Loc(Loc), Level(Level), Message(Message), NumArgs(0), NumRanges(0) {} ~Diag(); Diag &operator<<(const char *Str) { return AddArg(Str); } @@ -147,6 +183,7 @@ public: Diag &operator<<(const void *V) { return AddArg(V); } Diag &operator<<(const TypeDescriptor &V); Diag &operator<<(const Value &V); + Diag &operator<<(const Range &R) { return AddRange(R); } }; } // namespace __ubsan diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc index 1f3bf6823..5e73c04ee 100644 --- a/lib/ubsan/ubsan_handlers.cc +++ b/lib/ubsan/ubsan_handlers.cc @@ -33,17 +33,19 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, Loc = FallbackLoc; if (!Pointer) - Diag(Loc, "%0 null pointer of type %1") + Diag(Loc, DL_Error, "%0 null pointer of type %1") << TypeCheckKinds[Data->TypeCheckKind] << Data->Type; else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) - Diag(Loc, "%0 misaligned address %1 for type %3, " - "which requires %2 byte alignment") + 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 - Diag(Loc, "%0 address %1 with insufficient space " - "for an object of type %2") + 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"); } void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data, ValueHandle Pointer) { @@ -57,11 +59,11 @@ void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData *Data, /// \brief Common diagnostic emission for various forms of integer overflow. template<typename T> static void HandleIntegerOverflow(OverflowData *Data, - ValueHandle LHS, - const char *Operator, - T RHS) { - Diag(Data->Loc, "%0 integer overflow: " - "%1 %2 %3 cannot be represented in type %4") + ValueHandle LHS, + const char *Operator, + T RHS) { + Diag(Data->Loc, DL_Error, "%0 integer overflow: " + "%1 %2 %3 cannot be represented in type %4") << (Data->Type.isSignedIntegerTy() ? "signed" : "unsigned") << Value(Data->Type, LHS) << Operator << RHS << Data->Type; } @@ -101,8 +103,9 @@ void __ubsan::__ubsan_handle_mul_overflow_abort(OverflowData *Data, void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data, ValueHandle OldVal) { - Diag(Data->Loc, "negation of %0 cannot be represented in type %1; " - "cast to an unsigned type to negate this value to itself") + Diag(Data->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; } void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data, @@ -116,10 +119,11 @@ void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data, Value LHSVal(Data->Type, LHS); Value RHSVal(Data->Type, RHS); if (RHSVal.isMinusOne()) - Diag(Data->Loc, "division of %0 by -1 cannot be represented in type %1") + Diag(Data->Loc, DL_Error, + "division of %0 by -1 cannot be represented in type %1") << LHSVal << Data->Type; else - Diag(Data->Loc, "division by zero"); + Diag(Data->Loc, DL_Error, "division by zero"); } void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData *Data, ValueHandle LHS, @@ -134,15 +138,17 @@ void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data, Value LHSVal(Data->LHSType, LHS); Value RHSVal(Data->RHSType, RHS); if (RHSVal.isNegative()) - Diag(Data->Loc, "shift exponent %0 is negative") << RHSVal; + Diag(Data->Loc, DL_Error, "shift exponent %0 is negative") << RHSVal; else if (RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth()) - Diag(Data->Loc, "shift exponent %0 is too large for %1-bit type %2") + Diag(Data->Loc, DL_Error, + "shift exponent %0 is too large for %1-bit type %2") << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType; else if (LHSVal.isNegative()) - Diag(Data->Loc, "left shift of negative value %0") << LHSVal; + Diag(Data->Loc, DL_Error, "left shift of negative value %0") << LHSVal; else - Diag(Data->Loc, "left shift of %0 by %1 places cannot be represented " - "in type %2") << LHSVal << RHSVal << Data->LHSType; + Diag(Data->Loc, DL_Error, + "left shift of %0 by %1 places cannot be represented in type %2") + << LHSVal << RHSVal << Data->LHSType; } void __ubsan::__ubsan_handle_shift_out_of_bounds_abort( ShiftOutOfBoundsData *Data, @@ -153,20 +159,21 @@ void __ubsan::__ubsan_handle_shift_out_of_bounds_abort( } void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) { - Diag(Data->Loc, "execution reached a __builtin_unreachable() call"); + Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call"); Die(); } void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) { - Diag(Data->Loc, "execution reached the end of a value-returning function " - "without returning a value"); + Diag(Data->Loc, DL_Error, + "execution reached the end of a value-returning function " + "without returning a value"); Die(); } void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data, ValueHandle Bound) { - Diag(Data->Loc, "variable length array bound evaluates to " - "non-positive value %0") + Diag(Data->Loc, DL_Error, "variable length array bound evaluates to " + "non-positive value %0") << Value(Data->Type, Bound); } void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data, @@ -178,29 +185,29 @@ void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data, void __ubsan::__ubsan_handle_float_cast_overflow(FloatCastOverflowData *Data, ValueHandle From) { - Diag(getCallerLocation(), "value %0 is outside the range of representable " - "values of type %2") + Diag(getCallerLocation(), DL_Error, + "value %0 is outside the range of representable values of type %2") << Value(Data->FromType, From) << Data->FromType << Data->ToType; } void __ubsan::__ubsan_handle_float_cast_overflow_abort( FloatCastOverflowData *Data, ValueHandle From) { - Diag(getCallerLocation(), "value %0 is outside the range of representable " - "values of type %2") + Diag(getCallerLocation(), DL_Error, + "value %0 is outside the range of representable values of type %2") << Value(Data->FromType, From) << Data->FromType << Data->ToType; Die(); } void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data, ValueHandle Val) { - Diag(getCallerLocation(), "load of value %0, which is not a valid value for " - "type %1") + Diag(getCallerLocation(), DL_Error, + "load of value %0, which is not a valid value for type %1") << Value(Data->Type, Val) << Data->Type; } void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, ValueHandle Val) { - Diag(getCallerLocation(), "load of value %0, which is not a valid value for " - "type %1") + Diag(getCallerLocation(), DL_Error, + "load of value %0, which is not a valid value for type %1") << Value(Data->Type, Val) << Data->Type; Die(); } diff --git a/lib/ubsan/ubsan_handlers_cxx.cc b/lib/ubsan/ubsan_handlers_cxx.cc index 593fe1312..e3f39cca5 100644 --- a/lib/ubsan/ubsan_handlers_cxx.cc +++ b/lib/ubsan/ubsan_handlers_cxx.cc @@ -27,34 +27,42 @@ namespace __ubsan { } static void HandleDynamicTypeCacheMiss( - DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash, - bool abort) { + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash, + bool Abort) { if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash)) // Just a cache miss. The type matches after all. return; - Diag(Data->Loc, "%0 address %1 which does not point to an object of type %2") + Diag(Data->Loc, DL_Error, + "%0 address %1 which does not point to an object of type %2") << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; - // FIXME: If possible, say what type it actually points to. Produce a note - // pointing out the vptr: - // lib/VMCore/Instructions.cpp:2020:10: runtime error: member call on address - // 0xb7a4440 which does not point to an object of type - // 'llvm::OverflowingBinaryOperator' - // return cast<OverflowingBinaryOperator>(this)->hasNoSignedWrap(); - // ^ - // 0xb7a4440: note: object is of type 'llvm::BinaryOperator' - // 00 00 00 00 e0 f7 c5 09 00 00 00 00 20 00 00 00 - // ^~~~~~~~~~~ - // vptr for 'llvm::BinaryOperator' - if (abort) + + // If possible, say what type it actually points to. + // FIXME: Demangle the type names. + DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer); + if (!DTI.isValid()) + Diag(Pointer, DL_Note, "object has invalid vptr") + << DTI.getMostDerivedTypeName() + << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr"); + else if (!DTI.getOffset()) + Diag(Pointer, DL_Note, "object is of type %0") + << DTI.getMostDerivedTypeName() + << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0"); + else + Diag(Pointer - DTI.getOffset(), DL_Note, + "object is base class subobject at offset %0 within object of type %1") + << DTI.getOffset() << DTI.getMostDerivedTypeName() + << Range(Pointer, Pointer + sizeof(uptr), "vptr for %1"); + + if (Abort) Die(); } void __ubsan::__ubsan_handle_dynamic_type_cache_miss( - DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { HandleDynamicTypeCacheMiss(Data, Pointer, Hash, false); } void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort( - DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { + DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) { HandleDynamicTypeCacheMiss(Data, Pointer, Hash, true); } diff --git a/lib/ubsan/ubsan_type_hash.cc b/lib/ubsan/ubsan_type_hash.cc index 1f6a3dbd9..ee17af7d8 100644 --- a/lib/ubsan/ubsan_type_hash.cc +++ b/lib/ubsan/ubsan_type_hash.cc @@ -25,7 +25,7 @@ namespace std { class type_info { public: virtual ~type_info(); - private: + const char *__type_name; }; } @@ -160,8 +160,14 @@ struct VtablePrefix { std::type_info *TypeInfo; }; VtablePrefix *getVtablePrefix(void *Object) { - VtablePrefix **Ptr = reinterpret_cast<VtablePrefix**>(Object); - return *Ptr - 1; + VtablePrefix **VptrPtr = reinterpret_cast<VtablePrefix**>(Object); + if (!*VptrPtr) + return 0; + VtablePrefix *Prefix = *VptrPtr - 1; + if (Prefix->Offset > 0 || !Prefix->TypeInfo) + // This can't possibly be a valid vtable. + return 0; + return Prefix; } } @@ -178,8 +184,7 @@ bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { } VtablePrefix *Vtable = getVtablePrefix(Object); - if (Vtable + 1 == 0 || Vtable->Offset > 0) - // This can't possibly be a valid vtable. + if (!Vtable) return false; // Check that this is actually a type_info object for a class type. @@ -197,3 +202,10 @@ bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { *Bucket = Hash; return true; } + +__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) { + VtablePrefix *Vtable = getVtablePrefix(Object); + if (!Vtable) + return DynamicTypeInfo(0, 0); + return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset); +} diff --git a/lib/ubsan/ubsan_type_hash.h b/lib/ubsan/ubsan_type_hash.h index ac1be4944..dfaf32752 100644 --- a/lib/ubsan/ubsan_type_hash.h +++ b/lib/ubsan/ubsan_type_hash.h @@ -19,6 +19,27 @@ namespace __ubsan { typedef uptr HashValue; +/// \brief Information about the dynamic type of an object (extracted from its +/// vptr). +class DynamicTypeInfo { + const char *MostDerivedTypeName; + sptr Offset; + +public: + DynamicTypeInfo(const char *MDTN, sptr Offset) + : MostDerivedTypeName(MDTN), Offset(Offset) {} + + /// Determine whether the object had a valid dynamic type. + bool isValid() const { return MostDerivedTypeName; } + /// Get the name of the most-derived type of the object. + const char *getMostDerivedTypeName() const { return MostDerivedTypeName; } + /// Get the offset from the most-derived type to this base class. + sptr getOffset() const { return Offset; } +}; + +/// \brief Get information about the dynamic type of an object. +DynamicTypeInfo getDynamicTypeInfo(void *Object); + /// \brief Check whether the dynamic type of \p Object has a \p Type subobject /// at offset 0. /// \return \c true if the type matches, \c false if not. |