1// RUN: %clangxx -frtti -fsanitize=null,vptr -fno-sanitize-memory-param-retval -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: target={{.*windows-msvc.*}}
41// Suppressions file not pushed to the device.
42// UNSUPPORTED: android
43// Compilation error
44// UNSUPPORTED: target={{.*openbsd.*}}
45// Compilation error
46// UNSUPPORTED: target={{.*freebsd.*}}
47// FIXME: For MinGW targets, the vptr tests do generally work, but Itanium
48// demangling isn't done for the type names. The "(echo ..." line fails to
49// be handled by the shell.
50// XFAIL: target={{.*windows-gnu.*}}
51#include <new>
52#include <typeinfo>
53#include <assert.h>
54#include <stdio.h>
55
56struct S {
57 S() : a(0) {}
58 ~S();
59 int a;
60 int f() { return 0; }
61 virtual int v() { return 0; }
62};
63
64struct T : S {
65 T() : b(0) {}
66 int b;
67 int g() { return 0; }
68 virtual int v() { return 1; }
69};
70
71struct U : S, T { virtual int v() { return 2; } };
72
73struct V : S {};
74
75namespace {
76 struct W {};
77}
78
79T *p = 0;
80
81bool dtorCheck = false;
82
83volatile void *sink1, *sink2;
84
85int access_p(T *p, char type);
86
87S::~S() {
88 if (dtorCheck)
89 access_p(p, type: '~');
90}
91
92int main(int argc, char **argv) {
93 assert(argc > 1);
94 fprintf(stderr, format: "Test case: %s\n", argv[1]);
95 T t;
96 (void)t.a;
97 (void)t.b;
98 (void)t.f();
99 (void)t.g();
100 (void)t.v();
101 (void)t.S::v();
102
103 U u;
104 (void)u.T::a;
105 (void)u.b;
106 (void)u.T::f();
107 (void)u.g();
108 (void)u.v();
109 (void)u.T::v();
110 (void)((T&)u).S::v();
111
112 char Buffer[sizeof(U)] = {};
113 char TStorage[sizeof(T)];
114 // Allocate two dummy objects so that the real object
115 // is not on the boundary of mapped memory. Otherwise ubsan
116 // will not be able to describe the vptr in detail.
117 sink1 = new T;
118 sink2 = new U;
119 switch (argv[1][1]) {
120 case '0':
121 p = reinterpret_cast<T*>(Buffer);
122 break;
123 case 'S':
124 // Make sure p points to the memory chunk of sufficient size to prevent ASan
125 // reports about out-of-bounds access.
126 p = reinterpret_cast<T*>(new(TStorage) S);
127 break;
128 case 'T':
129 p = new T;
130 break;
131 case 'U':
132 p = new U;
133 break;
134 case 'V':
135 p = reinterpret_cast<T*>(new U);
136 break;
137 case 'N':
138 p = 0;
139 break;
140 }
141
142 access_p(p, type: argv[1][0]);
143 return 0;
144}
145
146int access_p(T *p, char type) {
147 switch (type) {
148 case 'r':
149 // Binding a reference to storage of appropriate size and alignment is OK.
150 {T &r = *p;}
151 return 0;
152
153 case 'x':
154 for (int i = 0; i < 2; i++) {
155 // Check that the first iteration ("S") succeeds, while the second ("V") fails.
156 p = reinterpret_cast<T*>((i == 0) ? new S : new V);
157 // 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'
158 // CHECK-LOC-SUPPRESS-NEXT: [[PTR]]: note: object is of type 'V'
159 // CHECK-LOC-SUPPRESS-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
160 // CHECK-LOC-SUPPRESS-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
161 // CHECK-LOC-SUPPRESS-NEXT: {{^ vptr for 'V'}}
162 p->g();
163 }
164 return 0;
165
166 case 'm':
167 // 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'
168 // CHECK-MEMBER-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
169 // CHECK-MEMBER-NEXT: {{^ ?.. .. .. .. ?.. .. .. .. ?.. .. .. .. ?}}
170 // CHECK-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
171 // CHECK-MEMBER-NEXT: {{^ vptr for}} [[DYN_TYPE]]
172 // CHECK-Linux-MEMBER: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
173 return p->b;
174
175 // 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'
176 // CHECK-INVALID-MEMBER-NEXT: [[PTR]]: note: object has invalid vptr
177 // CHECK-INVALID-MEMBER-NEXT: {{^ ?.. .. .. .. ?00 00 00 00 ?00 00 00 00 ?}}
178 // CHECK-INVALID-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
179 // CHECK-INVALID-MEMBER-NEXT: {{^ invalid vptr}}
180 // CHECK-Linux-NULL-MEMBER: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE-7]]
181
182 case 'f':
183 // 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'
184 // CHECK-MEMFUN-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
185 // CHECK-MEMFUN-NEXT: {{^ ?.. .. .. .. ?.. .. .. .. ?.. .. .. .. ?}}
186 // CHECK-MEMFUN-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
187 // CHECK-MEMFUN-NEXT: {{^ vptr for}} [[DYN_TYPE]]
188 // TODO: Add check for stacktrace here.
189 return p->g();
190
191 case 'o':
192 // 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'
193 // CHECK-OFFSET-NEXT: 0x{{[0-9a-f]*}}: note: object is base class subobject at offset {{8|16}} within object of type [[DYN_TYPE:'U']]
194 // CHECK-OFFSET-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }}
195 // CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)?~~~~~~~~~~~ *$}}
196 // CHECK-OFFSET-NEXT: {{^ ( )?vptr for}} 'T' base class of [[DYN_TYPE]]
197 // CHECK-Linux-OFFSET: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
198 return reinterpret_cast<U*>(p)->v() - 2;
199
200 case 'c':
201 // 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'
202 // CHECK-DOWNCAST-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
203 // CHECK-DOWNCAST-NEXT: {{^ ?.. .. .. .. ?.. .. .. .. ?.. .. .. .. ?}}
204 // CHECK-DOWNCAST-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
205 // CHECK-DOWNCAST-NEXT: {{^ vptr for}} [[DYN_TYPE]]
206 // CHECK-Linux-DOWNCAST: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
207 (void)static_cast<T*>(reinterpret_cast<S*>(p));
208 return 0;
209
210 case 'n':
211 // CHECK-NULL-MEMFUN: vptr.cpp:[[@LINE+1]]:15: runtime error: member call on null pointer of type 'T'
212 return p->g();
213
214 case 'd':
215 dtorCheck = true;
216 delete p;
217 dtorCheck = false;
218 return 0;
219 case '~':
220 // 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'
221 // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S'
222 // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
223 // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
224 // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S'
225 // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
226 (void)dynamic_cast<V*>(p);
227 // 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'
228 // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S'
229 // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
230 // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
231 // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S'
232 // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
233 (void)dynamic_cast<W*>(p);
234 try {
235 // 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'
236 // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S'
237 // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
238 // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
239 // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S'
240 // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
241 (void)dynamic_cast<V&>(*p);
242 } catch (std::bad_cast &) {}
243 // 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'
244 // CHECK-DYNAMIC-NEXT: [[PTR]]: note: object is of type 'S'
245 // CHECK-DYNAMIC-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
246 // CHECK-DYNAMIC-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
247 // CHECK-DYNAMIC-NEXT: {{^ vptr for}} 'S'
248 // CHECK-Linux-DYNAMIC: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]]
249 (void)typeid(*p);
250 return 0;
251
252 case 'z':
253 (void)dynamic_cast<V*>(p);
254 try {
255 (void)typeid(*p);
256 } catch (std::bad_typeid &) {}
257 return 0;
258 }
259 return 0;
260}
261

source code of compiler-rt/test/ubsan/TestCases/TypeCheck/vptr.cpp