Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,98 @@
set(CFI_TESTSUITES)
macro (add_cfi_test_suites lld thinlto)
set(suffix)
if (${lld})
set(suffix ${suffix}-lld)
endif()
if (${thinlto})
set(suffix ${suffix}-thinlto)
endif()
set(suffix ${suffix}-${CFI_TEST_TARGET_ARCH})
set(CFI_TEST_USE_LLD ${lld})
set(CFI_TEST_USE_THINLTO ${thinlto})
set(CFI_LIT_TEST_MODE Standalone)
set(CFI_TEST_CONFIG_SUFFIX -standalone${suffix})
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/Standalone${suffix}/lit.site.cfg
)
list(APPEND CFI_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Standalone${suffix})
set(CFI_LIT_TEST_MODE Devirt)
set(CFI_TEST_CONFIG_SUFFIX -devirt${suffix})
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/Devirt${suffix}/lit.site.cfg
)
list(APPEND CFI_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Devirt${suffix})
endmacro()
set(CFI_TEST_ARCH ${CFI_SUPPORTED_ARCH})
if(APPLE)
darwin_filter_host_archs(CFI_SUPPORTED_ARCH CFI_TEST_ARCH)
endif()
foreach(arch ${CFI_TEST_ARCH})
set(CFI_TEST_TARGET_ARCH ${arch})
get_test_cc_for_arch(${arch} CFI_TEST_TARGET_CC CFI_TEST_TARGET_CFLAGS)
if (APPLE)
# FIXME: enable ThinLTO tests after fixing http://llvm.org/pr32741
add_cfi_test_suites(False False)
elseif(WIN32)
add_cfi_test_suites(True False)
add_cfi_test_suites(True True)
else()
add_cfi_test_suites(False False)
add_cfi_test_suites(False True)
if (COMPILER_RT_HAS_LLD AND NOT arch STREQUAL "i386")
add_cfi_test_suites(True False)
add_cfi_test_suites(True True)
endif()
endif()
endforeach()
set(CFI_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
list(APPEND CFI_TEST_DEPS
ubsan
stats
)
if(COMPILER_RT_HAS_CFI)
list(APPEND CFI_TEST_DEPS cfi)
endif()
if(NOT COMPILER_RT_STANDALONE_BUILD)
list(APPEND CFI_TEST_DEPS
opt
sanstats
)
if(LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR)
list(APPEND CFI_TEST_DEPS
LLVMgold
)
endif()
if(APPLE)
list(APPEND CFI_TEST_DEPS
LTO
)
endif()
if(NOT APPLE AND COMPILER_RT_HAS_LLD)
list(APPEND CFI_TEST_DEPS
lld
)
endif()
endif()
add_lit_testsuite(check-cfi "Running the cfi regression tests"
${CFI_TESTSUITES}
DEPENDS ${CFI_TEST_DEPS})
add_lit_target(check-cfi-and-supported "Running the cfi regression tests"
${CFI_TESTSUITES}
PARAMS check_supported=1
DEPENDS ${CFI_TEST_DEPS})
set_target_properties(check-cfi PROPERTIES FOLDER "Compiler-RT Misc")
set_target_properties(check-cfi-and-supported PROPERTIES FOLDER "Compiler-RT Misc")

View File

@@ -0,0 +1,8 @@
The tests in this directory use a common convention for exercising the
functionality associated with bit sets of different sizes. When certain
macros are defined the tests instantiate classes that force the bit sets
to be of certain sizes.
- B32 forces 32-bit bit sets.
- B64 forces 64-bit bit sets.
- BM forces memory bit sets.

View File

@@ -0,0 +1,91 @@
// 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 %t1 %t1.o %t2.o
// RUN: %expect_crash %run %t1 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -c -DTU1 -DB32 -o %t1.o %s
// RUN: %clangxx_cfi -c -DTU2 -DB32 -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx_cfi -o %t2 %t1.o %t2.o
// RUN: %expect_crash %run %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -c -DTU1 -DB64 -o %t1.o %s
// RUN: %clangxx_cfi -c -DTU2 -DB64 -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx_cfi -o %t3 %t1.o %t2.o
// RUN: %expect_crash %run %t3 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -c -DTU1 -DBM -o %t1.o %s
// RUN: %clangxx_cfi -c -DTU2 -DBM -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx_cfi -o %t4 %t1.o %t2.o
// RUN: %expect_crash %run %t4 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 %t5 %t1.o %t2.o
// RUN: %run %t5 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx_cfi_diag -c -DTU1 -o %t1.o %s
// RUN: %clangxx_cfi_diag -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp
// RUN: %clangxx_cfi_diag -o %t6 %t1.o %t2.o
// RUN: %run %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %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.
// REQUIRES: cxxabi
#include <stdio.h>
#include "utils.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() {
create_derivers<B>();
A *a = mkb();
break_optimization(a);
// CFI: 1
// NCFI: 1
fprintf(stderr, "1\n");
// CFI-DIAG: runtime error: control flow integrity check for type '(anonymous namespace)::B' failed during base-to-derived cast
// CFI-DIAG-NEXT: note: vtable is of type '{{.*}}anonymous namespace{{.*}}::B'
// CFI-DIAG: runtime error: control flow integrity check for type '(anonymous namespace)::B' failed during virtual call
// CFI-DIAG-NEXT: note: vtable is of type '{{.*}}anonymous namespace{{.*}}::B'
((B *)a)->f(); // UB here
// CFI-NOT: {{^2$}}
// NCFI: {{^2$}}
fprintf(stderr, "2\n");
}
#endif // TU2

