| 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 %dynamiclib %ld_flags_rpath_so |
| 10 | // RUN: %clangxx_cfi_dso_diag -g %s -o %t %ld_flags_rpath_exe |
| 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 %dynamiclib %ld_flags_rpath_so |
| 27 | // RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \ |
| 28 | // RUN: -g %s -o %t %ld_flags_rpath_exe |
| 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 %dynamiclib %ld_flags_rpath_so |
| 44 | // RUN: %clangxx_cfi_dso_diag \ |
| 45 | // RUN: -g %s -o %t %ld_flags_rpath_exe |
| 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 %dynamiclib %ld_flags_rpath_so |
| 61 | // RUN: %clangxx_cfi_dso -fno-sanitize-trap=cfi-nvcall \ |
| 62 | // RUN: -g %s -o %t %ld_flags_rpath_exe |
| 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, format: "=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, format: "=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(dest: &a, src: &p, n: 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, format: "=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, format: "=3=\n" ); |
| 157 | |
| 158 | } |
| 159 | #endif |
| 160 | |