forked from OSchip/llvm-project
Add test suite for the Control Flow Integrity feature.
Differential Revision: http://reviews.llvm.org/D7738 llvm-svn: 230056
This commit is contained in:
parent
a4ccff3281
commit
e0c4f7eb81
|
@ -57,6 +57,7 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS)
|
|||
if(COMPILER_RT_HAS_UBSAN)
|
||||
add_subdirectory(ubsan)
|
||||
endif()
|
||||
add_subdirectory(cfi)
|
||||
endif()
|
||||
|
||||
if(COMPILER_RT_STANDALONE_BUILD)
|
||||
|
|
|
@ -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")
|
|
@ -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
|
|
@ -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
|
|
@ -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")
|
|
@ -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");
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -18,6 +18,8 @@ set_default("llvm_obj_root", "@LLVM_BINARY_DIR@")
|
|||
set_default("compiler_rt_src_root", "@COMPILER_RT_SOURCE_DIR@")
|
||||
set_default("compiler_rt_obj_root", "@COMPILER_RT_BINARY_DIR@")
|
||||
set_default("llvm_tools_dir", "@LLVM_TOOLS_DIR@")
|
||||
set_default("llvm_shlib_dir", "@SHLIBDIR@")
|
||||
set_default("gold_executable", "@GOLD_EXECUTABLE@")
|
||||
set_default("clang", "@COMPILER_RT_TEST_COMPILER@")
|
||||
set_default("compiler_id", "@COMPILER_RT_TEST_COMPILER_ID@")
|
||||
set_default("compiler_rt_arch", "@COMPILER_RT_SUPPORTED_ARCH@")
|
||||
|
|
Loading…
Reference in New Issue