View File

@@ -0,0 +1,137 @@
// RUN: %clangxx_cfi -o %t1 %s
// RUN: %expect_crash %run %t1 a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t1 b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t1 c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %run %t1 d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t1 e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t1 f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %expect_crash %run %t1 g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %run %t1 h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -DB32 -o %t2 %s
// RUN: %expect_crash %run %t2 a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t2 b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t2 c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %run %t2 d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t2 e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t2 f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %expect_crash %run %t2 g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %run %t2 h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -DB64 -o %t3 %s
// RUN: %expect_crash %run %t3 a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t3 b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t3 c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %run %t3 d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t3 e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t3 f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %expect_crash %run %t3 g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %run %t3 h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -DBM -o %t4 %s
// RUN: %expect_crash %run %t4 a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t4 b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t4 c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %run %t4 d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t4 e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t4 f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %expect_crash %run %t4 g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %run %t4 h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi -fsanitize=cfi-cast-strict -o %t5 %s
// RUN: %expect_crash %run %t5 a 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t5 b 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t5 c 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t5 d 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t5 e 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t5 f 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t5 g 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %expect_crash %run %t5 h 2>&1 | FileCheck --check-prefix=FAIL %s
// RUN: %clangxx -o %t6 %s
// RUN: %run %t6 a 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t6 b 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t6 c 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t6 d 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t6 e 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t6 f 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t6 g 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %run %t6 h 2>&1 | FileCheck --check-prefix=PASS %s
// RUN: %clangxx_cfi_diag -o %t7 %s
// RUN: %run %t7 a 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s
// RUN: %run %t7 b 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s
// RUN: %run %t7 c 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s
// RUN: %run %t7 g 2>&1 | FileCheck --check-prefix=CFI-DIAG-U %s
// Tests that the CFI enforcement detects bad casts.
// REQUIRES: cxxabi
#include <stdio.h>
#include "utils.h"
struct A {
virtual void f();
};
void A::f() {}
struct B : A {
virtual void f();
};
void B::f() {}
struct C : A {
};
int main(int argc, char **argv) {
create_derivers<B>();
B *b = new B;
break_optimization(b);
// FAIL: 1
// PASS: 1
fprintf(stderr, "1\n");
A a;
// CFI-DIAG-D: runtime error: control flow integrity check for type 'B' failed during base-to-derived cast
// CFI-DIAG-D-NEXT: note: vtable is of type '{{(struct )?}}A'
// CFI-DIAG-U: runtime error: control flow integrity check for type 'B' failed during cast to unrelated type
// CFI-DIAG-U-NEXT: note: vtable is of type '{{(struct )?}}A'
switch (argv[1][0]) {
case 'a':
static_cast<B *>(&a); // UB
break;
case 'b':
static_cast<B &>(a); // UB
break;
case 'c':
static_cast<B &&>(a); // UB
break;
case 'd':
static_cast<C *>(&a); // UB, strict only
break;
case 'e':
static_cast<C &>(a); // UB, strict only
break;
case 'f':
static_cast<C &&>(a); // UB, strict only
break;
case 'g':
static_cast<B *>(static_cast<void *>(&a)); // Non-UB bad cast
break;
case 'h':
static_cast<C *>(static_cast<void *>(&a)); // Non-UB bad cast, strict only
break;
}
// FAIL-NOT: {{^2$}}
// PASS: {{^2$}}
fprintf(stderr, "2\n");
}

View File

@@ -0,0 +1,21 @@
// GlobalSplit used to lose type metadata for classes with virtual bases but no virtual methods.
// RUN: %clangxx_cfi -o %t1 %s && %run %t1
// UNSUPPORTED: win32
struct Z {
};
struct ZZ : public virtual Z {
};
struct A : public ZZ {
};
struct B : public A {
};
int main() {
A* a = new B();
B *b = (B*)a;
}

View File

