15a3bb1a4SNico Weber //===-- tsan_external.cpp -------------------------------------------------===//
25a3bb1a4SNico Weber //
35a3bb1a4SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45a3bb1a4SNico Weber // See https://llvm.org/LICENSE.txt for license information.
55a3bb1a4SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65a3bb1a4SNico Weber //
75a3bb1a4SNico Weber //===----------------------------------------------------------------------===//
85a3bb1a4SNico Weber //
95a3bb1a4SNico Weber // This file is a part of ThreadSanitizer (TSan), a race detector.
105a3bb1a4SNico Weber //
115a3bb1a4SNico Weber //===----------------------------------------------------------------------===//
125a3bb1a4SNico Weber #include "tsan_rtl.h"
13e713b0ecSKuba Mracek #include "sanitizer_common/sanitizer_ptrauth.h"
145a3bb1a4SNico Weber
15cb7b0a5fSDmitry Vyukov #if !SANITIZER_GO
16cb7b0a5fSDmitry Vyukov # include "tsan_interceptors.h"
17cb7b0a5fSDmitry Vyukov #endif
18cb7b0a5fSDmitry Vyukov
195a3bb1a4SNico Weber namespace __tsan {
205a3bb1a4SNico Weber
215a3bb1a4SNico Weber #define CALLERPC ((uptr)__builtin_return_address(0))
225a3bb1a4SNico Weber
235a3bb1a4SNico Weber struct TagData {
245a3bb1a4SNico Weber const char *object_type;
255a3bb1a4SNico Weber const char *header;
265a3bb1a4SNico Weber };
275a3bb1a4SNico Weber
285a3bb1a4SNico Weber static TagData registered_tags[kExternalTagMax] = {
295a3bb1a4SNico Weber {},
305a3bb1a4SNico Weber {"Swift variable", "Swift access race"},
315a3bb1a4SNico Weber };
32c0fa6322SVitaly Buka static atomic_uint32_t used_tags{kExternalTagFirstUserAvailable};
GetTagData(uptr tag)335a3bb1a4SNico Weber static TagData *GetTagData(uptr tag) {
345a3bb1a4SNico Weber // Invalid/corrupted tag? Better return NULL and let the caller deal with it.
355a3bb1a4SNico Weber if (tag >= atomic_load(&used_tags, memory_order_relaxed)) return nullptr;
365a3bb1a4SNico Weber return ®istered_tags[tag];
375a3bb1a4SNico Weber }
385a3bb1a4SNico Weber
GetObjectTypeFromTag(uptr tag)395a3bb1a4SNico Weber const char *GetObjectTypeFromTag(uptr tag) {
405a3bb1a4SNico Weber TagData *tag_data = GetTagData(tag);
415a3bb1a4SNico Weber return tag_data ? tag_data->object_type : nullptr;
425a3bb1a4SNico Weber }
435a3bb1a4SNico Weber
GetReportHeaderFromTag(uptr tag)445a3bb1a4SNico Weber const char *GetReportHeaderFromTag(uptr tag) {
455a3bb1a4SNico Weber TagData *tag_data = GetTagData(tag);
465a3bb1a4SNico Weber return tag_data ? tag_data->header : nullptr;
475a3bb1a4SNico Weber }
485a3bb1a4SNico Weber
TagFromShadowStackFrame(uptr pc)495a3bb1a4SNico Weber uptr TagFromShadowStackFrame(uptr pc) {
505a3bb1a4SNico Weber uptr tag_count = atomic_load(&used_tags, memory_order_relaxed);
515a3bb1a4SNico Weber void *pc_ptr = (void *)pc;
525a3bb1a4SNico Weber if (pc_ptr < GetTagData(0) || pc_ptr > GetTagData(tag_count - 1))
535a3bb1a4SNico Weber return 0;
545a3bb1a4SNico Weber return (TagData *)pc_ptr - GetTagData(0);
555a3bb1a4SNico Weber }
565a3bb1a4SNico Weber
575a3bb1a4SNico Weber #if !SANITIZER_GO
585a3bb1a4SNico Weber
59*beafd235SJulian Lettner // We need to track tags for individual memory accesses, but there is no space
60*beafd235SJulian Lettner // in the shadow cells for them. Instead we push/pop them onto the thread
61*beafd235SJulian Lettner // traces and ignore the extra tag frames when printing reports.
PushTag(ThreadState * thr,uptr tag)62*beafd235SJulian Lettner static void PushTag(ThreadState *thr, uptr tag) {
63*beafd235SJulian Lettner FuncEntry(thr, (uptr)®istered_tags[tag]);
64*beafd235SJulian Lettner }
PopTag(ThreadState * thr)65*beafd235SJulian Lettner static void PopTag(ThreadState *thr) { FuncExit(thr); }
66*beafd235SJulian Lettner
ExternalAccess(void * addr,uptr caller_pc,uptr tsan_caller_pc,void * tag,AccessType typ)67*beafd235SJulian Lettner static void ExternalAccess(void *addr, uptr caller_pc, uptr tsan_caller_pc,
68*beafd235SJulian Lettner void *tag, AccessType typ) {
695a3bb1a4SNico Weber CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
70*beafd235SJulian Lettner bool in_ignored_lib;
71*beafd235SJulian Lettner if (caller_pc && libignore()->IsIgnored(caller_pc, &in_ignored_lib))
72*beafd235SJulian Lettner return;
73*beafd235SJulian Lettner
745a3bb1a4SNico Weber ThreadState *thr = cur_thread();
75e713b0ecSKuba Mracek if (caller_pc) FuncEntry(thr, caller_pc);
76*beafd235SJulian Lettner PushTag(thr, (uptr)tag);
77711ff37bSJulian Lettner MemoryAccess(thr, tsan_caller_pc, (uptr)addr, 1, typ);
78*beafd235SJulian Lettner PopTag(thr);
795a3bb1a4SNico Weber if (caller_pc) FuncExit(thr);
805a3bb1a4SNico Weber }
815a3bb1a4SNico Weber
825a3bb1a4SNico Weber extern "C" {
835a3bb1a4SNico Weber SANITIZER_INTERFACE_ATTRIBUTE
__tsan_external_register_tag(const char * object_type)845a3bb1a4SNico Weber void *__tsan_external_register_tag(const char *object_type) {
855a3bb1a4SNico Weber uptr new_tag = atomic_fetch_add(&used_tags, 1, memory_order_relaxed);
865a3bb1a4SNico Weber CHECK_LT(new_tag, kExternalTagMax);
875a3bb1a4SNico Weber GetTagData(new_tag)->object_type = internal_strdup(object_type);
885a3bb1a4SNico Weber char header[127] = {0};
895a3bb1a4SNico Weber internal_snprintf(header, sizeof(header), "race on %s", object_type);
905a3bb1a4SNico Weber GetTagData(new_tag)->header = internal_strdup(header);
915a3bb1a4SNico Weber return (void *)new_tag;
925a3bb1a4SNico Weber }
935a3bb1a4SNico Weber
945a3bb1a4SNico Weber SANITIZER_INTERFACE_ATTRIBUTE
__tsan_external_register_header(void * tag,const char * header)955a3bb1a4SNico Weber void __tsan_external_register_header(void *tag, const char *header) {
965a3bb1a4SNico Weber CHECK_GE((uptr)tag, kExternalTagFirstUserAvailable);
975a3bb1a4SNico Weber CHECK_LT((uptr)tag, kExternalTagMax);
985a3bb1a4SNico Weber atomic_uintptr_t *header_ptr =
995a3bb1a4SNico Weber (atomic_uintptr_t *)&GetTagData((uptr)tag)->header;
1005a3bb1a4SNico Weber header = internal_strdup(header);
1015a3bb1a4SNico Weber char *old_header =
1025a3bb1a4SNico Weber (char *)atomic_exchange(header_ptr, (uptr)header, memory_order_seq_cst);
103817f942aSDmitry Vyukov Free(old_header);
1045a3bb1a4SNico Weber }
1055a3bb1a4SNico Weber
1065a3bb1a4SNico Weber SANITIZER_INTERFACE_ATTRIBUTE
__tsan_external_assign_tag(void * addr,void * tag)1075a3bb1a4SNico Weber void __tsan_external_assign_tag(void *addr, void *tag) {
1085a3bb1a4SNico Weber CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
1095a3bb1a4SNico Weber Allocator *a = allocator();
1105a3bb1a4SNico Weber MBlock *b = nullptr;
1115a3bb1a4SNico Weber if (a->PointerIsMine((void *)addr)) {
1125a3bb1a4SNico Weber void *block_begin = a->GetBlockBegin((void *)addr);
1135a3bb1a4SNico Weber if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
1145a3bb1a4SNico Weber }
1155a3bb1a4SNico Weber if (b) {
1165a3bb1a4SNico Weber b->tag = (uptr)tag;
1175a3bb1a4SNico Weber }
1185a3bb1a4SNico Weber }
1195a3bb1a4SNico Weber
1205a3bb1a4SNico Weber SANITIZER_INTERFACE_ATTRIBUTE
__tsan_external_read(void * addr,void * caller_pc,void * tag)1215a3bb1a4SNico Weber void __tsan_external_read(void *addr, void *caller_pc, void *tag) {
122711ff37bSJulian Lettner ExternalAccess(addr, STRIP_PAC_PC(caller_pc), CALLERPC, tag, kAccessRead);
1235a3bb1a4SNico Weber }
1245a3bb1a4SNico Weber
1255a3bb1a4SNico Weber SANITIZER_INTERFACE_ATTRIBUTE
__tsan_external_write(void * addr,void * caller_pc,void * tag)1265a3bb1a4SNico Weber void __tsan_external_write(void *addr, void *caller_pc, void *tag) {
127711ff37bSJulian Lettner ExternalAccess(addr, STRIP_PAC_PC(caller_pc), CALLERPC, tag, kAccessWrite);
1285a3bb1a4SNico Weber }
1295a3bb1a4SNico Weber } // extern "C"
1305a3bb1a4SNico Weber
1315a3bb1a4SNico Weber #endif // !SANITIZER_GO
1325a3bb1a4SNico Weber
1335a3bb1a4SNico Weber } // namespace __tsan
134