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
79struct A {
80 virtual void f();
81};
82
83void *create_B();
84
85#ifdef SHARED_LIB
86
87#include "../../utils.h"
88struct B {
89 virtual void f();
90};
91void B::f() {}
92
93void *create_B() {
94 create_derivers<B>();
95 return (void *)(new B());
96}
97
98#else
99
100void A::f() {}
101
102int 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

source code of compiler-rt/test/cfi/cross-dso/icall/diag.cpp