@@ -0,0 +1,93 @@
// RUN: %clangxx_cfi -o %t1 %s
// RUN: %expect_crash %run %t1 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB32 -o %t2 %s
// RUN: %expect_crash %run %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DB64 -o %t3 %s
// RUN: %expect_crash %run %t3 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -DBM -o %t4 %s
// RUN: %expect_crash %run %t4 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -o %t5 %s
// RUN: %expect_crash %run %t5 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -DB32 -o %t6 %s
// RUN: %expect_crash %run %t6 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -DB64 -o %t7 %s
// RUN: %expect_crash %run %t7 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O1 -DBM -o %t8 %s
// RUN: %expect_crash %run %t8 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -o %t9 %s
// RUN: %expect_crash %run %t9 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -DB32 -o %t10 %s
// RUN: %expect_crash %run %t10 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -DB64 -o %t11 %s
// RUN: %expect_crash %run %t11 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O2 -DBM -o %t12 %s
// RUN: %expect_crash %run %t12 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -o %t13 %s
// RUN: %expect_crash %run %t13 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -DB32 -o %t14 %s
// RUN: %expect_crash %run %t14 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -DB64 -o %t15 %s
// RUN: %expect_crash %run %t15 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi -O3 -DBM -o %t16 %s
// RUN: %expect_crash %run %t16 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi_diag -o %t17 %s
// RUN: %run %t17 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
// RUN: %clangxx -o %t18 %s
// RUN: %run %t18 2>&1 | FileCheck --check-prefix=NCFI %s
// Tests that the CFI mechanism crashes the program when making a
// base-to-derived cast from a destructor of the base class,
// where both types have virtual tables.
// REQUIRES: cxxabi
#include <stdio.h>
#include "utils.h"
template<typename T>
class A {
public:
T* context() { return static_cast<T*>(this); }
virtual ~A() {
break_optimization(context());
}
};
class B : public A<B> {
public:
virtual ~B() { }
};
int main() {
// CFI: 1
// NCFI: 1
fprintf(stderr, "1\n");
// CFI-DIAG: runtime error: control flow integrity check for type 'B' failed during base-to-derived cast
// CFI-DIAG-NEXT: note: vtable is of type '{{(class )?}}A<{{(class )?}}B>'
B* b = new B;
break_optimization(b);
delete b; // UB here
// CFI-NOT: {{^2$}}
// NCFI: {{^2$}}
fprintf(stderr, "2\n");
}

View File

@@ -0,0 +1,21 @@
REQUIRES: asserts
%% Explicit -flto to override possible -flto=thin in %clangxx_cfi
RUN: %clangxx_cfi -flto -c -o %t1.o %S/simple-fail.cpp
RUN: opt -lowertypetests -debug-only=lowertypetests -o /dev/null %t1.o 2>&1 | FileCheck --check-prefix=B0 %s
B0: {{1B|B@@}}: {{.*}} size 1
RUN: %clangxx_cfi -DB32 -flto -c -o %t2.o %S/simple-fail.cpp
RUN: opt -lowertypetests -debug-only=lowertypetests -o /dev/null %t2.o 2>&1 | FileCheck --check-prefix=B32 %s
B32: {{1B|B@@}}: {{.*}} size 24
B32-NOT: all-ones
RUN: %clangxx_cfi -DB64 -flto -c -o %t3.o %S/simple-fail.cpp
RUN: opt -lowertypetests -debug-only=lowertypetests -o /dev/null %t3.o 2>&1 | FileCheck --check-prefix=B64 %s
B64: {{1B|B@@}}: {{.*}} size 54
B64-NOT: all-ones
RUN: %clangxx_cfi -DBM -flto -c -o %t4.o %S/simple-fail.cpp
RUN: opt -lowertypetests -debug-only=lowertypetests -o /dev/null %t4.o 2>&1 | FileCheck --check-prefix=BM %s
BM: {{1B|B@@}}: {{.*}} size 84
BM-NOT: all-ones

View File

