forked from OSchip/llvm-project
[UBSan] Add the ability to dump call stacks to -fsanitize=vptr
This change introduces the first UBSan-specific runtime flag: print_stacktrace (off by default). It can be set in UBSAN_OPTIONS to unwind and print call stacks in addition to diagnostic messages. For now these stacks are printed only in vptr checker. This change is based on http://reviews.llvm.org/D4410 by Byoungyoung Lee! llvm-svn: 213783
This commit is contained in:
parent
cd875aa551
commit
bfa711c8b0
|
@ -2,6 +2,7 @@
|
|||
|
||||
set(UBSAN_SOURCES
|
||||
ubsan_diag.cc
|
||||
ubsan_flags.cc
|
||||
ubsan_handlers.cc
|
||||
ubsan_value.cc
|
||||
)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ubsan_diag.h"
|
||||
#include "ubsan_flags.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
@ -37,9 +38,28 @@ static void InitializeSanitizerCommonAndFlags() {
|
|||
// Common flags may only be modified via UBSAN_OPTIONS.
|
||||
ParseCommonFlagsFromString(cf, GetEnv("UBSAN_OPTIONS"));
|
||||
}
|
||||
// Initialize UBSan-specific flags.
|
||||
InitializeFlags();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void __ubsan::MaybePrintStackTrace(uptr pc, uptr bp) {
|
||||
// We assume that flags are already parsed: InitializeSanitizerCommonAndFlags
|
||||
// will definitely be called when we print the first diagnostics message.
|
||||
if (!flags()->print_stacktrace)
|
||||
return;
|
||||
// We can only use slow unwind, as we don't have any information about stack
|
||||
// top/bottom.
|
||||
// FIXME: It's better to respect "fast_unwind_on_fatal" runtime flag and
|
||||
// fetch stack top/bottom information if we have it (e.g. if we're running
|
||||
// under ASan).
|
||||
if (StackTrace::WillUseFastUnwind(false))
|
||||
return;
|
||||
StackTrace stack;
|
||||
stack.Unwind(kStackTraceMax, pc, bp, 0, 0, 0, false);
|
||||
stack.Print();
|
||||
}
|
||||
|
||||
namespace {
|
||||
class Decorator : public SanitizerCommonDecorator {
|
||||
public:
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#define UBSAN_DIAG_H
|
||||
|
||||
#include "ubsan_value.h"
|
||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||
|
||||
namespace __ubsan {
|
||||
|
||||
|
@ -203,6 +204,13 @@ public:
|
|||
Diag &operator<<(const Range &R) { return AddRange(R); }
|
||||
};
|
||||
|
||||
void MaybePrintStackTrace(uptr pc, uptr bp);
|
||||
|
||||
#define MAYBE_PRINT_STACK_TRACE() do { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
MaybePrintStackTrace(pc, bp); \
|
||||
} while (0)
|
||||
|
||||
} // namespace __ubsan
|
||||
|
||||
#endif // UBSAN_DIAG_H
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
//===-- ubsan_flags.cc ----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Runtime flags for UndefinedBehaviorSanitizer.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ubsan_flags.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
|
||||
namespace __ubsan {
|
||||
|
||||
Flags ubsan_flags;
|
||||
|
||||
void InitializeFlags() {
|
||||
Flags *f = flags();
|
||||
// Default values.
|
||||
f->print_stacktrace = false;
|
||||
|
||||
const char *options = GetEnv("UBSAN_OPTIONS");
|
||||
if (options) {
|
||||
ParseFlag(options, &f->print_stacktrace, "print_stacktrace",
|
||||
"Include full stacktrace into an error report");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace __ubsan
|
|
@ -0,0 +1,29 @@
|
|||
//===-- ubsan_flags.h -------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Runtime flags for UndefinedBehaviorSanitizer.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef UBSAN_FLAGS_H
|
||||
#define UBSAN_FLAGS_H
|
||||
|
||||
namespace __ubsan {
|
||||
|
||||
struct Flags {
|
||||
bool print_stacktrace;
|
||||
};
|
||||
|
||||
extern Flags ubsan_flags;
|
||||
inline Flags *flags() { return &ubsan_flags; }
|
||||
|
||||
void InitializeFlags();
|
||||
|
||||
} // namespace __ubsan
|
||||
|
||||
#endif // UBSAN_FLAGS_H
|
|
@ -60,6 +60,7 @@ static void HandleDynamicTypeCacheMiss(
|
|||
<< MangledName(DTI.getSubobjectTypeName())
|
||||
<< Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1");
|
||||
|
||||
MAYBE_PRINT_STACK_TRACE();
|
||||
if (Abort)
|
||||
Die();
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
// RUN: %clangxx -fsanitize=vptr %s -O3 -o %t
|
||||
// RUN: %clangxx -fsanitize=vptr -g %s -O3 -o %t
|
||||
// RUN: %run %t rT && %run %t mT && %run %t fT && %run %t cT
|
||||
// RUN: %run %t rU && %run %t mU && %run %t fU && %run %t cU
|
||||
// RUN: %run %t rS && %run %t rV && %run %t oV
|
||||
// RUN: %run %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace
|
||||
// RUN: %run %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace
|
||||
// RUN: %run %t cS 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace
|
||||
// RUN: %run %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace
|
||||
// RUN: %run %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace
|
||||
// RUN: %run %t cV 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace
|
||||
// RUN: %run %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace
|
||||
// RUN: %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace
|
||||
// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace
|
||||
// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace
|
||||
// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t cS 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace
|
||||
// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace
|
||||
// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace
|
||||
// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t cV 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace
|
||||
// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace
|
||||
// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace
|
||||
|
||||
// FIXME: This test produces linker errors on Darwin.
|
||||
// XFAIL: darwin
|
||||
|
@ -77,11 +77,12 @@ int main(int, char **argv) {
|
|||
break;
|
||||
|
||||
case 'm':
|
||||
// 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: vptr.cpp:[[@LINE+6]]: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:'S'|'U']]
|
||||
// CHECK-MEMBER-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
|
||||
// CHECK-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
|
||||
// CHECK-MEMBER-NEXT: {{^ vptr for}} [[DYN_TYPE]]
|
||||
// CHECK-MEMBER-NEXT: #0 {{.*}} in main {{.*}}vptr.cpp:[[@LINE+1]]
|
||||
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'
|
||||
|
@ -89,29 +90,33 @@ int main(int, char **argv) {
|
|||
// CHECK-NULL-MEMBER-NEXT: {{^ ?.. .. .. .. ?00 00 00 00 ?00 00 00 00 ?}}
|
||||
// CHECK-NULL-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
|
||||
// CHECK-NULL-MEMBER-NEXT: {{^ invalid vptr}}
|
||||
// CHECK-NULL-MEMBER-NEXT: #0 {{.*}} in main {{.*}}vptr.cpp:[[@LINE-7]]
|
||||
|
||||
case 'f':
|
||||
// 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: vptr.cpp:[[@LINE+6]]: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:'S'|'U']]
|
||||
// CHECK-MEMFUN-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
|
||||
// CHECK-MEMFUN-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
|
||||
// CHECK-MEMFUN-NEXT: {{^ vptr for}} [[DYN_TYPE]]
|
||||
// TODO: Add check for stacktrace here.
|
||||
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: vptr.cpp:[[@LINE+6]]: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:'U']]
|
||||
// CHECK-OFFSET-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }}
|
||||
// CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)?~~~~~~~~~~~ *$}}
|
||||
// CHECK-OFFSET-NEXT: {{^ ( )?vptr for}} 'T' base class of [[DYN_TYPE]]
|
||||
// CHECK-OFFSET-NEXT: #0 {{.*}} in main {{.*}}vptr.cpp:[[@LINE+1]]
|
||||
return reinterpret_cast<U*>(p)->v() - 2;
|
||||
|
||||
case 'c':
|
||||
// CHECK-DOWNCAST: vptr.cpp:[[@LINE+5]]:5: runtime error: downcast of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
|
||||
// CHECK-DOWNCAST: vptr.cpp:[[@LINE+6]]:5: runtime error: downcast of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
|
||||
// CHECK-DOWNCAST-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
|
||||
// CHECK-DOWNCAST-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
|
||||
// CHECK-DOWNCAST-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
|
||||
// CHECK-DOWNCAST-NEXT: {{^ vptr for}} [[DYN_TYPE]]
|
||||
// CHECK-DOWNCAST-NEXT: #0 {{.*}} in main {{.*}}vptr.cpp:[[@LINE+1]]
|
||||
static_cast<T*>(reinterpret_cast<S*>(p));
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue