// RUN: %clangxx -frtti -fsanitize=null,vptr -g %s -O3 -o %t -mllvm -enable-tail-merge=false // RUN: %env_ubsan_opts=halt_on_error=1 %run %t rT // RUN: %env_ubsan_opts=halt_on_error=1 %run %t mT // RUN: %env_ubsan_opts=halt_on_error=1 %run %t fT // RUN: %env_ubsan_opts=halt_on_error=1 %run %t cT // RUN: %env_ubsan_opts=halt_on_error=1 %run %t rU // RUN: %env_ubsan_opts=halt_on_error=1 %run %t mU // RUN: %env_ubsan_opts=halt_on_error=1 %run %t fU // RUN: %env_ubsan_opts=halt_on_error=1 %run %t cU // RUN: %env_ubsan_opts=halt_on_error=1 %run %t rS // RUN: %env_ubsan_opts=halt_on_error=1 %run %t rV // RUN: %env_ubsan_opts=halt_on_error=1 %run %t oV // RUN: %env_ubsan_opts=halt_on_error=1 %run %t zN // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --check-prefix=CHECK-%os-MEMBER --strict-whitespace // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t cS 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --check-prefix=CHECK-%os-DOWNCAST --strict-whitespace // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --check-prefix=CHECK-%os-MEMBER --strict-whitespace // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t cV 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --check-prefix=CHECK-%os-DOWNCAST --strict-whitespace // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --check-prefix=CHECK-%os-OFFSET --strict-whitespace // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID-MEMBER --check-prefix=CHECK-%os-NULL-MEMBER --strict-whitespace // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID-MEMBER --check-prefix=CHECK-%os-NULL-MEMBER --strict-whitespace // RUN: %env_ubsan_opts=halt_on_error=1 not %run %t nN 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMFUN --strict-whitespace // RUN: %env_ubsan_opts=print_stacktrace=1 %run %t dT 2>&1 | FileCheck %s --check-prefix=CHECK-DYNAMIC --check-prefix=CHECK-%os-DYNAMIC --strict-whitespace // RUN: (echo "vptr_check:S"; echo "vptr_check:T"; echo "vptr_check:U") > %t.supp // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t mS // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t fS // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t cS // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t mV // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t fV // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t cV // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t oU // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t dT // RUN: echo "vptr_check:S" > %t.loc-supp // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.loc-supp"' not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS // REQUIRES: stable-runtime, cxxabi // UNSUPPORTED: win32 // Suppressions file not pushed to the device. // UNSUPPORTED: android #include #include #include #include struct S { S() : a(0) {} ~S(); int a; int f() { return 0; } virtual int v() { return 0; } }; struct T : S { T() : b(0) {} int b; int g() { return 0; } virtual int v() { return 1; } }; struct U : S, T { virtual int v() { return 2; } }; struct V : S {}; namespace { struct W {}; } T *p = 0; bool dtorCheck = false; volatile void *sink1, *sink2; int access_p(T *p, char type); S::~S() { if (dtorCheck) access_p(p, '~'); } int main(int argc, char **argv) { assert(argc > 1); fprintf(stderr, "Test case: %s\n", argv[1]); T t; (void)t.a; (void)t.b; (void)t.f(); (void)t.g(); (void)t.v(); (void)t.S::v(); U u; (void)u.T::a; (void)u.b; (void)u.T::f(); (void)u.g(); (void)u.v(); (void)u.T::v(); (void)((T&)u).S::v(); char Buffer[sizeof(U)] = {}; char TStorage[sizeof(T)]; // Allocate two dummy objects so that the real object // is not on the boundary of mapped memory. Otherwise ubsan // will not be able to describe the vptr in detail. sink1 = new T; sink2 = new U; switch (argv[1][1]) { case '0': p = reinterpret_cast(Buffer); break; case 'S': // Make sure p points to the memory chunk of sufficient size to prevent ASan // reports about out-of-bounds access. p = reinterpret_cast(new(TStorage) S); break; case 'T': p = new T; break; case 'U': p = new U; break; case 'V': p = reinterpret_cast(new U); break; case 'N': p = 0; break; } access_p(p, argv[1][0]); return 0; } int access_p(T *p, char type) { switch (type) { case 'r': // Binding a reference to storage of appropriate size and alignment is OK. {T &r = *p;} return 0; case 'x': for (int i = 0; i < 2; i++) { // Check that the first iteration ("S") succeeds, while the second ("V") fails. p = reinterpret_cast((i == 0) ? new S : new V); // CHECK-LOC-SUPPRESS: vptr.cpp:[[@LINE+5]]:10: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' // CHECK-LOC-SUPPRESS-NEXT: [[PTR]]: note: object is of type 'V' // CHECK-LOC-SUPPRESS-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} // CHECK-LOC-SUPPRESS-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} // CHECK-LOC-SUPPRESS-NEXT: {{^ vptr for 'V'}} p->g(); } return 0; case 'm': // 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-Linux-MEMBER: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]] return p->b; // CHECK-INVALID-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-INVALID-MEMBER-NEXT: [[PTR]]: note: object has invalid vptr // CHECK-INVALID-MEMBER-NEXT: {{^ ?.. .. .. .. ?00 00 00 00 ?00 00 00 00 ?}} // CHECK-INVALID-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} // CHECK-INVALID-MEMBER-NEXT: {{^ invalid vptr}} // CHECK-Linux-NULL-MEMBER: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE-7]] case 'f': // CHECK-MEMFUN: vptr.cpp:[[@LINE+6]]:15: 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+6]]:37: 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-Linux-OFFSET: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]] return reinterpret_cast(p)->v() - 2; case 'c': // CHECK-DOWNCAST: vptr.cpp:[[@LINE+6]]:11: 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-Linux-DOWNCAST: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]] (void)static_cast(reinterpret_cast(p)); return 0; case 'n': // CHECK-NULL-MEMFUN: vptr.cpp:[[@LINE+1]]:15: runtime error: member call on null pointer of type 'T' return p->g(); case 'd': dtorCheck = true; delete p; dtorCheck = false; return 0; case '~': // CHECK-DYNAMIC: vptr.cpp:[[@LINE+6]]:11: runtime error: dynamic operation on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S' // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S' // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]] (void)dynamic_cast(p); // CHECK-DYNAMIC: vptr.cpp:[[@LINE+6]]:11: runtime error: dynamic operation on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S' // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S' // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]] (void)dynamic_cast(p); try { // CHECK-DYNAMIC: vptr.cpp:[[@LINE+6]]:13: runtime error: dynamic operation on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S' // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S' // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]] (void)dynamic_cast(*p); } catch (std::bad_cast &) {} // CHECK-DYNAMIC: vptr.cpp:[[@LINE+6]]:18: runtime error: dynamic operation on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S' // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S' // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]] (void)typeid(*p); return 0; case 'z': (void)dynamic_cast(p); try { (void)typeid(*p); } catch (std::bad_typeid &) {} return 0; } return 0; }