@@ -0,0 +1,159 @@
// Cross-DSO diagnostics.
// The rules are:
// * If the library needs diagnostics, the main executable must request at
// least some diagnostics as well (to link the diagnostic runtime).
// * -fsanitize-trap on the caller side overrides everything.
// * otherwise, the callee decides between trap/recover/norecover.
// Full-recover.
// RUN: %clangxx_cfi_dso_diag -g -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso_diag -g %s -o %t %ld_flags_rpath_exe
// RUN: %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-DIAG \
// RUN: --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER
// RUN: %t i_v 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-NODIAG \
// RUN: --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER
// RUN: %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \
// RUN: --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER
// RUN: %t ic_ 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-DIAG \
// RUN: --check-prefix=VCALL-NODIAG --check-prefix=ALL-RECOVER
// Trap on icall, no-recover on cast.
// RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \
// RUN: -g -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \
// RUN: -g %s -o %t %ld_flags_rpath_exe
// RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
// RUN: --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL
// RUN: not %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \
// RUN: --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL
// RUN: %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
// RUN: --check-prefix=VCALL-DIAG
// Callee: trap on icall, no-recover on cast.
// Caller: recover on everything.
// The same as in the previous case, behaviour is decided by the callee.
// RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \
// RUN: -g -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso_diag \
// RUN: -g %s -o %t %ld_flags_rpath_exe
// RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
// RUN: --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL
// RUN: not %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \
// RUN: --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL
// RUN: %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
// RUN: --check-prefix=VCALL-DIAG
// Caller in trapping mode, callee with full diagnostic+recover.
// Caller wins.
// cfi-nvcall is non-trapping in the main executable to link the diagnostic runtime library.
// RUN: %clangxx_cfi_dso_diag \
// RUN: -g -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso -fno-sanitize-trap=cfi-nvcall \
// RUN: -g %s -o %t %ld_flags_rpath_exe
// RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
// RUN: --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL
// RUN: %expect_crash %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
// RUN: --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL
// RUN: %expect_crash %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
// RUN: --check-prefix=VCALL-NODIAG --check-prefix=VCALL-FATAL
// REQUIRES: cxxabi
#include <assert.h>
#include <stdio.h>
#include <string.h>
struct A {
virtual void f();
};
void *create_B();
#ifdef SHARED_LIB
#include "../../utils.h"
struct B {
virtual void f();
};
void B::f() {}
void *create_B() {
create_derivers<B>();
return (void *)(new B());
}
#else
void A::f() {}
int main(int argc, char *argv[]) {
assert(argc == 2);
assert(strlen(argv[1]) == 3);
// ICALL-FATAL: =0=
// CAST-FATAL: =0=
// VCALL-FATAL: =0=
// ALL-RECOVER: =0=
fprintf(stderr, "=0=\n");
void *p;
if (argv[1][0] == 'i') {
// ICALL-DIAG: runtime error: control flow integrity check for type 'void *(int)' failed during indirect function call
// ICALL-DIAG-NEXT: note: create_B() defined here
// ICALL-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during indirect function call
p = ((void *(*)(int))create_B)(42);
} else {
p = create_B();
}
// ICALL-FATAL-NOT: =1=
// CAST-FATAL: =1=
// VCALL-FATAL: =1=
// ALL-RECOVER: =1=
fprintf(stderr, "=1=\n");
A *a;
if (argv[1][1] == 'c') {
// CAST-DIAG: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type
// CAST-DIAG-NEXT: note: vtable is of type '{{(struct )?}}B'
// CAST-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during cast to unrelated type
a = (A*)p;
} else {
// Invisible to CFI.
memcpy(&a, &p, sizeof(a));
}
// ICALL-FATAL-NOT: =2=
// CAST-FATAL-NOT: =2=
// VCALL-FATAL: =2=
// ALL-RECOVER: =2=
fprintf(stderr, "=2=\n");
// VCALL-DIAG: runtime error: control flow integrity check for type 'A' failed during virtual call
// VCALL-DIAG-NEXT: note: vtable is of type '{{(struct )?}}B'
// VCALL-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during virtual call
if (argv[1][2] == 'v') {
a->f(); // UB here
}
// ICALL-FATAL-NOT: =3=
// CAST-FATAL-NOT: =3=
// VCALL-FATAL-NOT: =3=
// ALL-RECOVER: =3=
fprintf(stderr, "=3=\n");
}
#endif

View File

