1 // Cross-DSO diagnostics. 2 // The rules are: 3 // * If the library needs diagnostics, the main executable must request at 4 // least some diagnostics as well (to link the diagnostic runtime). 5 // * -fsanitize-trap on the caller side overrides everything. 6 // * otherwise, the callee decides between trap/recover/norecover. 7 8 // Full-recover. 9 // RUN: %clangxx_cfi_dso_diag -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so 10 // RUN: %clangxx_cfi_dso_diag -g %s -o %t %t-so.so 11 12 // RUN: %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-DIAG \ 13 // RUN: --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER 14 15 // RUN: %t i_v 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-NODIAG \ 16 // RUN: --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER 17 18 // RUN: %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \ 19 // RUN: --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER 20 21 // RUN: %t ic_ 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-DIAG \ 22 // RUN: --check-prefix=VCALL-NODIAG --check-prefix=ALL-RECOVER 23 24 // Trap on icall, no-recover on cast. 25 // RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \ 26 // RUN: -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so 27 // RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \ 28 // RUN: -g %s -o %t %t-so.so 29 30 // RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ 31 // RUN: --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL 32 33 // RUN: not %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \ 34 // RUN: --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL 35 36 // RUN: %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ 37 // RUN: --check-prefix=VCALL-DIAG 38 39 // Callee: trap on icall, no-recover on cast. 40 // Caller: recover on everything. 41 // The same as in the previous case, behaviour is decided by the callee. 42 // RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \ 43 // RUN: -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so 44 // RUN: %clangxx_cfi_dso_diag \ 45 // RUN: -g %s -o %t %t-so.so 46 47 // RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ 48 // RUN: --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL 49 50 // RUN: not %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \ 51 // RUN: --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL 52 53 // RUN: %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ 54 // RUN: --check-prefix=VCALL-DIAG 55 56 // Caller in trapping mode, callee with full diagnostic+recover. 57 // Caller wins. 58 // cfi-nvcall is non-trapping in the main executable to link the diagnostic runtime library. 59 // RUN: %clangxx_cfi_dso_diag \ 60 // RUN: -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so 61 // RUN: %clangxx_cfi_dso -fno-sanitize-trap=cfi-nvcall \ 62 // RUN: -g %s -o %t %t-so.so 63 64 // RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ 65 // RUN: --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL 66 67 // RUN: %expect_crash %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ 68 // RUN: --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL 69 70 // RUN: %expect_crash %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ 71 // RUN: --check-prefix=VCALL-NODIAG --check-prefix=VCALL-FATAL 72 73 // REQUIRES: cxxabi 74 75 #include <assert.h> 76 #include <stdio.h> 77 #include <string.h> 78 79 struct A { 80 virtual void f(); 81 }; 82 83 void *create_B(); 84 85 #ifdef SHARED_LIB 86 87 #include "../../utils.h" 88 struct B { 89 virtual void f(); 90 }; 91 void B::f() {} 92 93 void *create_B() { 94 create_derivers<B>(); 95 return (void *)(new B()); 96 } 97 98 #else 99 100 void A::f() {} 101 102 int main(int argc, char *argv[]) { 103 assert(argc == 2); 104 assert(strlen(argv[1]) == 3); 105 106 // ICALL-FATAL: =0= 107 // CAST-FATAL: =0= 108 // VCALL-FATAL: =0= 109 // ALL-RECOVER: =0= 110 fprintf(stderr, "=0=\n"); 111 112 void *p; 113 if (argv[1][0] == 'i') { 114 // ICALL-DIAG: runtime error: control flow integrity check for type 'void *(int)' failed during indirect function call 115 // ICALL-DIAG-NEXT: note: create_B() defined here 116 // ICALL-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during indirect function call 117 p = ((void *(*)(int))create_B)(42); 118 } else { 119 p = create_B(); 120 } 121 122 // ICALL-FATAL-NOT: =1= 123 // CAST-FATAL: =1= 124 // VCALL-FATAL: =1= 125 // ALL-RECOVER: =1= 126 fprintf(stderr, "=1=\n"); 127 128 A *a; 129 if (argv[1][1] == 'c') { 130 // CAST-DIAG: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type 131 // CAST-DIAG-NEXT: note: vtable is of type '{{(struct )?}}B' 132 // CAST-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during cast to unrelated type 133 a = (A*)p; 134 } else { 135 // Invisible to CFI. 136 memcpy(&a, &p, sizeof(a)); 137 } 138 139 // ICALL-FATAL-NOT: =2= 140 // CAST-FATAL-NOT: =2= 141 // VCALL-FATAL: =2= 142 // ALL-RECOVER: =2= 143 fprintf(stderr, "=2=\n"); 144 145 // VCALL-DIAG: runtime error: control flow integrity check for type 'A' failed during virtual call 146 // VCALL-DIAG-NEXT: note: vtable is of type '{{(struct )?}}B' 147 // VCALL-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during virtual call 148 if (argv[1][2] == 'v') { 149 a->f(); // UB here 150 } 151 152 // ICALL-FATAL-NOT: =3= 153 // CAST-FATAL-NOT: =3= 154 // VCALL-FATAL-NOT: =3= 155 // ALL-RECOVER: =3= 156 fprintf(stderr, "=3=\n"); 157 158 } 159 #endif 160