Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add test suite for the Control Flow Integrity feature.
Differential Revision: http://reviews.llvm.org/D7738 llvm-svn: 230056
- Loading branch information
Showing
11 changed files
with
384 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
configure_lit_site_cfg( | ||
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in | ||
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg | ||
) | ||
|
||
set(CFI_TEST_DEPS) | ||
if(NOT COMPILER_RT_STANDALONE_BUILD) | ||
list(APPEND CFI_TEST_DEPS | ||
FileCheck | ||
clang | ||
not | ||
) | ||
if(LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR) | ||
list(APPEND CFI_TEST_DEPS | ||
LLVMgold | ||
) | ||
endif() | ||
endif() | ||
|
||
add_lit_testsuite(check-cfi "Running the cfi regression tests" | ||
${CMAKE_CURRENT_BINARY_DIR} | ||
DEPENDS ${CFI_TEST_DEPS}) | ||
set_target_properties(check-cfi PROPERTIES FOLDER "Tests") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// RUN: %clangxx_cfi -c -DTU1 -o %t1.o %s | ||
// RUN: %clangxx_cfi -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp | ||
// RUN: %clangxx_cfi -o %t %t1.o %t2.o | ||
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s | ||
|
||
// RUN: %clangxx -c -DTU1 -o %t1.o %s | ||
// RUN: %clangxx -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp | ||
// RUN: %clangxx -o %t %t1.o %t2.o | ||
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s | ||
|
||
// Tests that the CFI mechanism treats classes in the anonymous namespace in | ||
// different translation units as having distinct identities. This is done by | ||
// compiling two translation units TU1 and TU2 containing a class named B in an | ||
// anonymous namespace, and testing that the program crashes if TU2 attempts to | ||
// use a TU1 B as a TU2 B. | ||
|
||
// FIXME: This test should not require that the paths supplied to the compiler | ||
// are different. It currently does so because bitset names have global scope | ||
// so we have to mangle the file path into the bitset name. | ||
|
||
#include <stdio.h> | ||
|
||
struct A { | ||
virtual void f() = 0; | ||
}; | ||
|
||
namespace { | ||
|
||
struct B : A { | ||
virtual void f() {} | ||
}; | ||
|
||
} | ||
|
||
A *mkb(); | ||
|
||
#ifdef TU1 | ||
|
||
A *mkb() { | ||
return new B; | ||
} | ||
|
||
#endif // TU1 | ||
|
||
#ifdef TU2 | ||
|
||
int main() { | ||
A *a = mkb(); | ||
|
||
// CFI: 1 | ||
// NCFI: 1 | ||
fprintf(stderr, "1\n"); | ||
|
||
((B *)a)->f(); // UB here | ||
|
||
// CFI-NOT: 2 | ||
// NCFI: 2 | ||
fprintf(stderr, "2\n"); | ||
} | ||
|
||
#endif // TU2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import lit.formats | ||
import os | ||
import subprocess | ||
import sys | ||
|
||
config.name = 'cfi' | ||
config.suffixes = ['.cpp'] | ||
config.test_source_root = os.path.dirname(__file__) | ||
|
||
def is_darwin_lto_supported(): | ||
return os.path.exists(os.path.join(config.llvm_shlib_dir, 'libLTO.dylib')) | ||
|
||
def is_linux_lto_supported(): | ||
if not os.path.exists(os.path.join(config.llvm_shlib_dir, 'LLVMgold.so')): | ||
return False | ||
|
||
ld_cmd = subprocess.Popen([config.gold_executable, '--help'], stdout = subprocess.PIPE) | ||
ld_out = ld_cmd.stdout.read().decode() | ||
ld_cmd.wait() | ||
|
||
if not '-plugin' in ld_out: | ||
return False | ||
|
||
return True | ||
|
||
clangxx = ' '.join([config.clang] + config.cxx_mode_flags) | ||
|
||
config.substitutions.append((r"%clangxx ", clangxx + ' ')) | ||
|
||
if sys.platform == 'darwin' and is_darwin_lto_supported(): | ||
config.substitutions.append((r"%clangxx_cfi ", 'env DYLD_LIBRARY_PATH=' + config.llvm_shlib_dir + ' ' + clangxx + ' -fsanitize=cfi ')) | ||
elif sys.platform.startswith('linux') and is_linux_lto_supported(): | ||
config.substitutions.append((r"%clangxx_cfi ", clangxx + ' -fuse-ld=gold -fsanitize=cfi ')) | ||
else: | ||
config.unsupported = True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") | ||
lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// RUN: %clangxx_cfi -o %t %s | ||
// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s | ||
// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s | ||
|
||
// RUN: %clangxx -o %t %s | ||
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s | ||
// RUN: %t x 2>&1 | FileCheck --check-prefix=NCFI %s | ||
|
||
// Tests that the CFI mechanism is sensitive to multiple inheritance and only | ||
// permits calls via virtual tables for the correct base class. | ||
|
||
#include <stdio.h> | ||
|
||
struct A { | ||
virtual void f() = 0; | ||
}; | ||
|
||
struct B { | ||
virtual void g() = 0; | ||
}; | ||
|
||
struct C : A, B { | ||
virtual void f(), g(); | ||
}; | ||
|
||
void C::f() {} | ||
void C::g() {} | ||
|
||
int main(int argc, char **argv) { | ||
C *c = new C; | ||
|
||
// CFI: 1 | ||
// NCFI: 1 | ||
fprintf(stderr, "1\n"); | ||
|
||
if (argc > 1) { | ||
A *a = c; | ||
((B *)a)->g(); // UB here | ||
} else { | ||
B *b = c; | ||
((A *)b)->f(); // UB here | ||
} | ||
|
||
// CFI-NOT: 2 | ||
// NCFI: 2 | ||
fprintf(stderr, "2\n"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// RUN: %clangxx_cfi -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 a virtual table is | ||
// replaced with a compatible table of function pointers that does not belong to | ||
// any class, by manually overwriting the virtual table of an object and | ||
// attempting to make a call through it. | ||
|
||
#include <stdio.h> | ||
|
||
struct A { | ||
virtual void f(); | ||
}; | ||
|
||
void A::f() {} | ||
|
||
void foo() { | ||
fprintf(stderr, "foo\n"); | ||
} | ||
|
||
void *fake_vtable[] = { (void *)&foo }; | ||
|
||
int main() { | ||
A *a = new A; | ||
*((void **)a) = fake_vtable; // UB here | ||
|
||
// CFI: 1 | ||
// NCFI: 1 | ||
fprintf(stderr, "1\n"); | ||
|
||
// CFI-NOT: foo | ||
// NCFI: foo | ||
a->f(); | ||
|
||
// CFI-NOT: 2 | ||
// NCFI: 2 | ||
fprintf(stderr, "2\n"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// RUN: %clangxx_cfi -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 virtual call | ||
// to an object of the wrong class but with a compatible vtable, by casting a | ||
// pointer to such an object and attempting to make a call through it. | ||
|
||
#include <stdio.h> | ||
|
||
struct A { | ||
virtual void f(); | ||
}; | ||
|
||
void A::f() {} | ||
|
||
struct B { | ||
virtual void f(); | ||
}; | ||
|
||
void B::f() {} | ||
|
||
int main() { | ||
A *a = new A; | ||
|
||
// CFI: 1 | ||
// NCFI: 1 | ||
fprintf(stderr, "1\n"); | ||
|
||
((B *)a)->f(); // UB here | ||
|
||
// CFI-NOT: 2 | ||
// NCFI: 2 | ||
fprintf(stderr, "2\n"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// RUN: %clangxx_cfi -o %t %s | ||
// RUN: %t | ||
|
||
// 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. | ||
|
||
inline void break_optimization(void *arg) { | ||
__asm__ __volatile__("" : : "r" (arg) : "memory"); | ||
} | ||
|
||
struct A { | ||
virtual void f(); | ||
}; | ||
|
||
void A::f() {} | ||
|
||
struct A2 : A { | ||
virtual void f(); | ||
}; | ||
|
||
void A2::f() {} | ||
|
||
struct B { | ||
virtual void f() {} | ||
}; | ||
|
||
struct B2 : B { | ||
virtual void f() {} | ||
}; | ||
|
||
namespace { | ||
|
||
struct C { | ||
virtual void f(); | ||
}; | ||
|
||
void C::f() {} | ||
|
||
struct C2 : C { | ||
virtual void f(); | ||
}; | ||
|
||
void C2::f() {} | ||
|
||
struct D { | ||
virtual void f() {} | ||
}; | ||
|
||
struct D2 : D { | ||
virtual void f() {} | ||
}; | ||
|
||
} | ||
|
||
struct E { | ||
virtual void f() {} | ||
}; | ||
|
||
struct E2 : virtual E { | ||
virtual void f() {} | ||
}; | ||
|
||
int main() { | ||
A *a = new A; | ||
break_optimization(a); | ||
a->f(); | ||
a = new A2; | ||
break_optimization(a); | ||
a->f(); | ||
|
||
B *b = new B; | ||
break_optimization(b); | ||
b->f(); | ||
b = new B2; | ||
break_optimization(b); | ||
b->f(); | ||
|
||
C *c = new C; | ||
break_optimization(c); | ||
c->f(); | ||
c = new C2; | ||
break_optimization(c); | ||
c->f(); | ||
|
||
D *d = new D; | ||
break_optimization(d); | ||
d->f(); | ||
d = new D2; | ||
break_optimization(d); | ||
d->f(); | ||
|
||
E *e = new E; | ||
break_optimization(e); | ||
e->f(); | ||
e = new E2; | ||
break_optimization(e); | ||
e->f(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// RUN: %clangxx_cfi -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 enforcement also applies to virtual destructor calls made | ||
// via 'delete'. | ||
|
||
#include <stdio.h> | ||
|
||
struct A { | ||
virtual ~A(); | ||
}; | ||
|
||
A::~A() {} | ||
|
||
struct B { | ||
virtual ~B(); | ||
}; | ||
|
||
B::~B() {} | ||
|
||
int main() { | ||
A *a = new A; | ||
|
||
// CFI: 1 | ||
// NCFI: 1 | ||
fprintf(stderr, "1\n"); | ||
|
||
delete (B *)a; // UB here | ||
|
||
// CFI-NOT: 2 | ||
// NCFI: 2 | ||
fprintf(stderr, "2\n"); | ||
} |
Oops, something went wrong.