@@ -0,0 +1,147 @@
// RUN: %clangxx_cfi_dso -DSHARED_LIB %s -fPIC -shared -o %t1-so.so
// RUN: %clangxx_cfi_dso %s -o %t1
// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t1 cast 2>&1 | FileCheck --check-prefix=CFI-CAST %s
// RUN: %expect_crash %t1 dlclose 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi_dso -DB32 -DSHARED_LIB %s -fPIC -shared -o %t2-so.so
// RUN: %clangxx_cfi_dso -DB32 %s -o %t2
// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t2 cast 2>&1 | FileCheck --check-prefix=CFI-CAST %s
// RUN: %expect_crash %t2 dlclose 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi_dso -DB64 -DSHARED_LIB %s -fPIC -shared -o %t3-so.so
// RUN: %clangxx_cfi_dso -DB64 %s -o %t3
// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t3 cast 2>&1 | FileCheck --check-prefix=CFI-CAST %s
// RUN: %expect_crash %t3 dlclose 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi_dso -DBM -DSHARED_LIB %s -fPIC -shared -o %t4-so.so
// RUN: %clangxx_cfi_dso -DBM %s -o %t4
// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t4 cast 2>&1 | FileCheck --check-prefix=CFI-CAST %s
// RUN: %expect_crash %t4 dlclose 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx -g -DBM -DSHARED_LIB -DNOCFI %s -fPIC -shared -o %t5-so.so
// RUN: %clangxx -g -DBM -DNOCFI %s -ldl -o %t5
// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %t5 cast 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %t5 dlclose 2>&1 | FileCheck --check-prefix=NCFI %s
// Test that calls to uninstrumented library are unchecked.
// RUN: %clangxx -DBM -DSHARED_LIB %s -fPIC -shared -o %t6-so.so
// RUN: %clangxx_cfi_dso -DBM %s -o %t6
// RUN: %t6 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %t6 cast 2>&1 | FileCheck --check-prefix=NCFI %s
// Call-after-dlclose is checked on the caller side.
// RUN: %expect_crash %t6 dlclose 2>&1 | FileCheck --check-prefix=CFI %s
// Tests calls into dlopen-ed library.
// REQUIRES: cxxabi
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <string>
struct A {
virtual void f();
};
#ifdef SHARED_LIB
#include "../../utils.h"
struct B {
virtual void f();
};
void B::f() {}
extern "C" void *create_B() {
create_derivers<B>();
return (void *)(new B());
}
extern "C" __attribute__((aligned(4096))) void do_nothing() {}
#else
void A::f() {}
static const int kCodeAlign = 4096;
static const int kCodeSize = 4096;
static char saved_code[kCodeSize];
static char *real_start;
static void save_code(char *p) {
real_start = (char *)(((uintptr_t)p) & ~(kCodeAlign - 1));
memcpy(saved_code, real_start, kCodeSize);
}
static void restore_code() {
char *code = (char *)mmap(real_start, kCodeSize, PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0);
assert(code == real_start);
memcpy(code, saved_code, kCodeSize);
}
int main(int argc, char *argv[]) {
const bool test_cast = argc > 1 && strcmp(argv[1], "cast") == 0;
const bool test_dlclose = argc > 1 && strcmp(argv[1], "dlclose") == 0;
std::string name = std::string(argv[0]) + "-so.so";
void *handle = dlopen(name.c_str(), RTLD_NOW);
assert(handle);
void *(*create_B)() = (void *(*)())dlsym(handle, "create_B");
assert(create_B);
void *p = create_B();
A *a;
// CFI: =0=
// CFI-CAST: =0=
// NCFI: =0=
fprintf(stderr, "=0=\n");
if (test_cast) {
// Test cast. BOOM.
a = (A*)p;
} else {
// Invisible to CFI. Test virtual call later.
memcpy(&a, &p, sizeof(a));
}
// CFI: =1=
// CFI-CAST-NOT: =1=
// NCFI: =1=
fprintf(stderr, "=1=\n");
if (test_dlclose) {
// Imitate an attacker sneaking in an executable page where a dlclose()d
// library was loaded. This needs to pass w/o CFI, so for the testing
// purpose, we just copy the bytes of a "void f() {}" function back and
// forth.
void (*do_nothing)() = (void (*)())dlsym(handle, "do_nothing");
assert(do_nothing);
save_code((char *)do_nothing);
int res = dlclose(handle);
assert(res == 0);
restore_code();
do_nothing(); // UB here
} else {
a->f(); // UB here
}
// CFI-NOT: =2=
// CFI-CAST-NOT: =2=
// NCFI: =2=
fprintf(stderr, "=2=\n");
}
#endif

View File

@@ -0,0 +1,34 @@
// RUN: %clangxx_cfi_dso -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso %s -o %t %ld_flags_rpath_exe && %expect_crash %t 2>&1 | FileCheck %s
// RUN: %clangxx_cfi_dso_diag -g -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso_diag -g %s -o %t %ld_flags_rpath_exe && %t 2>&1 | FileCheck %s --check-prefix=CFI-DIAG
#include <stdio.h>
#ifdef SHARED_LIB
void g();
void f() {
// CHECK-DIAG: =1=
// CHECK: =1=
fprintf(stderr, "=1=\n");
((void (*)(void))g)();
// CHECK-DIAG: =2=
// CHECK: =2=
fprintf(stderr, "=2=\n");
// CFI-DIAG: runtime error: control flow integrity check for type 'void (int)' failed during indirect function call
// CFI-DIAG-NEXT: note: g() defined here
((void (*)(int))g)(42); // UB here
// CHECK-DIAG: =3=
// CHECK-NOT: =3=
fprintf(stderr, "=3=\n");
}
#else
void f();
void g() {
}
int main() {
f();
}
#endif

View File

@@ -0,0 +1,29 @@
// RUN: %clangxx_cfi_dso -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso %s -o %t %ld_flags_rpath_exe && %expect_crash %t 2>&1 | FileCheck %s
// RUN: %clangxx_cfi_dso_diag -g -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso_diag -g %s -o %t %ld_flags_rpath_exe && %t 2>&1 | FileCheck %s --check-prefix=CFI-DIAG
#include <stdio.h>
#ifdef SHARED_LIB
void f() {
}
#else
void f();
int main() {
// CHECK-DIAG: =1=
// CHECK: =1=
fprintf(stderr, "=1=\n");
((void (*)(void))f)();
// CHECK-DIAG: =2=
// CHECK: =2=
fprintf(stderr, "=2=\n");
// CFI-DIAG: runtime error: control flow integrity check for type 'void (int)' failed during indirect function call
// CFI-DIAG-NEXT: note: f() defined here
((void (*)(int))f)(42); // UB here
// CHECK-DIAG: =3=
// CHECK-NOT: =3=
fprintf(stderr, "=3=\n");
}
#endif

View File

@@ -0,0 +1,3 @@
# The cfi-icall checker is only supported on x86 and x86_64 for now.
if config.root.host_arch not in ['x86', 'x86_64']:
config.unsupported = True

