summaryrefslogtreecommitdiff
path: root/lib/ubsan
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2012-12-18 06:30:32 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2012-12-18 06:30:32 +0000
commit25ee97fe8ada76755c8bd1087fac9cc3cd03b28c (patch)
tree7eb8ac2d90d4635d1c727ebd835c7aa4c90d290b /lib/ubsan
parent01e9a38b8cfe8967efb259978b754c3a9f0c380c (diff)
downloadcompiler-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.cpp49
-rw-r--r--lib/ubsan/lit_tests/TypeCheck/vptr.cpp44
-rw-r--r--lib/ubsan/ubsan_diag.cc154
-rw-r--r--lib/ubsan/ubsan_diag.h41
-rw-r--r--lib/ubsan/ubsan_handlers.cc71
-rw-r--r--lib/ubsan/ubsan_handlers_cxx.cc42
-rw-r--r--lib/ubsan/ubsan_type_hash.cc22
-rw-r--r--lib/ubsan/ubsan_type_hash.h21
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.