Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/compiler-rt/lib/tysan/tysan.cpp
213766 views
1
//===-- tysan.cpp ---------------------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This file is a part of TypeSanitizer.
10
//
11
// TypeSanitizer runtime.
12
//===----------------------------------------------------------------------===//
13
14
#include "sanitizer_common/sanitizer_atomic.h"
15
#include "sanitizer_common/sanitizer_common.h"
16
#include "sanitizer_common/sanitizer_flag_parser.h"
17
#include "sanitizer_common/sanitizer_flags.h"
18
#include "sanitizer_common/sanitizer_libc.h"
19
#include "sanitizer_common/sanitizer_report_decorator.h"
20
#include "sanitizer_common/sanitizer_stacktrace.h"
21
#include "sanitizer_common/sanitizer_symbolizer.h"
22
23
#include "tysan/tysan.h"
24
25
#include <string.h>
26
27
using namespace __sanitizer;
28
using namespace __tysan;
29
30
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
31
tysan_set_type_unknown(const void *addr, uptr size) {
32
if (tysan_inited)
33
internal_memset(shadow_for(addr), 0, size * sizeof(uptr));
34
}
35
36
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
37
tysan_copy_types(const void *daddr, const void *saddr, uptr size) {
38
if (tysan_inited)
39
internal_memmove(shadow_for(daddr), shadow_for(saddr), size * sizeof(uptr));
40
}
41
42
static const char *getDisplayName(const char *Name) {
43
if (Name[0] == '\0')
44
return "<anonymous type>";
45
46
// Clang generates tags for C++ types that demangle as typeinfo. Remove the
47
// prefix from the generated string.
48
const char *TIPrefix = "typeinfo name for ";
49
size_t TIPrefixLen = strlen(TIPrefix);
50
51
const char *DName = Symbolizer::GetOrInit()->Demangle(Name);
52
if (!internal_strncmp(DName, TIPrefix, TIPrefixLen))
53
DName += TIPrefixLen;
54
55
return DName;
56
}
57
58
static void printTDName(tysan_type_descriptor *td) {
59
if (((sptr)td) <= 0) {
60
Printf("<unknown type>");
61
return;
62
}
63
64
switch (td->Tag) {
65
default:
66
CHECK(false && "invalid enum value");
67
break;
68
case TYSAN_MEMBER_TD:
69
printTDName(td->Member.Access);
70
if (td->Member.Access != td->Member.Base) {
71
Printf(" (in ");
72
printTDName(td->Member.Base);
73
Printf(" at offset %zu)", td->Member.Offset);
74
}
75
break;
76
case TYSAN_STRUCT_TD:
77
Printf("%s", getDisplayName(
78
(char *)(td->Struct.Members + td->Struct.MemberCount)));
79
break;
80
}
81
}
82
83
static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) {
84
tysan_type_descriptor *RootTD = TD;
85
86
do {
87
RootTD = TD;
88
89
if (TD->Tag == TYSAN_STRUCT_TD) {
90
if (TD->Struct.MemberCount > 0)
91
TD = TD->Struct.Members[0].Type;
92
else
93
TD = nullptr;
94
} else if (TD->Tag == TYSAN_MEMBER_TD) {
95
TD = TD->Member.Access;
96
} else {
97
CHECK(false && "invalid enum value");
98
break;
99
}
100
} while (TD);
101
102
return RootTD;
103
}
104
105
// Walk up TDA to see if it reaches TDB.
106
static bool walkAliasTree(tysan_type_descriptor *TDA,
107
tysan_type_descriptor *TDB, uptr OffsetA,
108
uptr OffsetB) {
109
do {
110
if (TDA == TDB)
111
return OffsetA == OffsetB;
112
113
if (TDA->Tag == TYSAN_STRUCT_TD) {
114
// Reached root type descriptor.
115
if (!TDA->Struct.MemberCount)
116
break;
117
118
uptr Idx = 0;
119
for (; Idx < TDA->Struct.MemberCount - 1; ++Idx) {
120
if (TDA->Struct.Members[Idx].Offset >= OffsetA)
121
break;
122
}
123
124
// This offset can't be negative. Therefore we must be accessing something
125
// before the current type (not legal) or partially inside the last type.
126
// In the latter case, we adjust Idx.
127
if (TDA->Struct.Members[Idx].Offset > OffsetA) {
128
// Trying to access something before the current type.
129
if (!Idx)
130
return false;
131
132
Idx -= 1;
133
}
134
135
OffsetA -= TDA->Struct.Members[Idx].Offset;
136
TDA = TDA->Struct.Members[Idx].Type;
137
} else {
138
CHECK(false && "invalid enum value");
139
break;
140
}
141
} while (TDA);
142
143
return false;
144
}
145
146
// Walk up the tree starting with TDA to see if we reach TDB.
147
static bool isAliasingLegalUp(tysan_type_descriptor *TDA,
148
tysan_type_descriptor *TDB) {
149
uptr OffsetA = 0, OffsetB = 0;
150
if (TDB->Tag == TYSAN_MEMBER_TD) {
151
OffsetB = TDB->Member.Offset;
152
TDB = TDB->Member.Base;
153
}
154
155
if (TDA->Tag == TYSAN_MEMBER_TD) {
156
OffsetA = TDA->Member.Offset;
157
TDA = TDA->Member.Base;
158
}
159
160
return walkAliasTree(TDA, TDB, OffsetA, OffsetB);
161
}
162
163
static bool isAliasingLegalWithOffset(tysan_type_descriptor *TDA,
164
tysan_type_descriptor *TDB,
165
uptr OffsetB) {
166
// This is handled by calls to isAliasingLegalUp.
167
if (OffsetB == 0)
168
return false;
169
170
// You can't have an offset into a member.
171
if (TDB->Tag == TYSAN_MEMBER_TD)
172
return false;
173
174
uptr OffsetA = 0;
175
if (TDA->Tag == TYSAN_MEMBER_TD) {
176
OffsetA = TDA->Member.Offset;
177
TDA = TDA->Member.Base;
178
}
179
180
// Since the access was partially inside TDB (the shadow), it can be assumed
181
// that we are accessing a member in an object. This means that rather than
182
// walk up the scalar access TDA to reach an object, we should walk up the
183
// object TBD to reach the scalar we are accessing it with. The offsets will
184
// still be checked at the end to make sure this alias is legal.
185
return walkAliasTree(TDB, TDA, OffsetB, OffsetA);
186
}
187
188
static bool isAliasingLegal(tysan_type_descriptor *TDA,
189
tysan_type_descriptor *TDB, uptr OffsetB = 0) {
190
if (TDA == TDB || !TDB || !TDA)
191
return true;
192
193
// Aliasing is legal is the two types have different root nodes.
194
if (getRootTD(TDA) != getRootTD(TDB))
195
return true;
196
197
// TDB may have been adjusted by offset TDAOffset in the caller to point to
198
// the outer type. Check for aliasing with and without adjusting for this
199
// offset.
200
return isAliasingLegalUp(TDA, TDB) || isAliasingLegalUp(TDB, TDA) ||
201
isAliasingLegalWithOffset(TDA, TDB, OffsetB);
202
}
203
204
namespace __tysan {
205
class Decorator : public __sanitizer::SanitizerCommonDecorator {
206
public:
207
Decorator() : SanitizerCommonDecorator() {}
208
const char *Warning() { return Red(); }
209
const char *Name() { return Green(); }
210
const char *End() { return Default(); }
211
};
212
} // namespace __tysan
213
214
ALWAYS_INLINE
215
static void reportError(void *Addr, int Size, tysan_type_descriptor *TD,
216
tysan_type_descriptor *OldTD, const char *AccessStr,
217
const char *DescStr, int Offset, uptr pc, uptr bp,
218
uptr sp) {
219
Decorator d;
220
Printf("%s", d.Warning());
221
Report("ERROR: TypeSanitizer: type-aliasing-violation on address %p"
222
" (pc %p bp %p sp %p tid %llu)\n",
223
Addr, (void *)pc, (void *)bp, (void *)sp, GetTid());
224
Printf("%s", d.End());
225
Printf("%s of size %d at %p with type ", AccessStr, Size, Addr);
226
227
Printf("%s", d.Name());
228
printTDName(TD);
229
Printf("%s", d.End());
230
231
Printf(" %s of type ", DescStr);
232
233
Printf("%s", d.Name());
234
printTDName(OldTD);
235
Printf("%s", d.End());
236
237
if (Offset != 0)
238
Printf(" that starts at offset %d\n", Offset);
239
else
240
Printf("\n");
241
242
if (pc) {
243
uptr top = 0;
244
uptr bottom = 0;
245
if (flags().print_stacktrace)
246
GetThreadStackTopAndBottom(false, &top, &bottom);
247
248
bool request_fast = StackTrace::WillUseFastUnwind(true);
249
BufferedStackTrace ST;
250
ST.Unwind(kStackTraceMax, pc, bp, 0, top, bottom, request_fast);
251
ST.Print();
252
} else {
253
Printf("\n");
254
}
255
}
256
257
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
258
__tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) {
259
GET_CALLER_PC_BP_SP;
260
261
bool IsRead = flags & 1;
262
bool IsWrite = flags & 2;
263
const char *AccessStr;
264
if (IsRead && !IsWrite)
265
AccessStr = "READ";
266
else if (!IsRead && IsWrite)
267
AccessStr = "WRITE";
268
else
269
AccessStr = "ATOMIC UPDATE";
270
271
tysan_type_descriptor **OldTDPtr = shadow_for(addr);
272
tysan_type_descriptor *OldTD = *OldTDPtr;
273
if (((sptr)OldTD) < 0) {
274
int i = -((sptr)OldTD);
275
OldTDPtr -= i;
276
OldTD = *OldTDPtr;
277
278
if (!isAliasingLegal(td, OldTD, i))
279
reportError(addr, size, td, OldTD, AccessStr,
280
"accesses part of an existing object", -i, pc, bp, sp);
281
282
return;
283
}
284
285
if (!isAliasingLegal(td, OldTD)) {
286
reportError(addr, size, td, OldTD, AccessStr, "accesses an existing object",
287
0, pc, bp, sp);
288
return;
289
}
290
291
// These types are allowed to alias (or the stored type is unknown), report
292
// an error if we find an interior type.
293
294
for (int i = 0; i < size; ++i) {
295
OldTDPtr = shadow_for((void *)(((uptr)addr) + i));
296
OldTD = *OldTDPtr;
297
if (((sptr)OldTD) >= 0 && !isAliasingLegal(td, OldTD))
298
reportError(addr, size, td, OldTD, AccessStr,
299
"partially accesses an object", i, pc, bp, sp);
300
}
301
}
302
303
Flags __tysan::flags_data;
304
305
SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address;
306
SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_app_memory_mask;
307
308
#ifdef TYSAN_RUNTIME_VMA
309
// Runtime detected VMA size.
310
int __tysan::vmaSize;
311
#endif
312
313
void Flags::SetDefaults() {
314
#define TYSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
315
#include "tysan_flags.inc"
316
#undef TYSAN_FLAG
317
}
318
319
static void RegisterTySanFlags(FlagParser *parser, Flags *f) {
320
#define TYSAN_FLAG(Type, Name, DefaultValue, Description) \
321
RegisterFlag(parser, #Name, Description, &f->Name);
322
#include "tysan_flags.inc"
323
#undef TYSAN_FLAG
324
}
325
326
static void InitializeFlags() {
327
SetCommonFlagsDefaults();
328
{
329
CommonFlags cf;
330
cf.CopyFrom(*common_flags());
331
cf.external_symbolizer_path = GetEnv("TYSAN_SYMBOLIZER_PATH");
332
OverrideCommonFlags(cf);
333
}
334
335
flags().SetDefaults();
336
337
FlagParser parser;
338
RegisterCommonFlags(&parser);
339
RegisterTySanFlags(&parser, &flags());
340
parser.ParseString(GetEnv("TYSAN_OPTIONS"));
341
InitializeCommonFlags();
342
if (Verbosity())
343
ReportUnrecognizedFlags();
344
if (common_flags()->help)
345
parser.PrintFlagDescriptions();
346
}
347
348
static void TySanInitializePlatformEarly() {
349
AvoidCVE_2016_2143();
350
#ifdef TYSAN_RUNTIME_VMA
351
vmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
352
#if defined(__aarch64__) && !SANITIZER_APPLE
353
if (vmaSize != 39 && vmaSize != 42 && vmaSize != 48) {
354
Printf("FATAL: TypeSanitizer: unsupported VMA range\n");
355
Printf("FATAL: Found %d - Supported 39, 42 and 48\n", vmaSize);
356
Die();
357
}
358
#endif
359
#endif
360
361
__sanitizer::InitializePlatformEarly();
362
363
__tysan_shadow_memory_address = ShadowAddr();
364
__tysan_app_memory_mask = AppMask();
365
}
366
367
namespace __tysan {
368
bool tysan_inited = false;
369
bool tysan_init_is_running;
370
} // namespace __tysan
371
372
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __tysan_init() {
373
CHECK(!tysan_init_is_running);
374
if (tysan_inited)
375
return;
376
tysan_init_is_running = true;
377
378
InitializeFlags();
379
TySanInitializePlatformEarly();
380
381
InitializeInterceptors();
382
383
if (!MmapFixedNoReserve(ShadowAddr(), AppAddr() - ShadowAddr()))
384
Die();
385
386
tysan_init_is_running = false;
387
tysan_inited = true;
388
}
389
390
#if SANITIZER_CAN_USE_PREINIT_ARRAY
391
__attribute__((section(".preinit_array"),
392
used)) static void (*tysan_init_ptr)() = __tysan_init;
393
#endif
394
395