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 | |