From 805b0e8f7f27a88fe1930bf346c48499655fe091 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Wed, 23 Jul 2014 18:44:54 +0000 Subject: [PATCH] [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! git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@213783 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/ubsan/CMakeLists.txt | 1 + lib/ubsan/ubsan_diag.cc | 20 +++++++++++++++++++ lib/ubsan/ubsan_diag.h | 8 ++++++++ lib/ubsan/ubsan_flags.cc | 34 +++++++++++++++++++++++++++++++++ lib/ubsan/ubsan_flags.h | 29 ++++++++++++++++++++++++++++ lib/ubsan/ubsan_handlers_cxx.cc | 1 + test/ubsan/TestCases/TypeCheck/vptr.cpp | 31 +++++++++++++++++------------- 7 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 lib/ubsan/ubsan_flags.cc create mode 100644 lib/ubsan/ubsan_flags.h diff --git a/lib/ubsan/CMakeLists.txt b/lib/ubsan/CMakeLists.txt index 78c0d703d..14e3dce2b 100644 --- a/lib/ubsan/CMakeLists.txt +++ b/lib/ubsan/CMakeLists.txt @@ -2,6 +2,7 @@ set(UBSAN_SOURCES ubsan_diag.cc + ubsan_flags.cc ubsan_handlers.cc ubsan_value.cc ) diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc index 603aee4a3..c19f2f5ed 100644 --- a/lib/ubsan/ubsan_diag.cc +++ b/lib/ubsan/ubsan_diag.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: diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h index 54d15a0cc..033695190 100644 --- a/lib/ubsan/ubsan_diag.h +++ b/lib/ubsan/ubsan_diag.h @@ -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 diff --git a/lib/ubsan/ubsan_flags.cc b/lib/ubsan/ubsan_flags.cc new file mode 100644 index 000000000..6c1adc963 --- /dev/null +++ b/lib/ubsan/ubsan_flags.cc @@ -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 diff --git a/lib/ubsan/ubsan_flags.h b/lib/ubsan/ubsan_flags.h new file mode 100644 index 000000000..db0a4e4aa --- /dev/null +++ b/lib/ubsan/ubsan_flags.h @@ -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 diff --git a/lib/ubsan/ubsan_handlers_cxx.cc b/lib/ubsan/ubsan_handlers_cxx.cc index b6cddefeb..0abeaa6a3 100644 --- a/lib/ubsan/ubsan_handlers_cxx.cc +++ b/lib/ubsan/ubsan_handlers_cxx.cc @@ -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(); } diff --git a/test/ubsan/TestCases/TypeCheck/vptr.cpp b/test/ubsan/TestCases/TypeCheck/vptr.cpp index 4a8910cc6..68ca6926d 100644 --- a/test/ubsan/TestCases/TypeCheck/vptr.cpp +++ b/test/ubsan/TestCases/TypeCheck/vptr.cpp @@ -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(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(reinterpret_cast(p)); return 0; } -- 2.11.4.GIT