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 };
f()91 void B::f() {}
92
create_B()93 void *create_B() {
94 create_derivers<B>();
95 return (void *)(new B());
96 }
97
98 #else
99
f()100 void A::f() {}
101
main(int argc,char * argv[])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