
Currently HwASan has no fiber switch interface for coroutines. This PR adds fiber switch interfaces similar to ASan which helps to pass sp check correctly on unwinding. The only difference is HwASan does not need a fake stack since tags can do the same thing (e.g., detect UAR). Interfaces are made identical with ASan's. Also adds unit test which is similar to ASan with minor adjustments: 1. change `__asan_handle_no_return` to `__hwasan_handle_vfork` 2. remove huge stack test since `__hwasan_handle_vfork` has no stack size limitation. 3. use uninstrumented globals to simulate allocation since hwasan do not support tagged pointer while using `longjmp` The testcase is tested on both x86 with alias mode enabled and aarch64.
140 lines
3.8 KiB
C++
140 lines
3.8 KiB
C++
//===-- hwasan_thread.h -----------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file is a part of HWAddressSanitizer.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef HWASAN_THREAD_H
|
|
#define HWASAN_THREAD_H
|
|
|
|
#include "hwasan_allocator.h"
|
|
#include "sanitizer_common/sanitizer_common.h"
|
|
#include "sanitizer_common/sanitizer_ring_buffer.h"
|
|
|
|
namespace __hwasan {
|
|
|
|
typedef __sanitizer::CompactRingBuffer<uptr> StackAllocationsRingBuffer;
|
|
|
|
class Thread {
|
|
public:
|
|
// These are optional parameters that can be passed to Init.
|
|
struct InitState;
|
|
|
|
void Init(uptr stack_buffer_start, uptr stack_buffer_size,
|
|
const InitState *state = nullptr);
|
|
|
|
void InitStackAndTls(const InitState *state = nullptr);
|
|
|
|
// Must be called from the thread itself.
|
|
void InitStackRingBuffer(uptr stack_buffer_start, uptr stack_buffer_size);
|
|
|
|
inline void EnsureRandomStateInited() {
|
|
if (UNLIKELY(!random_state_inited_))
|
|
InitRandomState();
|
|
}
|
|
|
|
void Destroy();
|
|
|
|
uptr stack_top();
|
|
uptr stack_bottom();
|
|
uptr stack_size();
|
|
uptr tls_begin() { return tls_begin_; }
|
|
uptr tls_end() { return tls_end_; }
|
|
DTLS *dtls() { return dtls_; }
|
|
bool IsMainThread() { return unique_id_ == 0; }
|
|
|
|
bool AddrIsInStack(uptr addr) {
|
|
return addr >= stack_bottom_ && addr < stack_top_;
|
|
}
|
|
|
|
void StartSwitchFiber(uptr bottom, uptr size);
|
|
void FinishSwitchFiber(uptr *bottom_old, uptr *size_old);
|
|
|
|
AllocatorCache *allocator_cache() { return &allocator_cache_; }
|
|
HeapAllocationsRingBuffer *heap_allocations() { return heap_allocations_; }
|
|
StackAllocationsRingBuffer *stack_allocations() { return stack_allocations_; }
|
|
|
|
tag_t GenerateRandomTag(uptr num_bits = kTagBits);
|
|
|
|
void DisableTagging() { tagging_disabled_++; }
|
|
void EnableTagging() { tagging_disabled_--; }
|
|
|
|
u32 unique_id() const { return unique_id_; }
|
|
void Announce() {
|
|
if (announced_) return;
|
|
announced_ = true;
|
|
Print("Thread: ");
|
|
}
|
|
|
|
ThreadID os_id() const { return os_id_; }
|
|
void set_os_id(ThreadID os_id) { os_id_ = os_id; }
|
|
|
|
uptr &vfork_spill() { return vfork_spill_; }
|
|
|
|
private:
|
|
// NOTE: There is no Thread constructor. It is allocated
|
|
// via mmap() and *must* be valid in zero-initialized state.
|
|
void ClearShadowForThreadStackAndTLS();
|
|
void Print(const char *prefix);
|
|
void InitRandomState();
|
|
|
|
struct StackBounds {
|
|
uptr bottom;
|
|
uptr top;
|
|
};
|
|
StackBounds GetStackBounds() const;
|
|
|
|
uptr vfork_spill_;
|
|
uptr stack_top_;
|
|
uptr stack_bottom_;
|
|
// these variables are used when the thread is about to switch stack
|
|
uptr next_stack_top_;
|
|
uptr next_stack_bottom_;
|
|
// true if switching is in progress
|
|
atomic_uint8_t stack_switching_;
|
|
|
|
uptr tls_begin_;
|
|
uptr tls_end_;
|
|
DTLS *dtls_;
|
|
|
|
u32 random_state_;
|
|
u32 random_buffer_;
|
|
|
|
AllocatorCache allocator_cache_;
|
|
HeapAllocationsRingBuffer *heap_allocations_;
|
|
StackAllocationsRingBuffer *stack_allocations_;
|
|
|
|
u32 unique_id_; // counting from zero.
|
|
|
|
ThreadID os_id_;
|
|
|
|
u32 tagging_disabled_; // if non-zero, malloc uses zero tag in this thread.
|
|
|
|
bool announced_;
|
|
|
|
bool random_state_inited_; // Whether InitRandomState() has been called.
|
|
|
|
friend struct ThreadListHead;
|
|
};
|
|
|
|
Thread *GetCurrentThread();
|
|
uptr *GetCurrentThreadLongPtr();
|
|
|
|
// Used to handle fork().
|
|
void EnsureMainThreadIDIsCorrect();
|
|
|
|
struct ScopedTaggingDisabler {
|
|
ScopedTaggingDisabler() { GetCurrentThread()->DisableTagging(); }
|
|
~ScopedTaggingDisabler() { GetCurrentThread()->EnableTagging(); }
|
|
};
|
|
|
|
} // namespace __hwasan
|
|
|
|
#endif // HWASAN_THREAD_H
|