View File

@@ -0,0 +1,13 @@
def getRoot(config):
if not config.parent:
return config
return getRoot(config.parent)
root = getRoot(config)
if root.host_os not in ['Linux']:
config.unsupported = True
# Android O (API level 26) has support for cross-dso cfi in libdl.so.
if config.android and 'android-26' not in config.available_features:
config.unsupported = True

View File

@@ -0,0 +1,88 @@
// RUN: %clangxx_cfi_dso -std=c++11 -g -DSHARED_LIB %s -fPIC -shared -o %t-cfi-so.so
// RUN: %clangxx -std=c++11 -g -DSHARED_LIB %s -fPIC -shared -o %t-nocfi-so.so
// RUN: %clangxx_cfi_dso -std=c++11 -g %s -o %t
// RUN: %expect_crash %t start 2>&1 | FileCheck %s
// RUN: %expect_crash %t mmap 2>&1 | FileCheck %s
// RUN: %expect_crash %t dlopen %t-cfi-so.so 2>&1 | FileCheck %s
// RUN: %expect_crash %t dlclose %t-cfi-so.so 2>&1 | FileCheck %s
// RUN: %expect_crash %t dlopen %t-nocfi-so.so 2>&1 | FileCheck %s
// RUN: %expect_crash %t dlclose %t-nocfi-so.so 2>&1 | FileCheck %s
// Tests that shadow is read-only most of the time.
// REQUIRES: cxxabi
// Uses private API that is not available on Android.
// UNSUPPORTED: android
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
struct A {
virtual void f();
};
#ifdef SHARED_LIB
void A::f() {}
extern "C" A *create_A() { return new A(); }
#else
constexpr unsigned kShadowGranularity = 12;
namespace __cfi {
uintptr_t GetShadow();
}
void write_shadow(void *ptr) {
uintptr_t base = __cfi::GetShadow();
uint16_t *s =
(uint16_t *)(base + (((uintptr_t)ptr >> kShadowGranularity) << 1));
fprintf(stderr, "going to crash\n");
// CHECK: going to crash
*s = 42;
fprintf(stderr, "did not crash\n");
// CHECK-NOT: did not crash
exit(1);
}
int main(int argc, char *argv[]) {
assert(argc > 1);
const bool test_mmap = strcmp(argv[1], "mmap") == 0;
const bool test_start = strcmp(argv[1], "start") == 0;
const bool test_dlopen = strcmp(argv[1], "dlopen") == 0;
const bool test_dlclose = strcmp(argv[1], "dlclose") == 0;
const char *lib = argc > 2 ? argv[2] : nullptr;
if (test_start)
write_shadow((void *)&main);
if (test_mmap) {
void *p = mmap(nullptr, 1 << 20, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
assert(p != MAP_FAILED);
write_shadow((char *)p + 100);
} else {
void *handle = dlopen(lib, RTLD_NOW);
assert(handle);
void *create_A = dlsym(handle, "create_A");
assert(create_A);
if (test_dlopen)
write_shadow(create_A);
int res = dlclose(handle);
assert(res == 0);
if (test_dlclose)
write_shadow(create_A);
}
}
#endif

View File

@@ -0,0 +1,101 @@
// RUN: %clangxx_cfi_dso -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso %s -o %t %ld_flags_rpath_exe
// RUN: %expect_crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t x 2>&1 | FileCheck --check-prefix=CFI-CAST %s
// RUN: %clangxx_cfi_dso -DB32 -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso -DB32 %s -o %t %ld_flags_rpath_exe
// RUN: %expect_crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t x 2>&1 | FileCheck --check-prefix=CFI-CAST %s
// RUN: %clangxx_cfi_dso -DB64 -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso -DB64 %s -o %t %ld_flags_rpath_exe
// RUN: %expect_crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t x 2>&1 | FileCheck --check-prefix=CFI-CAST %s
// RUN: %clangxx_cfi_dso -DBM -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso -DBM %s -o %t %ld_flags_rpath_exe
// RUN: %expect_crash %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %expect_crash %t x 2>&1 | FileCheck --check-prefix=CFI-CAST %s
// RUN: %clangxx -DBM -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx -DBM %s -o %t %ld_flags_rpath_exe
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %t x 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx -DBM -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso -DBM %s -o %t %ld_flags_rpath_exe
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %t x 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clangxx_cfi_dso_diag -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso_diag %s -o %t %ld_flags_rpath_exe
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI-DIAG-CALL %s
// RUN: %t x 2>&1 | FileCheck --check-prefix=CFI-DIAG-CALL --check-prefix=CFI-DIAG-CAST %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.
// REQUIRES: cxxabi
#include <stdio.h>
#include <string.h>
struct A {
virtual void f();
};
void *create_B();
#ifdef SHARED_LIB
#include "../utils.h"
struct B {
virtual void f();
};
void B::f() {}
void *create_B() {
create_derivers<B>();
return (void *)(new B());
}
#else
void A::f() {}
int main(int argc, char *argv[]) {
void *p = create_B();
A *a;
// CFI: =0=
// CFI-CAST: =0=
// NCFI: =0=
fprintf(stderr, "=0=\n");
if (argc > 1 && argv[1][0] == 'x') {
// Test cast. BOOM.
// CFI-DIAG-CAST: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type
// CFI-DIAG-CAST-NEXT: note: vtable is of type '{{(struct )?}}B'
a = (A*)p;
} else {
// Invisible to CFI. Test virtual call later.
memcpy(&a, &p, sizeof(a));
}
// CFI: =1=
// CFI-CAST-NOT: =1=
// NCFI: =1=
fprintf(stderr, "=1=\n");
// CFI-DIAG-CALL: runtime error: control flow integrity check for type 'A' failed during virtual call
// CFI-DIAG-CALL-NEXT: note: vtable is of type '{{(struct )?}}B'
a->f(); // UB here
// CFI-NOT: =2=
// CFI-CAST-NOT: =2=
// NCFI: =2=
fprintf(stderr, "=2=\n");
}
#endif

View File

@@ -0,0 +1,65 @@
// RUN: %clangxx_cfi_dso -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso -g %s -o %t %ld_flags_rpath_exe
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi_dso -DB32 -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso -DB32 %s -o %t %ld_flags_rpath_exe
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi_dso -DB64 -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso -DB64 %s -o %t %ld_flags_rpath_exe
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx_cfi_dso -DBM -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso -DBM %s -o %t %ld_flags_rpath_exe
// RUN: %t 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clangxx -DBM -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx -DBM %s -o %t %ld_flags_rpath_exe
// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %t x 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.
// REQUIRES: cxxabi
#include <stdio.h>
#include <string.h>
struct A {
virtual void f();
};
A *create_B();
#ifdef SHARED_LIB
#include "../utils.h"
struct B : public A {
virtual void f();
};
void B::f() {}
A *create_B() {
create_derivers<B>();
return new B();
}
#else
void A::f() {}
int main(int argc, char *argv[]) {
A *a = create_B();
// CFI: =1=
// NCFI: =1=
fprintf(stderr, "=1=\n");
a->f(); // OK
// CFI: =2=
// NCFI: =2=
fprintf(stderr, "=2=\n");
}
#endif

View File

@@ -0,0 +1,65 @@
// RUN: %clangxx_cfi_dso -DSHARED_LIB -fPIC -g -fsanitize-stats -shared -o %t.so %s
// RUN: %clangxx_cfi_dso -g -fsanitize-stats -o %t %s %t.so
// RUN: env SANITIZER_STATS_PATH=%t.stats %t
// RUN: sanstats %t.stats | FileCheck %s
// CFI-icall is not implemented in thinlto mode => ".cfi" suffixes are missing
// in sanstats output.
// FIXME: %t.stats must be transferred from device to host for this to work on Android.
// XFAIL: android
struct ABase {};
struct A : ABase {
virtual void vf() {}
void nvf() {}
};
extern "C" void vcall(A *a);
extern "C" void nvcall(A *a);
#ifdef SHARED_LIB
extern "C" __attribute__((noinline)) void vcall(A *a) {
// CHECK: stats.cpp:[[@LINE+1]] vcall.cfi cfi-vcall 37
a->vf();
}
extern "C" __attribute__((noinline)) void nvcall(A *a) {
// CHECK: stats.cpp:[[@LINE+1]] nvcall.cfi cfi-nvcall 51
a->nvf();
}
#else
extern "C" __attribute__((noinline)) A *dcast(A *a) {
// CHECK: stats.cpp:[[@LINE+1]] dcast.cfi cfi-derived-cast 24
return (A *)(ABase *)a;
}
extern "C" __attribute__((noinline)) A *ucast(A *a) {
// CHECK: stats.cpp:[[@LINE+1]] ucast.cfi cfi-unrelated-cast 81
return (A *)(char *)a;
}
extern "C" __attribute__((noinline)) void unreachable(A *a) {
// CHECK-NOT: unreachable
a->vf();
}
int main() {
A a;
for (unsigned i = 0; i != 37; ++i)
vcall(&a);
for (unsigned i = 0; i != 51; ++i)
nvcall(&a);
for (unsigned i = 0; i != 24; ++i)
dcast(&a);
for (unsigned i = 0; i != 81; ++i)
ucast(&a);
for (unsigned i = 0; i != 0; ++i)
unreachable(&a);
}
#endif

View File

@@ -0,0 +1,64 @@
// RUN: %clangxx_cfi_dso_diag -std=c++11 %s -o %t
// RUN: %t zero 2>&1 | FileCheck --check-prefix=CHECK-ZERO %s
// RUN: %t unaddressable 2>&1 | FileCheck --check-prefix=CHECK-UNADDR %s
// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-TYPEINFO %s
// RUN: %clangxx_cfi_diag -std=c++11 %s -o %t2
// RUN: %t2 zero 2>&1 | FileCheck --check-prefix=CHECK-ZERO %s
// RUN: %t2 unaddressable 2>&1 | FileCheck --check-prefix=CHECK-UNADDR %s
// RUN: %t2 2>&1 | FileCheck --check-prefix=CHECK-TYPEINFO %s
// REQUIRES: cxxabi
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
struct A {
virtual void f();
};
void A::f() {}
int main(int argc, char *argv[]) {
char *volatile p = reinterpret_cast<char *>(new A());
if (argc > 1 && strcmp(argv[1], "unaddressable") == 0) {
void *vtable = mmap(nullptr, 4096, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
// Create an object with a vtable in an unaddressable memory region.
*(uintptr_t *)p = (uintptr_t)vtable + 64;
// CHECK-UNADDR: runtime error: control flow integrity check for type 'A' failed during cast
// CHECK-UNADDR: note: invalid vtable
// CHECK-UNADDR: <memory cannot be printed>
// CHECK-UNADDR: runtime error: control flow integrity check for type 'A' failed during cast
// CHECK-UNADDR: note: invalid vtable
// CHECK-UNADDR: <memory cannot be printed>
} else if (argc > 1 && strcmp(argv[1], "zero") == 0) {
// Create an object with a vtable outside of any known DSO, but still in an
// addressable area.
void *vtable = calloc(1, 128);
*(uintptr_t *)p = (uintptr_t)vtable + 64;
// CHECK-ZERO: runtime error: control flow integrity check for type 'A' failed during cast
// CHECK-ZERO: note: invalid vtable
// CHECK-ZERO: 00 00 00 00 00 00 00 00
// CHECK-ZERO: runtime error: control flow integrity check for type 'A' failed during cast
// CHECK-ZERO: note: invalid vtable
// CHECK-ZERO: 00 00 00 00 00 00 00 00
} else {
// Create an object with a seemingly fine vtable, but with an unaddressable
// typeinfo pointer.
void *vtable = calloc(1, 128);
memset(vtable, 0xFE, 128);
*(uintptr_t *)p = (uintptr_t)vtable + 64;
// CHECK-TYPEINFO: runtime error: control flow integrity check for type 'A' failed during cast
// CHECK-TYPEINFO: note: invalid vtable
// CHECK-TYPEINFO: fe fe fe fe fe fe fe fe
// CHECK-TYPEINFO: runtime error: control flow integrity check for type 'A' failed during cast
// CHECK-TYPEINFO: note: invalid vtable
// CHECK-TYPEINFO: fe fe fe fe fe fe fe fe
}
A *volatile pa = reinterpret_cast<A *>(p);
pa = reinterpret_cast<A *>(p);
}

View File

@@ -0,0 +1,30 @@
// This is a hack to access CFI interface that Android has in libdl.so on
// device, but not in the NDK.
#include <dlfcn.h>
#include <stdint.h>
#include <stdlib.h>
typedef void (*cfi_slowpath_ty)(uint64_t, void *);
typedef void (*cfi_slowpath_diag_ty)(uint64_t, void *, void *);
static cfi_slowpath_ty cfi_slowpath;
static cfi_slowpath_diag_ty cfi_slowpath_diag;
__attribute__((constructor(0), no_sanitize("cfi"))) static void init() {
cfi_slowpath = (cfi_slowpath_ty)dlsym(RTLD_NEXT, "__cfi_slowpath");
cfi_slowpath_diag =
(cfi_slowpath_diag_ty)dlsym(RTLD_NEXT, "__cfi_slowpath_diag");
if (!cfi_slowpath || !cfi_slowpath_diag) abort();
}
extern "C" {
__attribute__((visibility("hidden"), no_sanitize("cfi"))) void __cfi_slowpath(
uint64_t Type, void *Addr) {
cfi_slowpath(Type, Addr);
}
__attribute__((visibility("hidden"), no_sanitize("cfi"))) void
__cfi_slowpath_diag(uint64_t Type, void *Addr, void *Diag) {
cfi_slowpath_diag(Type, Addr, Diag);
}
}

View File

@@ -0,0 +1,27 @@
// RUN: %clang -o %t1 %s
// RUN: %t1 2>&1 | FileCheck --check-prefix=NCFI %s
// RUN: %clang_cfi -o %t2 %s
// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s
// RUN: %clang_cfi_diag -g -o %t3 %s
// RUN: %t3 2>&1 | FileCheck --check-prefix=CFI-DIAG %s
#include <stdio.h>
void f() {
}
int main() {
// CFI: 1
// NCFI: 1
fprintf(stderr, "1\n");
// CFI-DIAG: runtime error: control flow integrity check for type 'void (int)' failed during indirect function call
// CFI-DIAG: f defined here
((void (*)(int))f)(42); // UB here
// CFI-NOT: 2
// NCFI: 2
fprintf(stderr, "2\n");
}

Some files were not shown because too many files have changed in this diff Show More