13cab2bb3Spatrick //===-- tsan_external.cpp -------------------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of ThreadSanitizer (TSan), a race detector.
103cab2bb3Spatrick //
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick #include "tsan_rtl.h"
13d89ec533Spatrick #include "sanitizer_common/sanitizer_ptrauth.h"
143cab2bb3Spatrick
15*810390e3Srobert #if !SANITIZER_GO
16*810390e3Srobert # include "tsan_interceptors.h"
17*810390e3Srobert #endif
18*810390e3Srobert
193cab2bb3Spatrick namespace __tsan {
203cab2bb3Spatrick
213cab2bb3Spatrick #define CALLERPC ((uptr)__builtin_return_address(0))
223cab2bb3Spatrick
233cab2bb3Spatrick struct TagData {
243cab2bb3Spatrick const char *object_type;
253cab2bb3Spatrick const char *header;
263cab2bb3Spatrick };
273cab2bb3Spatrick
283cab2bb3Spatrick static TagData registered_tags[kExternalTagMax] = {
293cab2bb3Spatrick {},
303cab2bb3Spatrick {"Swift variable", "Swift access race"},
313cab2bb3Spatrick };
323cab2bb3Spatrick static atomic_uint32_t used_tags{kExternalTagFirstUserAvailable};
GetTagData(uptr tag)333cab2bb3Spatrick static TagData *GetTagData(uptr tag) {
343cab2bb3Spatrick // Invalid/corrupted tag? Better return NULL and let the caller deal with it.
353cab2bb3Spatrick if (tag >= atomic_load(&used_tags, memory_order_relaxed)) return nullptr;
363cab2bb3Spatrick return ®istered_tags[tag];
373cab2bb3Spatrick }
383cab2bb3Spatrick
GetObjectTypeFromTag(uptr tag)393cab2bb3Spatrick const char *GetObjectTypeFromTag(uptr tag) {
403cab2bb3Spatrick TagData *tag_data = GetTagData(tag);
413cab2bb3Spatrick return tag_data ? tag_data->object_type : nullptr;
423cab2bb3Spatrick }
433cab2bb3Spatrick
GetReportHeaderFromTag(uptr tag)443cab2bb3Spatrick const char *GetReportHeaderFromTag(uptr tag) {
453cab2bb3Spatrick TagData *tag_data = GetTagData(tag);
463cab2bb3Spatrick return tag_data ? tag_data->header : nullptr;
473cab2bb3Spatrick }
483cab2bb3Spatrick
InsertShadowStackFrameForTag(ThreadState * thr,uptr tag)493cab2bb3Spatrick void InsertShadowStackFrameForTag(ThreadState *thr, uptr tag) {
503cab2bb3Spatrick FuncEntry(thr, (uptr)®istered_tags[tag]);
513cab2bb3Spatrick }
523cab2bb3Spatrick
TagFromShadowStackFrame(uptr pc)533cab2bb3Spatrick uptr TagFromShadowStackFrame(uptr pc) {
543cab2bb3Spatrick uptr tag_count = atomic_load(&used_tags, memory_order_relaxed);
553cab2bb3Spatrick void *pc_ptr = (void *)pc;
563cab2bb3Spatrick if (pc_ptr < GetTagData(0) || pc_ptr > GetTagData(tag_count - 1))
573cab2bb3Spatrick return 0;
583cab2bb3Spatrick return (TagData *)pc_ptr - GetTagData(0);
593cab2bb3Spatrick }
603cab2bb3Spatrick
613cab2bb3Spatrick #if !SANITIZER_GO
623cab2bb3Spatrick
ExternalAccess(void * addr,uptr caller_pc,void * tag,AccessType typ)63*810390e3Srobert void ExternalAccess(void *addr, uptr caller_pc, void *tag, AccessType typ) {
643cab2bb3Spatrick CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
653cab2bb3Spatrick ThreadState *thr = cur_thread();
66d89ec533Spatrick if (caller_pc) FuncEntry(thr, caller_pc);
673cab2bb3Spatrick InsertShadowStackFrameForTag(thr, (uptr)tag);
683cab2bb3Spatrick bool in_ignored_lib;
69*810390e3Srobert if (!caller_pc || !libignore()->IsIgnored(caller_pc, &in_ignored_lib))
70*810390e3Srobert MemoryAccess(thr, CALLERPC, (uptr)addr, 1, typ);
713cab2bb3Spatrick FuncExit(thr);
723cab2bb3Spatrick if (caller_pc) FuncExit(thr);
733cab2bb3Spatrick }
743cab2bb3Spatrick
753cab2bb3Spatrick extern "C" {
763cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE
__tsan_external_register_tag(const char * object_type)773cab2bb3Spatrick void *__tsan_external_register_tag(const char *object_type) {
783cab2bb3Spatrick uptr new_tag = atomic_fetch_add(&used_tags, 1, memory_order_relaxed);
793cab2bb3Spatrick CHECK_LT(new_tag, kExternalTagMax);
803cab2bb3Spatrick GetTagData(new_tag)->object_type = internal_strdup(object_type);
813cab2bb3Spatrick char header[127] = {0};
823cab2bb3Spatrick internal_snprintf(header, sizeof(header), "race on %s", object_type);
833cab2bb3Spatrick GetTagData(new_tag)->header = internal_strdup(header);
843cab2bb3Spatrick return (void *)new_tag;
853cab2bb3Spatrick }
863cab2bb3Spatrick
873cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE
__tsan_external_register_header(void * tag,const char * header)883cab2bb3Spatrick void __tsan_external_register_header(void *tag, const char *header) {
893cab2bb3Spatrick CHECK_GE((uptr)tag, kExternalTagFirstUserAvailable);
903cab2bb3Spatrick CHECK_LT((uptr)tag, kExternalTagMax);
913cab2bb3Spatrick atomic_uintptr_t *header_ptr =
923cab2bb3Spatrick (atomic_uintptr_t *)&GetTagData((uptr)tag)->header;
933cab2bb3Spatrick header = internal_strdup(header);
943cab2bb3Spatrick char *old_header =
953cab2bb3Spatrick (char *)atomic_exchange(header_ptr, (uptr)header, memory_order_seq_cst);
96*810390e3Srobert Free(old_header);
973cab2bb3Spatrick }
983cab2bb3Spatrick
993cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE
__tsan_external_assign_tag(void * addr,void * tag)1003cab2bb3Spatrick void __tsan_external_assign_tag(void *addr, void *tag) {
1013cab2bb3Spatrick CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
1023cab2bb3Spatrick Allocator *a = allocator();
1033cab2bb3Spatrick MBlock *b = nullptr;
1043cab2bb3Spatrick if (a->PointerIsMine((void *)addr)) {
1053cab2bb3Spatrick void *block_begin = a->GetBlockBegin((void *)addr);
1063cab2bb3Spatrick if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
1073cab2bb3Spatrick }
1083cab2bb3Spatrick if (b) {
1093cab2bb3Spatrick b->tag = (uptr)tag;
1103cab2bb3Spatrick }
1113cab2bb3Spatrick }
1123cab2bb3Spatrick
1133cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE
__tsan_external_read(void * addr,void * caller_pc,void * tag)1143cab2bb3Spatrick void __tsan_external_read(void *addr, void *caller_pc, void *tag) {
115*810390e3Srobert ExternalAccess(addr, STRIP_PAC_PC(caller_pc), tag, kAccessRead);
1163cab2bb3Spatrick }
1173cab2bb3Spatrick
1183cab2bb3Spatrick SANITIZER_INTERFACE_ATTRIBUTE
__tsan_external_write(void * addr,void * caller_pc,void * tag)1193cab2bb3Spatrick void __tsan_external_write(void *addr, void *caller_pc, void *tag) {
120*810390e3Srobert ExternalAccess(addr, STRIP_PAC_PC(caller_pc), tag, kAccessWrite);
1213cab2bb3Spatrick }
1223cab2bb3Spatrick } // extern "C"
1233cab2bb3Spatrick
1243cab2bb3Spatrick #endif // !SANITIZER_GO
1253cab2bb3Spatrick
1263cab2bb3Spatrick } // namespace __tsan
127