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:
Richard Smith 2012-12-18 06:30:32 +00:00
parent cb23342876
commit cf56ebd52a
8 changed files with 350 additions and 98 deletions

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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.