diff options
author | Peter Collingbourne <peter@pcc.me.uk> | 2018-06-26 02:15:47 +0000 |
---|---|---|
committer | Peter Collingbourne <peter@pcc.me.uk> | 2018-06-26 02:15:47 +0000 |
commit | 2d0b060aaf31bfad01827f8af15f8f5af2ae072b (patch) | |
tree | bac083fc96016e0e7ff4adb2a0bdf9aea7db616f /test/cfi | |
parent | 2a1852a7218178ecdbd2cfd28970cb65941cea82 (diff) |
Implement CFI for indirect calls via a member function pointer.
Similarly to CFI on virtual and indirect calls, this implementation
tries to use program type information to make the checks as precise
as possible. The basic way that it works is as follows, where `C`
is the name of the class being defined or the target of a call and
the function type is assumed to be `void()`.
For virtual calls:
- Attach type metadata to the addresses of function pointers in vtables
(not the functions themselves) of type `void (B::*)()` for each `B`
that is a recursive dynamic base class of `C`, including `C` itself.
This type metadata has an annotation that the type is for virtual
calls (to distinguish it from the non-virtual case).
- At the call site, check that the computed address of the function
pointer in the vtable has type `void (C::*)()`.
For non-virtual calls:
- Attach type metadata to each non-virtual member function whose address
can be taken with a member function pointer. The type of a function
in class `C` of type `void()` is each of the types `void (B::*)()`
where `B` is a most-base class of `C`. A most-base class of `C`
is defined as a recursive base class of `C`, including `C` itself,
that does not have any bases.
- At the call site, check that the function pointer has one of the types
`void (B::*)()` where `B` is a most-base class of `C`.
Differential Revision: https://reviews.llvm.org/D47567
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@335569 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/cfi')
-rw-r--r-- | test/cfi/mfcall.cpp | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/test/cfi/mfcall.cpp b/test/cfi/mfcall.cpp new file mode 100644 index 000000000..d10105467 --- /dev/null +++ b/test/cfi/mfcall.cpp @@ -0,0 +1,94 @@ +// RUN: %clangxx_cfi -o %t %s +// RUN: %expect_crash %run %t a +// RUN: %expect_crash %run %t b +// RUN: %expect_crash %run %t c +// RUN: %expect_crash %run %t d +// RUN: %expect_crash %run %t e +// RUN: %run %t f +// RUN: %run %t g + +// RUN: %clangxx_cfi_diag -o %t2 %s +// RUN: %run %t2 a 2>&1 | FileCheck --check-prefix=A %s +// RUN: %run %t2 b 2>&1 | FileCheck --check-prefix=B %s +// RUN: %run %t2 c 2>&1 | FileCheck --check-prefix=C %s +// RUN: %run %t2 d 2>&1 | FileCheck --check-prefix=D %s +// RUN: %run %t2 e 2>&1 | FileCheck --check-prefix=E %s + +#include <assert.h> +#include <string.h> + +struct SBase1 { + void b1() {} +}; + +struct SBase2 { + void b2() {} +}; + +struct S : SBase1, SBase2 { + void f1() {} + int f2() { return 1; } + virtual void g1() {} + virtual int g2() { return 1; } + virtual int g3() { return 1; } +}; + +struct T { + void f1() {} + int f2() { return 2; } + virtual void g1() {} + virtual int g2() { return 2; } + virtual void g3() {} +}; + +typedef void (S::*S_void)(); + +typedef int (S::*S_int)(); +typedef int (T::*T_int)(); + +template <typename To, typename From> +To bitcast(From f) { + assert(sizeof(To) == sizeof(From)); + To t; + memcpy(&t, &f, sizeof(f)); + return t; +} + +int main(int argc, char **argv) { + S s; + T t; + + switch (argv[1][0]) { + case 'a': + // A: runtime error: control flow integrity check for type 'int (S::*)()' failed during non-virtual pointer to member function call + // A: note: S::f1() defined here + (s.*bitcast<S_int>(&S::f1))(); + break; + case 'b': + // B: runtime error: control flow integrity check for type 'int (T::*)()' failed during non-virtual pointer to member function call + // B: note: S::f2() defined here + (t.*bitcast<T_int>(&S::f2))(); + break; + case 'c': + // C: runtime error: control flow integrity check for type 'int (S::*)()' failed during virtual pointer to member function call + // C: note: vtable is of type 'S' + (s.*bitcast<S_int>(&S::g1))(); + break; + case 'd': + // D: runtime error: control flow integrity check for type 'int (S::*)()' failed during virtual pointer to member function call + // D: note: vtable is of type 'T' + (reinterpret_cast<S &>(t).*&S::g2)(); + break; + case 'e': + // E: runtime error: control flow integrity check for type 'void (S::*)()' failed during virtual pointer to member function call + // E: note: vtable is of type 'S' + (s.*bitcast<S_void>(&T::g3))(); + break; + case 'f': + (s.*&SBase1::b1)(); + break; + case 'g': + (s.*&SBase2::b2)(); + break; + } +} |