forked from OSchip/llvm-project
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. llvm-svn: 170415
This commit is contained in:
parent
cb23342876
commit
cf56ebd52a
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
RawWrite("\n");
|
||||
if (UseAnsiColor)
|
||||
Printf("\033[0m");
|
||||
}
|
||||
|
||||
/// 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)
|
||||
RawWrite("\033[0m");
|
||||
|
||||
RawWrite("\n");
|
||||
|
||||
if (Loc.isMemoryLocation())
|
||||
renderMemorySnippet(Loc.getMemoryLocation(), Ranges, NumRanges, Args);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue