1 // RUN: %clangxx -frtti -fsanitize=null,vptr -g %s -O3 -o %t -mllvm -enable-tail-merge=false
2 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t rT
3 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t mT
4 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t fT
5 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t cT
6 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t rU
7 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t mU
8 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t fU
9 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t cU
10 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t rS
11 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t rV
12 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t oV
13 // RUN: %env_ubsan_opts=halt_on_error=1 %run %t zN
14 // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --allow-unused-prefixes --check-prefix=CHECK-%os-MEMBER --strict-whitespace
15 // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace
16 // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t cS 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --allow-unused-prefixes --check-prefix=CHECK-%os-DOWNCAST --strict-whitespace
17 // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --allow-unused-prefixes --check-prefix=CHECK-%os-MEMBER --strict-whitespace
18 // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace
19 // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t cV 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --allow-unused-prefixes --check-prefix=CHECK-%os-DOWNCAST --strict-whitespace
20 // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --allow-unused-prefixes --check-prefix=CHECK-%os-OFFSET --strict-whitespace
21 // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID-MEMBER --allow-unused-prefixes --check-prefix=CHECK-%os-NULL-MEMBER --strict-whitespace
22 // RUN: %env_ubsan_opts=halt_on_error=1:print_stacktrace=1 not %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-INVALID-MEMBER --allow-unused-prefixes --check-prefix=CHECK-%os-NULL-MEMBER --strict-whitespace
23 // RUN: %env_ubsan_opts=halt_on_error=1 not %run %t nN 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMFUN --strict-whitespace
24 // RUN: %env_ubsan_opts=print_stacktrace=1 %run %t dT 2>&1 | FileCheck %s --check-prefix=CHECK-DYNAMIC --allow-unused-prefixes --check-prefix=CHECK-%os-DYNAMIC --strict-whitespace
25
26 // RUN: (echo "vptr_check:S"; echo "vptr_check:T"; echo "vptr_check:U") > %t.supp
27 // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t mS
28 // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t fS
29 // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t cS
30 // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t mV
31 // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t fV
32 // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t cV
33 // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t oU
34 // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.supp"' %run %t dT
35
36 // RUN: echo "vptr_check:S" > %t.loc-supp
37 // RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.loc-supp"' not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS
38
39 // REQUIRES: stable-runtime, cxxabi
40 // UNSUPPORTED: windows-msvc
41 // Suppressions file not pushed to the device.
42 // UNSUPPORTED: android
43 // Compilation error
44 // UNSUPPORTED: openbsd
45 // Compilation error
46 // UNSUPPORTED: freebsd
47 #include <new>
48 #include <typeinfo>
49 #include <assert.h>
50 #include <stdio.h>
51
52 struct S {
SS53 S() : a(0) {}
54 ~S();
55 int a;
fS56 int f() { return 0; }
vS57 virtual int v() { return 0; }
58 };
59
60 struct T : S {
TT61 T() : b(0) {}
62 int b;
gT63 int g() { return 0; }
vT64 virtual int v() { return 1; }
65 };
66
vU67 struct U : S, T { virtual int v() { return 2; } };
68
69 struct V : S {};
70
71 namespace {
72 struct W {};
73 }
74
75 T *p = 0;
76
77 bool dtorCheck = false;
78
79 volatile void *sink1, *sink2;
80
81 int access_p(T *p, char type);
82
~S()83 S::~S() {
84 if (dtorCheck)
85 access_p(p, '~');
86 }
87
main(int argc,char ** argv)88 int main(int argc, char **argv) {
89 assert(argc > 1);
90 fprintf(stderr, "Test case: %s\n", argv[1]);
91 T t;
92 (void)t.a;
93 (void)t.b;
94 (void)t.f();
95 (void)t.g();
96 (void)t.v();
97 (void)t.S::v();
98
99 U u;
100 (void)u.T::a;
101 (void)u.b;
102 (void)u.T::f();
103 (void)u.g();
104 (void)u.v();
105 (void)u.T::v();
106 (void)((T&)u).S::v();
107
108 char Buffer[sizeof(U)] = {};
109 char TStorage[sizeof(T)];
110 // Allocate two dummy objects so that the real object
111 // is not on the boundary of mapped memory. Otherwise ubsan
112 // will not be able to describe the vptr in detail.
113 sink1 = new T;
114 sink2 = new U;
115 switch (argv[1][1]) {
116 case '0':
117 p = reinterpret_cast<T*>(Buffer);
118 break;
119 case 'S':
120 // Make sure p points to the memory chunk of sufficient size to prevent ASan
121 // reports about out-of-bounds access.
122 p = reinterpret_cast<T*>(new(TStorage) S);
123 break;
124 case 'T':
125 p = new T;
126 break;
127 case 'U':
128 p = new U;
129 break;
130 case 'V':
131 p = reinterpret_cast<T*>(new U);
132 break;
133 case 'N':
134 p = 0;
135 break;
136 }
137
138 access_p(p, argv[1][0]);
139 return 0;
140 }
141
access_p(T * p,char type)142 int access_p(T *p, char type) {
143 switch (type) {
144 case 'r':
145 // Binding a reference to storage of appropriate size and alignment is OK.
146 {T &r = *p;}
147 return 0;
148
149 case 'x':
150 for (int i = 0; i < 2; i++) {
151 // Check that the first iteration ("S") succeeds, while the second ("V") fails.
152 p = reinterpret_cast<T*>((i == 0) ? new S : new V);
153 // CHECK-LOC-SUPPRESS: vptr.cpp:[[@LINE+5]]:10: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
154 // CHECK-LOC-SUPPRESS-NEXT: [[PTR]]: note: object is of type 'V'
155 // CHECK-LOC-SUPPRESS-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
156 // CHECK-LOC-SUPPRESS-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
157 // CHECK-LOC-SUPPRESS-NEXT: {{^ vptr for 'V'}}
158 p->g();
159 }
160 return 0;
161
162 case 'm':
163 // CHECK-MEMBER: vptr.cpp:[[@LINE+6]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
164 // CHECK-MEMBER-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
165 // CHECK-MEMBER-NEXT: {{^ ?.. .. .. .. ?.. .. .. .. ?.. .. .. .. ?}}
166 // CHECK-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
167 // CHECK-MEMBER-NEXT: {{^ vptr for}} [[DYN_TYPE]]
168 // CHECK-Linux-MEMBER: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
169 return p->b;
170
171 // CHECK-INVALID-MEMBER: vptr.cpp:[[@LINE-2]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
172 // CHECK-INVALID-MEMBER-NEXT: [[PTR]]: note: object has invalid vptr
173 // CHECK-INVALID-MEMBER-NEXT: {{^ ?.. .. .. .. ?00 00 00 00 ?00 00 00 00 ?}}
174 // CHECK-INVALID-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
175 // CHECK-INVALID-MEMBER-NEXT: {{^ invalid vptr}}
176 // CHECK-Linux-NULL-MEMBER: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE-7]]
177
178 case 'f':
179 // CHECK-MEMFUN: vptr.cpp:[[@LINE+6]]:15: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
180 // CHECK-MEMFUN-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
181 // CHECK-MEMFUN-NEXT: {{^ ?.. .. .. .. ?.. .. .. .. ?.. .. .. .. ?}}
182 // CHECK-MEMFUN-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
183 // CHECK-MEMFUN-NEXT: {{^ vptr for}} [[DYN_TYPE]]
184 // TODO: Add check for stacktrace here.
185 return p->g();
186
187 case 'o':
188 // CHECK-OFFSET: vptr.cpp:[[@LINE+6]]:37: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'U'
189 // CHECK-OFFSET-NEXT: 0x{{[0-9a-f]*}}: note: object is base class subobject at offset {{8|16}} within object of type [[DYN_TYPE:'U']]
190 // CHECK-OFFSET-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }}
191 // CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)?~~~~~~~~~~~ *$}}
192 // CHECK-OFFSET-NEXT: {{^ ( )?vptr for}} 'T' base class of [[DYN_TYPE]]
193 // CHECK-Linux-OFFSET: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
194 return reinterpret_cast<U*>(p)->v() - 2;
195
196 case 'c':
197 // CHECK-DOWNCAST: vptr.cpp:[[@LINE+6]]:11: runtime error: downcast of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
198 // CHECK-DOWNCAST-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
199 // CHECK-DOWNCAST-NEXT: {{^ ?.. .. .. .. ?.. .. .. .. ?.. .. .. .. ?}}
200 // CHECK-DOWNCAST-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
201 // CHECK-DOWNCAST-NEXT: {{^ vptr for}} [[DYN_TYPE]]
202 // CHECK-Linux-DOWNCAST: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
203 (void)static_cast<T*>(reinterpret_cast<S*>(p));
204 return 0;
205
206 case 'n':
207 // CHECK-NULL-MEMFUN: vptr.cpp:[[@LINE+1]]:15: runtime error: member call on null pointer of type 'T'
208 return p->g();
209
210 case 'd':
211 dtorCheck = true;
212 delete p;
213 dtorCheck = false;
214 return 0;
215 case '~':
216 // CHECK-DYNAMIC: vptr.cpp:[[@LINE+6]]:11: runtime error: dynamic operation on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
217 // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S'
218 // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
219 // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
220 // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S'
221 // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
222 (void)dynamic_cast<V*>(p);
223 // CHECK-DYNAMIC: vptr.cpp:[[@LINE+6]]:11: runtime error: dynamic operation on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
224 // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S'
225 // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
226 // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
227 // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S'
228 // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
229 (void)dynamic_cast<W*>(p);
230 try {
231 // CHECK-DYNAMIC: vptr.cpp:[[@LINE+6]]:13: runtime error: dynamic operation on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
232 // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S'
233 // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
234 // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
235 // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S'
236 // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
237 (void)dynamic_cast<V&>(*p);
238 } catch (std::bad_cast &) {}
239 // CHECK-DYNAMIC: vptr.cpp:[[@LINE+6]]:18: runtime error: dynamic operation on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
240 // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S'
241 // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
242 // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
243 // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S'
244 // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
245 (void)typeid(*p);
246 return 0;
247
248 case 'z':
249 (void)dynamic_cast<V*>(p);
250 try {
251 (void)typeid(*p);
252 } catch (std::bad_typeid &) {}
253 return 0;
254 }
255 return 0;
256 }
257