Index: test/cfi/nvcall.cpp =================================================================== --- /dev/null +++ test/cfi/nvcall.cpp @@ -0,0 +1,65 @@ +// RUN: %clangxx_cfi -o %t %s +// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx_cfi -DB32 -o %t %s +// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx_cfi -DB64 -o %t %s +// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx_cfi -DBM -o %t %s +// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx -o %t %s +// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s + +// Tests that the CFI mechanism crashes the program when making a non-virtual +// call to an object of the wrong class, by casting a pointer to such an object +// and attempting to make a call through it. + +#include +#include "utils.h" + +struct A { + virtual void f(); +}; + +void A::f() {} + +struct B { + void f(); + virtual void g(); +}; + +void B::f() {} +void B::g() {} + +int main() { +#ifdef B32 + break_optimization(new Deriver); +#endif + +#ifdef B64 + break_optimization(new Deriver); + break_optimization(new Deriver); +#endif + +#ifdef BM + break_optimization(new Deriver); + break_optimization(new Deriver); + break_optimization(new Deriver); +#endif + + A *a = new A; + break_optimization(a); + + // CFI: 1 + // NCFI: 1 + fprintf(stderr, "1\n"); + + ((B *)a)->f(); // UB here + + // CFI-NOT: 2 + // NCFI: 2 + fprintf(stderr, "2\n"); +} Index: test/cfi/simple-pass.cpp =================================================================== --- test/cfi/simple-pass.cpp +++ test/cfi/simple-pass.cpp @@ -3,95 +3,119 @@ // Tests that the CFI mechanism does not crash the program when making various // kinds of valid calls involving classes with various different linkages and -// types of inheritance. +// types of inheritance, and both virtual and non-virtual member functions. #include "utils.h" struct A { virtual void f(); + void g(); }; void A::f() {} +void A::g() {} struct A2 : A { virtual void f(); + void g(); }; void A2::f() {} +void A2::g() {} struct B { virtual void f() {} + void g() {} }; struct B2 : B { virtual void f() {} + void g() {} }; namespace { struct C { virtual void f(); + void g(); }; void C::f() {} +void C::g() {} struct C2 : C { virtual void f(); + void g(); }; void C2::f() {} +void C2::g() {} struct D { virtual void f() {} + void g() {} }; struct D2 : D { virtual void f() {} + void g() {} }; } struct E { virtual void f() {} + void g() {} }; struct E2 : virtual E { virtual void f() {} + void g() {} }; int main() { A *a = new A; break_optimization(a); a->f(); + a->g(); a = new A2; break_optimization(a); a->f(); + a->g(); B *b = new B; break_optimization(b); b->f(); + b->g(); b = new B2; break_optimization(b); b->f(); + b->g(); C *c = new C; break_optimization(c); c->f(); + c->g(); c = new C2; break_optimization(c); c->f(); + c->g(); D *d = new D; break_optimization(d); d->f(); + d->g(); d = new D2; break_optimization(d); d->f(); + d->g(); E *e = new E; break_optimization(e); e->f(); + e->g(); e = new E2; break_optimization(e); e->f(); + e->g(); }