1//===-- asan_thread.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 AddressSanitizer, an address sanity checker.
10//
11// Thread-related code.
12//===----------------------------------------------------------------------===//
13#include "asan_thread.h"
14
15#include "asan_allocator.h"
16#include "asan_interceptors.h"
17#include "asan_mapping.h"
18#include "asan_poisoning.h"
19#include "asan_stack.h"
20#include "lsan/lsan_common.h"
21#include "sanitizer_common/sanitizer_common.h"
22#include "sanitizer_common/sanitizer_dl.h"
23#include "sanitizer_common/sanitizer_placement_new.h"
24#include "sanitizer_common/sanitizer_stackdepot.h"
25#include "sanitizer_common/sanitizer_thread_history.h"
26#include "sanitizer_common/sanitizer_tls_get_addr.h"
27
28namespace __asan {
29
30// AsanThreadContext implementation.
31
32void AsanThreadContext::OnCreated(void *arg) {
33 thread = static_cast<AsanThread *>(arg);
34 thread->set_context(this);
35}
36
37void AsanThreadContext::OnFinished() {
38 // Drop the link to the AsanThread object.
39 thread = nullptr;
40}
41
42static ThreadRegistry *asan_thread_registry;
43static ThreadArgRetval *thread_data;
44
45static Mutex mu_for_thread_context;
46// TODO(leonardchan@): It should be possible to make LowLevelAllocator
47// threadsafe and consolidate this one into the GlobalLoweLevelAllocator.
48// We should be able to do something similar to what's in
49// sanitizer_stack_store.cpp.
50static LowLevelAllocator allocator_for_thread_context;
51
52static ThreadContextBase *GetAsanThreadContext(u32 tid) {
53 Lock lock(&mu_for_thread_context);
54 return new (allocator_for_thread_context) AsanThreadContext(tid);
55}
56
57static void InitThreads() {
58 static bool initialized;
59 // Don't worry about thread_safety - this should be called when there is
60 // a single thread.
61 if (LIKELY(initialized))
62 return;
63 // Never reuse ASan threads: we store pointer to AsanThreadContext
64 // in TSD and can't reliably tell when no more TSD destructors will
65 // be called. It would be wrong to reuse AsanThreadContext for another
66 // thread before all TSD destructors will be called for it.
67
68 // MIPS requires aligned address
69 alignas(alignof(ThreadRegistry)) static char
70 thread_registry_placeholder[sizeof(ThreadRegistry)];
71 alignas(alignof(ThreadArgRetval)) static char
72 thread_data_placeholder[sizeof(ThreadArgRetval)];
73
74 asan_thread_registry =
75 new (thread_registry_placeholder) ThreadRegistry(GetAsanThreadContext);
76 thread_data = new (thread_data_placeholder) ThreadArgRetval();
77 initialized = true;
78}
79
80ThreadRegistry &asanThreadRegistry() {
81 InitThreads();
82 return *asan_thread_registry;
83}
84
85ThreadArgRetval &asanThreadArgRetval() {
86 InitThreads();
87 return *thread_data;
88}
89
90AsanThreadContext *GetThreadContextByTidLocked(u32 tid) {
91 return static_cast<AsanThreadContext *>(
92 asanThreadRegistry().GetThreadLocked(tid));
93}
94
95// AsanThread implementation.
96
97AsanThread *AsanThread::Create(const void *start_data, uptr data_size,
98 u32 parent_tid, StackTrace *stack,
99 bool detached) {
100 uptr PageSize = GetPageSizeCached();
101 uptr size = RoundUpTo(size: sizeof(AsanThread), boundary: PageSize);
102 AsanThread *thread = (AsanThread *)MmapOrDie(size, mem_type: __func__);
103 if (data_size) {
104 uptr availible_size = (uptr)thread + size - (uptr)(thread->start_data_);
105 CHECK_LE(data_size, availible_size);
106 internal_memcpy(dest: thread->start_data_, src: start_data, n: data_size);
107 }
108 asanThreadRegistry().CreateThread(user_id: 0, detached, parent_tid,
109 stack_tid: stack ? StackDepotPut(stack: *stack) : 0, arg: thread);
110
111 return thread;
112}
113
114void AsanThread::GetStartData(void *out, uptr out_size) const {
115 internal_memcpy(dest: out, src: start_data_, n: out_size);
116}
117
118void AsanThread::TSDDtor(void *tsd) {
119 AsanThreadContext *context = (AsanThreadContext *)tsd;
120 VReport(1, "T%d TSDDtor\n", context->tid);
121 ClearDlerror();
122 if (context->thread)
123 context->thread->Destroy();
124}
125
126void AsanThread::Destroy() {
127 int tid = this->tid();
128 VReport(1, "T%d exited\n", tid);
129
130 bool was_running =
131 (asanThreadRegistry().FinishThread(tid) == ThreadStatusRunning);
132 if (was_running) {
133 if (AsanThread *thread = GetCurrentThread())
134 CHECK_EQ(this, thread);
135 malloc_storage().CommitBack();
136 if (common_flags()->use_sigaltstack)
137 UnsetAlternateSignalStack(altstack_base: altstack_base_);
138 FlushToDeadThreadStats(stats: &stats_);
139 // We also clear the shadow on thread destruction because
140 // some code may still be executing in later TSD destructors
141 // and we don't want it to have any poisoned stack.
142 ClearShadowForThreadStackAndTLS();
143 DeleteFakeStack(tid);
144 } else {
145 CHECK_NE(this, GetCurrentThread());
146 }
147 uptr size = RoundUpTo(size: sizeof(AsanThread), boundary: GetPageSizeCached());
148 UnmapOrDie(addr: this, size);
149 if (was_running)
150 DTLS_Destroy();
151}
152
153void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom,
154 uptr size) {
155 if (atomic_load(a: &stack_switching_, mo: memory_order_relaxed)) {
156 Report(format: "ERROR: starting fiber switch while in fiber switch\n");
157 Die();
158 }
159
160 next_stack_bottom_ = bottom;
161 next_stack_top_ = bottom + size;
162 atomic_store(a: &stack_switching_, v: 1, mo: memory_order_release);
163
164 FakeStack *current_fake_stack = fake_stack_;
165 if (fake_stack_save)
166 *fake_stack_save = fake_stack_;
167 fake_stack_ = nullptr;
168 ResetTLSFakeStack();
169 // if fake_stack_save is null, the fiber will die, delete the fakestack
170 if (!fake_stack_save && current_fake_stack)
171 current_fake_stack->Destroy(tid: this->tid());
172}
173
174void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save, uptr *bottom_old,
175 uptr *size_old) {
176 if (!atomic_load(a: &stack_switching_, mo: memory_order_relaxed)) {
177 Report(format: "ERROR: finishing a fiber switch that has not started\n");
178 Die();
179 }
180
181 if (fake_stack_save) {
182 fake_stack_ = fake_stack_save;
183 ResetTLSFakeStack();
184 }
185
186 if (bottom_old)
187 *bottom_old = stack_bottom_;
188 if (size_old)
189 *size_old = stack_top_ - stack_bottom_;
190 stack_bottom_ = next_stack_bottom_;
191 stack_top_ = next_stack_top_;
192 atomic_store(a: &stack_switching_, v: 0, mo: memory_order_release);
193 next_stack_top_ = 0;
194 next_stack_bottom_ = 0;
195}
196
197inline AsanThread::StackBounds AsanThread::GetStackBounds() const {
198 if (!atomic_load(a: &stack_switching_, mo: memory_order_acquire)) {
199 // Make sure the stack bounds are fully initialized.
200 if (stack_bottom_ >= stack_top_)
201 return {.bottom: 0, .top: 0};
202 return {.bottom: stack_bottom_, .top: stack_top_};
203 }
204 char local;
205 const uptr cur_stack = (uptr)&local;
206 // Note: need to check next stack first, because FinishSwitchFiber
207 // may be in process of overwriting stack_top_/bottom_. But in such case
208 // we are already on the next stack.
209 if (cur_stack >= next_stack_bottom_ && cur_stack < next_stack_top_)
210 return {.bottom: next_stack_bottom_, .top: next_stack_top_};
211 return {.bottom: stack_bottom_, .top: stack_top_};
212}
213
214uptr AsanThread::stack_top() { return GetStackBounds().top; }
215
216uptr AsanThread::stack_bottom() { return GetStackBounds().bottom; }
217
218uptr AsanThread::stack_size() {
219 const auto bounds = GetStackBounds();
220 return bounds.top - bounds.bottom;
221}
222
223// We want to create the FakeStack lazily on the first use, but not earlier
224// than the stack size is known and the procedure has to be async-signal safe.
225FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
226 uptr stack_size = this->stack_size();
227 if (stack_size == 0) // stack_size is not yet available, don't use FakeStack.
228 return nullptr;
229 uptr old_val = 0;
230 // fake_stack_ has 3 states:
231 // 0 -- not initialized
232 // 1 -- being initialized
233 // ptr -- initialized
234 // This CAS checks if the state was 0 and if so changes it to state 1,
235 // if that was successful, it initializes the pointer.
236 if (atomic_compare_exchange_strong(
237 a: reinterpret_cast<atomic_uintptr_t *>(&fake_stack_), cmp: &old_val, xchg: 1UL,
238 mo: memory_order_relaxed)) {
239 uptr stack_size_log = Log2(x: RoundUpToPowerOfTwo(size: stack_size));
240 CHECK_LE(flags()->min_uar_stack_size_log, flags()->max_uar_stack_size_log);
241 stack_size_log =
242 Min(a: stack_size_log, b: static_cast<uptr>(flags()->max_uar_stack_size_log));
243 stack_size_log =
244 Max(a: stack_size_log, b: static_cast<uptr>(flags()->min_uar_stack_size_log));
245 fake_stack_ = FakeStack::Create(stack_size_log);
246 DCHECK_EQ(GetCurrentThread(), this);
247 ResetTLSFakeStack();
248 return fake_stack_;
249 }
250 return nullptr;
251}
252
253void AsanThread::Init(const InitOptions *options) {
254 DCHECK_NE(tid(), kInvalidTid);
255 next_stack_top_ = next_stack_bottom_ = 0;
256 fake_stack_suppression_counter_ = 0;
257 atomic_store(a: &stack_switching_, v: false, mo: memory_order_release);
258 CHECK_EQ(this->stack_size(), 0U);
259 SetThreadStackAndTls(options);
260 if (stack_top_ != stack_bottom_) {
261 CHECK_GT(this->stack_size(), 0U);
262 CHECK(AddrIsInMem(stack_bottom_));
263 CHECK(AddrIsInMem(stack_top_ - 1));
264 }
265 ClearShadowForThreadStackAndTLS();
266 fake_stack_ = nullptr;
267 if (__asan_option_detect_stack_use_after_return &&
268 tid() == GetCurrentTidOrInvalid()) {
269 // AsyncSignalSafeLazyInitFakeStack makes use of threadlocals and must be
270 // called from the context of the thread it is initializing, not its parent.
271 // Most platforms call AsanThread::Init on the newly-spawned thread, but
272 // Fuchsia calls this function from the parent thread. To support that
273 // approach, we avoid calling AsyncSignalSafeLazyInitFakeStack here; it will
274 // be called by the new thread when it first attempts to access the fake
275 // stack.
276 AsyncSignalSafeLazyInitFakeStack();
277 }
278 int local = 0;
279 VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
280 (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
281 (void *)&local);
282}
283
284// Fuchsia doesn't use ThreadStart.
285// asan_fuchsia.c definies CreateMainThread and SetThreadStackAndTls.
286#if !SANITIZER_FUCHSIA
287
288void AsanThread::ThreadStart(ThreadID os_id) {
289 Init();
290 asanThreadRegistry().StartThread(tid: tid(), os_id, thread_type: ThreadType::Regular, arg: nullptr);
291
292 if (common_flags()->use_sigaltstack)
293 altstack_base_ = SetAlternateSignalStack();
294}
295
296AsanThread *CreateMainThread() {
297 AsanThread *main_thread = AsanThread::Create(
298 /* parent_tid */ kMainTid,
299 /* stack */ nullptr, /* detached */ true);
300 SetCurrentThread(main_thread);
301 main_thread->ThreadStart(os_id: internal_getpid());
302 return main_thread;
303}
304
305// This implementation doesn't use the argument, which is just passed down
306// from the caller of Init (which see, above). It's only there to support
307// OS-specific implementations that need more information passed through.
308void AsanThread::SetThreadStackAndTls(const InitOptions *options) {
309 DCHECK_EQ(options, nullptr);
310 GetThreadStackAndTls(main: tid() == kMainTid, stk_begin: &stack_bottom_, stk_end: &stack_top_,
311 tls_begin: &tls_begin_, tls_end: &tls_end_);
312 stack_top_ = RoundDownTo(x: stack_top_, ASAN_SHADOW_GRANULARITY);
313 stack_bottom_ = RoundDownTo(x: stack_bottom_, ASAN_SHADOW_GRANULARITY);
314 dtls_ = DTLS_Get();
315
316 if (stack_top_ != stack_bottom_) {
317 int local;
318 CHECK(AddrIsInStack((uptr)&local));
319 }
320}
321
322#endif // !SANITIZER_FUCHSIA
323
324void AsanThread::ClearShadowForThreadStackAndTLS() {
325 if (stack_top_ != stack_bottom_)
326 PoisonShadow(addr: stack_bottom_, size: stack_top_ - stack_bottom_, value: 0);
327 if (tls_begin_ != tls_end_) {
328 uptr tls_begin_aligned = RoundDownTo(x: tls_begin_, ASAN_SHADOW_GRANULARITY);
329 uptr tls_end_aligned = RoundUpTo(size: tls_end_, ASAN_SHADOW_GRANULARITY);
330 FastPoisonShadow(aligned_beg: tls_begin_aligned, aligned_size: tls_end_aligned - tls_begin_aligned, value: 0);
331 }
332}
333
334bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
335 StackFrameAccess *access) {
336 if (stack_top_ == stack_bottom_)
337 return false;
338
339 uptr bottom = 0;
340 if (AddrIsInStack(addr)) {
341 bottom = stack_bottom();
342 } else if (FakeStack *fake_stack = get_fake_stack()) {
343 bottom = fake_stack->AddrIsInFakeStack(addr);
344 CHECK(bottom);
345 access->offset = addr - bottom;
346 access->frame_pc = ((uptr *)bottom)[2];
347 access->frame_descr = (const char *)((uptr *)bottom)[1];
348 return true;
349 }
350 uptr aligned_addr = RoundDownTo(x: addr, SANITIZER_WORDSIZE / 8); // align addr.
351 uptr mem_ptr = RoundDownTo(x: aligned_addr, ASAN_SHADOW_GRANULARITY);
352 u8 *shadow_ptr = (u8 *)MemToShadow(p: aligned_addr);
353 u8 *shadow_bottom = (u8 *)MemToShadow(p: bottom);
354
355 while (shadow_ptr >= shadow_bottom &&
356 *shadow_ptr != kAsanStackLeftRedzoneMagic) {
357 shadow_ptr--;
358 mem_ptr -= ASAN_SHADOW_GRANULARITY;
359 }
360
361 while (shadow_ptr >= shadow_bottom &&
362 *shadow_ptr == kAsanStackLeftRedzoneMagic) {
363 shadow_ptr--;
364 mem_ptr -= ASAN_SHADOW_GRANULARITY;
365 }
366
367 if (shadow_ptr < shadow_bottom) {
368 return false;
369 }
370
371 uptr *ptr = (uptr *)(mem_ptr + ASAN_SHADOW_GRANULARITY);
372 CHECK(ptr[0] == kCurrentStackFrameMagic);
373 access->offset = addr - (uptr)ptr;
374 access->frame_pc = ptr[2];
375 access->frame_descr = (const char *)ptr[1];
376 return true;
377}
378
379uptr AsanThread::GetStackVariableShadowStart(uptr addr) {
380 uptr bottom = 0;
381 if (AddrIsInStack(addr)) {
382 bottom = stack_bottom();
383 } else if (FakeStack *fake_stack = get_fake_stack()) {
384 bottom = fake_stack->AddrIsInFakeStack(addr);
385 if (bottom == 0) {
386 return 0;
387 }
388 } else {
389 return 0;
390 }
391
392 uptr aligned_addr = RoundDownTo(x: addr, SANITIZER_WORDSIZE / 8); // align addr.
393 u8 *shadow_ptr = (u8 *)MemToShadow(p: aligned_addr);
394 u8 *shadow_bottom = (u8 *)MemToShadow(p: bottom);
395
396 while (shadow_ptr >= shadow_bottom &&
397 (*shadow_ptr != kAsanStackLeftRedzoneMagic &&
398 *shadow_ptr != kAsanStackMidRedzoneMagic &&
399 *shadow_ptr != kAsanStackRightRedzoneMagic))
400 shadow_ptr--;
401
402 return (uptr)shadow_ptr + 1;
403}
404
405bool AsanThread::AddrIsInStack(uptr addr) {
406 const auto bounds = GetStackBounds();
407 return addr >= bounds.bottom && addr < bounds.top;
408}
409
410void AsanThread::SuppressFakeStack() {
411 ++fake_stack_suppression_counter_;
412 ResetTLSFakeStack();
413}
414
415void AsanThread::UnsuppressFakeStack() {
416 if (fake_stack_suppression_counter_ == 0) {
417 Report(format: "ERROR: Unmatched call to __asan_unsuppress_fake_stack().\n");
418 Die();
419 }
420 --fake_stack_suppression_counter_;
421}
422
423static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
424 void *addr) {
425 AsanThreadContext *tctx = static_cast<AsanThreadContext *>(tctx_base);
426 AsanThread *t = tctx->thread;
427 if (!t)
428 return false;
429 if (t->AddrIsInStack(addr: (uptr)addr))
430 return true;
431 FakeStack *fake_stack = t->get_fake_stack();
432 if (!fake_stack)
433 return false;
434 return fake_stack->AddrIsInFakeStack(addr: (uptr)addr);
435}
436
437AsanThread *GetCurrentThread() {
438 AsanThreadContext *context =
439 reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
440 if (!context) {
441 if (SANITIZER_ANDROID) {
442 // On Android, libc constructor is called _after_ asan_init, and cleans up
443 // TSD. Try to figure out if this is still the main thread by the stack
444 // address. We are not entirely sure that we have correct main thread
445 // limits, so only do this magic on Android, and only if the found thread
446 // is the main thread.
447 AsanThreadContext *tctx = GetThreadContextByTidLocked(tid: kMainTid);
448 if (tctx && ThreadStackContainsAddress(tctx_base: tctx, addr: &context)) {
449 SetCurrentThread(tctx->thread);
450 return tctx->thread;
451 }
452 }
453 return nullptr;
454 }
455 return context->thread;
456}
457
458void SetCurrentThread(AsanThread *t) {
459 CHECK(t->context());
460 VReport(2, "SetCurrentThread: %p for thread %p\n", (void *)t->context(),
461 (void *)GetThreadSelf());
462 // Make sure we do not reset the current AsanThread.
463 CHECK_EQ(0, AsanTSDGet());
464 AsanTSDSet(tsd: t->context());
465 CHECK_EQ(t->context(), AsanTSDGet());
466}
467
468u32 GetCurrentTidOrInvalid() {
469 AsanThread *t = GetCurrentThread();
470 return t ? t->tid() : kInvalidTid;
471}
472
473AsanThread *FindThreadByStackAddress(uptr addr) {
474 asanThreadRegistry().CheckLocked();
475 AsanThreadContext *tctx = static_cast<AsanThreadContext *>(
476 asanThreadRegistry().FindThreadContextLocked(cb: ThreadStackContainsAddress,
477 arg: (void *)addr));
478 return tctx ? tctx->thread : nullptr;
479}
480
481void EnsureMainThreadIDIsCorrect() {
482 AsanThreadContext *context =
483 reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
484 if (context && (context->tid == kMainTid))
485 context->os_id = GetTid();
486}
487
488__asan::AsanThread *GetAsanThreadByOsIDLocked(ThreadID os_id) {
489 __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>(
490 __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id));
491 if (!context)
492 return nullptr;
493 return context->thread;
494}
495} // namespace __asan
496
497// --- Implementation of LSan-specific functions --- {{{1
498namespace __lsan {
499void LockThreads() {
500 __asan::asanThreadRegistry().Lock();
501 __asan::asanThreadArgRetval().Lock();
502}
503
504void UnlockThreads() {
505 __asan::asanThreadArgRetval().Unlock();
506 __asan::asanThreadRegistry().Unlock();
507}
508
509static ThreadRegistry *GetAsanThreadRegistryLocked() {
510 __asan::asanThreadRegistry().CheckLocked();
511 return &__asan::asanThreadRegistry();
512}
513
514void EnsureMainThreadIDIsCorrect() { __asan::EnsureMainThreadIDIsCorrect(); }
515
516bool GetThreadRangesLocked(ThreadID os_id, uptr *stack_begin, uptr *stack_end,
517 uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
518 uptr *cache_end, DTLS **dtls) {
519 __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
520 if (!t)
521 return false;
522 *stack_begin = t->stack_bottom();
523 *stack_end = t->stack_top();
524 *tls_begin = t->tls_begin();
525 *tls_end = t->tls_end();
526 // ASan doesn't keep allocator caches in TLS, so these are unused.
527 *cache_begin = 0;
528 *cache_end = 0;
529 *dtls = t->dtls();
530 return true;
531}
532
533void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches) {}
534
535void GetThreadExtraStackRangesLocked(ThreadID os_id,
536 InternalMmapVector<Range> *ranges) {
537 __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
538 if (!t)
539 return;
540 __asan::FakeStack *fake_stack = t->get_fake_stack();
541 if (!fake_stack)
542 return;
543
544 fake_stack->ForEachFakeFrame(
545 callback: [](uptr begin, uptr end, void *arg) {
546 reinterpret_cast<InternalMmapVector<Range> *>(arg)->push_back(
547 element: {.begin: begin, .end: end});
548 },
549 arg: ranges);
550}
551
552void GetThreadExtraStackRangesLocked(InternalMmapVector<Range> *ranges) {
553 GetAsanThreadRegistryLocked()->RunCallbackForEachThreadLocked(
554 cb: [](ThreadContextBase *tctx, void *arg) {
555 GetThreadExtraStackRangesLocked(
556 os_id: tctx->os_id, ranges: reinterpret_cast<InternalMmapVector<Range> *>(arg));
557 },
558 arg: ranges);
559}
560
561void GetAdditionalThreadContextPtrsLocked(InternalMmapVector<uptr> *ptrs) {
562 __asan::asanThreadArgRetval().GetAllPtrsLocked(ptrs);
563}
564
565void GetRunningThreadsLocked(InternalMmapVector<ThreadID> *threads) {
566 GetAsanThreadRegistryLocked()->RunCallbackForEachThreadLocked(
567 cb: [](ThreadContextBase *tctx, void *threads) {
568 if (tctx->status == ThreadStatusRunning)
569 reinterpret_cast<InternalMmapVector<ThreadID> *>(threads)->push_back(
570 element: tctx->os_id);
571 },
572 arg: threads);
573}
574
575void PrintThreads() {
576 InternalScopedString out;
577 PrintThreadHistory(registry&: __asan::asanThreadRegistry(), out);
578 Report(format: "%s\n", out.data());
579}
580
581} // namespace __lsan
582
583// ---------------------- Interface ---------------- {{{1
584using namespace __asan;
585
586extern "C" {
587SANITIZER_INTERFACE_ATTRIBUTE
588void __sanitizer_start_switch_fiber(void **fakestacksave, const void *bottom,
589 uptr size) {
590 AsanThread *t = GetCurrentThread();
591 if (!t) {
592 VReport(1, "__asan_start_switch_fiber called from unknown thread\n");
593 return;
594 }
595 t->StartSwitchFiber(fake_stack_save: (FakeStack **)fakestacksave, bottom: (uptr)bottom, size);
596}
597
598SANITIZER_INTERFACE_ATTRIBUTE
599void __sanitizer_finish_switch_fiber(void *fakestack, const void **bottom_old,
600 uptr *size_old) {
601 AsanThread *t = GetCurrentThread();
602 if (!t) {
603 VReport(1, "__asan_finish_switch_fiber called from unknown thread\n");
604 return;
605 }
606 t->FinishSwitchFiber(fake_stack_save: (FakeStack *)fakestack, bottom_old: (uptr *)bottom_old,
607 size_old: (uptr *)size_old);
608}
609}
610