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