Merge compiler-rt trunk r351319, and resolve conflicts.
This commit is contained in:
commit
9c710df928
@ -14,7 +14,7 @@ Full text of the relevant licenses is included below.
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2009-2018 by the contributors listed in CREDITS.TXT
|
||||
Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT
|
||||
|
||||
All rights reserved.
|
||||
|
||||
|
@ -82,7 +82,6 @@ extern "C" {
|
||||
Currently available with ASan only.
|
||||
*/
|
||||
void __sanitizer_purge_allocator(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -124,6 +124,12 @@ extern "C" {
|
||||
|
||||
// Symbolizes the supplied 'pc' using the format string 'fmt'.
|
||||
// Outputs at most 'out_buf_size' bytes into 'out_buf'.
|
||||
// If 'out_buf' is not empty then output is zero or more non empty C strings
|
||||
// followed by single empty C string. Multiple strings can be returned if PC
|
||||
// corresponds to inlined function. Inlined frames are printed in the order
|
||||
// from "most-inlined" to the "least-inlined", so the last frame should be the
|
||||
// not inlined function.
|
||||
// Inlined frames can be removed with 'symbolize_inline_frames=0'.
|
||||
// The format syntax is described in
|
||||
// lib/sanitizer_common/sanitizer_stacktrace_printer.h.
|
||||
void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf,
|
||||
|
@ -19,6 +19,12 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// Initialize shadow but not the rest of the runtime.
|
||||
// Does not call libc unless there is an error.
|
||||
// Can be called multiple times, or not at all (in which case shadow will
|
||||
// be initialized in compiler-inserted __hwasan_init() call).
|
||||
void __hwasan_shadow_init(void);
|
||||
|
||||
// This function may be optionally provided by user and should return
|
||||
// a string containing HWASan runtime options. See asan_flags.h for details.
|
||||
const char* __hwasan_default_options(void);
|
||||
@ -26,6 +32,51 @@ extern "C" {
|
||||
void __hwasan_enable_allocator_tagging(void);
|
||||
void __hwasan_disable_allocator_tagging(void);
|
||||
|
||||
// Mark region of memory with the given tag. Both address and size need to be
|
||||
// 16-byte aligned.
|
||||
void __hwasan_tag_memory(const volatile void *p, unsigned char tag,
|
||||
size_t size);
|
||||
|
||||
/// Set pointer tag. Previous tag is lost.
|
||||
void *__hwasan_tag_pointer(const volatile void *p, unsigned char tag);
|
||||
|
||||
// Set memory tag from the current SP address to the given address to zero.
|
||||
// This is meant to annotate longjmp and other non-local jumps.
|
||||
// This function needs to know the (almost) exact destination frame address;
|
||||
// clearing shadow for the entire thread stack like __asan_handle_no_return
|
||||
// does would cause false reports.
|
||||
void __hwasan_handle_longjmp(const void *sp_dst);
|
||||
|
||||
// Libc hook for thread creation. Should be called in the child thread before
|
||||
// any instrumented code.
|
||||
void __hwasan_thread_enter();
|
||||
|
||||
// Libc hook for thread destruction. No instrumented code should run after
|
||||
// this call.
|
||||
void __hwasan_thread_exit();
|
||||
|
||||
// Print shadow and origin for the memory range to stderr in a human-readable
|
||||
// format.
|
||||
void __hwasan_print_shadow(const volatile void *x, size_t size);
|
||||
|
||||
// Print one-line report about the memory usage of the current process.
|
||||
void __hwasan_print_memory_usage();
|
||||
|
||||
int __sanitizer_posix_memalign(void **memptr, size_t alignment, size_t size);
|
||||
void * __sanitizer_memalign(size_t alignment, size_t size);
|
||||
void * __sanitizer_aligned_alloc(size_t alignment, size_t size);
|
||||
void * __sanitizer___libc_memalign(size_t alignment, size_t size);
|
||||
void * __sanitizer_valloc(size_t size);
|
||||
void * __sanitizer_pvalloc(size_t size);
|
||||
void __sanitizer_free(void *ptr);
|
||||
void __sanitizer_cfree(void *ptr);
|
||||
size_t __sanitizer_malloc_usable_size(const void *ptr);
|
||||
struct mallinfo __sanitizer_mallinfo();
|
||||
int __sanitizer_mallopt(int cmd, int value);
|
||||
void __sanitizer_malloc_stats(void);
|
||||
void * __sanitizer_calloc(size_t nmemb, size_t size);
|
||||
void * __sanitizer_realloc(void *ptr, size_t size);
|
||||
void * __sanitizer_malloc(size_t size);
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -21,8 +21,8 @@
|
||||
// DO NOT EDIT! THIS FILE HAS BEEN GENERATED!
|
||||
//
|
||||
// Generated with: generate_netbsd_syscalls.awk
|
||||
// Generated date: 2018-03-03
|
||||
// Generated from: syscalls.master,v 1.291 2018/01/06 16:41:23 kamil Exp
|
||||
// Generated date: 2018-10-30
|
||||
// Generated from: syscalls.master,v 1.293 2018/07/31 13:00:13 rjs Exp
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_NETBSD_SYSCALL_HOOKS_H
|
||||
@ -986,7 +986,15 @@
|
||||
#define __sanitizer_syscall_post_fpathconf(res, fd, name) \
|
||||
__sanitizer_syscall_post_impl_fpathconf(res, (long long)(fd), \
|
||||
(long long)(name))
|
||||
/* syscall 193 has been skipped */
|
||||
#define __sanitizer_syscall_pre_getsockopt2(s, level, name, val, avalsize) \
|
||||
__sanitizer_syscall_pre_impl_getsockopt2( \
|
||||
(long long)(s), (long long)(level), (long long)(name), (long long)(val), \
|
||||
(long long)(avalsize))
|
||||
#define __sanitizer_syscall_post_getsockopt2(res, s, level, name, val, \
|
||||
avalsize) \
|
||||
__sanitizer_syscall_post_impl_getsockopt2( \
|
||||
res, (long long)(s), (long long)(level), (long long)(name), \
|
||||
(long long)(val), (long long)(avalsize))
|
||||
#define __sanitizer_syscall_pre_getrlimit(which, rlp) \
|
||||
__sanitizer_syscall_pre_impl_getrlimit((long long)(which), (long long)(rlp))
|
||||
#define __sanitizer_syscall_post_getrlimit(res, which, rlp) \
|
||||
@ -1752,18 +1760,8 @@
|
||||
__sanitizer_syscall_post_impl___sigaction_sigtramp( \
|
||||
res, (long long)(signum), (long long)(nsa), (long long)(osa), \
|
||||
(long long)(tramp), (long long)(vers))
|
||||
#define __sanitizer_syscall_pre_pmc_get_info(ctr, op, args) \
|
||||
__sanitizer_syscall_pre_impl_pmc_get_info((long long)(ctr), (long long)(op), \
|
||||
(long long)(args))
|
||||
#define __sanitizer_syscall_post_pmc_get_info(res, ctr, op, args) \
|
||||
__sanitizer_syscall_post_impl_pmc_get_info( \
|
||||
res, (long long)(ctr), (long long)(op), (long long)(args))
|
||||
#define __sanitizer_syscall_pre_pmc_control(ctr, op, args) \
|
||||
__sanitizer_syscall_pre_impl_pmc_control((long long)(ctr), (long long)(op), \
|
||||
(long long)(args))
|
||||
#define __sanitizer_syscall_post_pmc_control(res, ctr, op, args) \
|
||||
__sanitizer_syscall_post_impl_pmc_control( \
|
||||
res, (long long)(ctr), (long long)(op), (long long)(args))
|
||||
/* syscall 341 has been skipped */
|
||||
/* syscall 342 has been skipped */
|
||||
#define __sanitizer_syscall_pre_rasctl(addr, len, op) \
|
||||
__sanitizer_syscall_pre_impl_rasctl((long long)(addr), (long long)(len), \
|
||||
(long long)(op))
|
||||
@ -3444,7 +3442,13 @@ void __sanitizer_syscall_post_impl_pathconf(long long res, long long path,
|
||||
void __sanitizer_syscall_pre_impl_fpathconf(long long fd, long long name);
|
||||
void __sanitizer_syscall_post_impl_fpathconf(long long res, long long fd,
|
||||
long long name);
|
||||
/* syscall 193 has been skipped */
|
||||
void __sanitizer_syscall_pre_impl_getsockopt2(long long s, long long level,
|
||||
long long name, long long val,
|
||||
long long avalsize);
|
||||
void __sanitizer_syscall_post_impl_getsockopt2(long long res, long long s,
|
||||
long long level, long long name,
|
||||
long long val,
|
||||
long long avalsize);
|
||||
void __sanitizer_syscall_pre_impl_getrlimit(long long which, long long rlp);
|
||||
void __sanitizer_syscall_post_impl_getrlimit(long long res, long long which,
|
||||
long long rlp);
|
||||
@ -4001,14 +4005,8 @@ void __sanitizer_syscall_pre_impl___sigaction_sigtramp(long long signum,
|
||||
void __sanitizer_syscall_post_impl___sigaction_sigtramp(
|
||||
long long res, long long signum, long long nsa, long long osa,
|
||||
long long tramp, long long vers);
|
||||
void __sanitizer_syscall_pre_impl_pmc_get_info(long long ctr, long long op,
|
||||
long long args);
|
||||
void __sanitizer_syscall_post_impl_pmc_get_info(long long res, long long ctr,
|
||||
long long op, long long args);
|
||||
void __sanitizer_syscall_pre_impl_pmc_control(long long ctr, long long op,
|
||||
long long args);
|
||||
void __sanitizer_syscall_post_impl_pmc_control(long long res, long long ctr,
|
||||
long long op, long long args);
|
||||
/* syscall 341 has been skipped */
|
||||
/* syscall 342 has been skipped */
|
||||
void __sanitizer_syscall_pre_impl_rasctl(long long addr, long long len,
|
||||
long long op);
|
||||
void __sanitizer_syscall_post_impl_rasctl(long long res, long long addr,
|
||||
|
@ -158,8 +158,8 @@ struct XRayLogImpl {
|
||||
/// The log initialization routine provided by the implementation, always
|
||||
/// provided with the following parameters:
|
||||
///
|
||||
/// - buffer size
|
||||
/// - maximum number of buffers
|
||||
/// - buffer size (unused)
|
||||
/// - maximum number of buffers (unused)
|
||||
/// - a pointer to an argument struct that the implementation MUST handle
|
||||
/// - the size of the argument struct
|
||||
///
|
||||
@ -355,25 +355,4 @@ XRayLogFlushStatus __xray_log_process_buffers(void (*Processor)(const char *,
|
||||
|
||||
} // extern "C"
|
||||
|
||||
namespace __xray {
|
||||
|
||||
/// DEPRECATED: Use __xray_log_init_mode(...) instead, and provide flag
|
||||
/// configuration strings to set the options instead.
|
||||
/// Options used by the LLVM XRay FDR logging implementation.
|
||||
struct FDRLoggingOptions {
|
||||
bool ReportErrors = false;
|
||||
int Fd = -1;
|
||||
};
|
||||
|
||||
/// DEPRECATED: Use __xray_log_init_mode(...) instead, and provide flag
|
||||
/// configuration strings to set the options instead.
|
||||
/// Options used by the LLVM XRay Basic (Naive) logging implementation.
|
||||
struct BasicLoggingOptions {
|
||||
int DurationFilterMicros = 0;
|
||||
size_t MaxStackDepth = 0;
|
||||
size_t ThreadBufferSize = 0;
|
||||
};
|
||||
|
||||
} // namespace __xray
|
||||
|
||||
#endif // XRAY_XRAY_LOG_INTERFACE_H
|
||||
|
@ -148,6 +148,7 @@ const uptr kAllocatorSpace = 0x600000000000ULL;
|
||||
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
|
||||
typedef DefaultSizeClassMap SizeClassMap;
|
||||
# endif
|
||||
template <typename AddressSpaceViewTy>
|
||||
struct AP64 { // Allocator64 parameters. Deliberately using a short name.
|
||||
static const uptr kSpaceBeg = kAllocatorSpace;
|
||||
static const uptr kSpaceSize = kAllocatorSize;
|
||||
@ -155,37 +156,57 @@ struct AP64 { // Allocator64 parameters. Deliberately using a short name.
|
||||
typedef __asan::SizeClassMap SizeClassMap;
|
||||
typedef AsanMapUnmapCallback MapUnmapCallback;
|
||||
static const uptr kFlags = 0;
|
||||
using AddressSpaceView = AddressSpaceViewTy;
|
||||
};
|
||||
|
||||
typedef SizeClassAllocator64<AP64> PrimaryAllocator;
|
||||
template <typename AddressSpaceView>
|
||||
using PrimaryAllocatorASVT = SizeClassAllocator64<AP64<AddressSpaceView>>;
|
||||
using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
|
||||
#else // Fallback to SizeClassAllocator32.
|
||||
static const uptr kRegionSizeLog = 20;
|
||||
static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
|
||||
# if SANITIZER_WORDSIZE == 32
|
||||
typedef FlatByteMap<kNumRegions> ByteMap;
|
||||
template <typename AddressSpaceView>
|
||||
using ByteMapASVT = FlatByteMap<kNumRegions, AddressSpaceView>;
|
||||
# elif SANITIZER_WORDSIZE == 64
|
||||
typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
|
||||
template <typename AddressSpaceView>
|
||||
using ByteMapASVT =
|
||||
TwoLevelByteMap<(kNumRegions >> 12), 1 << 12, AddressSpaceView>;
|
||||
# endif
|
||||
typedef CompactSizeClassMap SizeClassMap;
|
||||
template <typename AddressSpaceViewTy>
|
||||
struct AP32 {
|
||||
static const uptr kSpaceBeg = 0;
|
||||
static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
|
||||
static const uptr kMetadataSize = 16;
|
||||
typedef __asan::SizeClassMap SizeClassMap;
|
||||
static const uptr kRegionSizeLog = __asan::kRegionSizeLog;
|
||||
typedef __asan::ByteMap ByteMap;
|
||||
using AddressSpaceView = AddressSpaceViewTy;
|
||||
using ByteMap = __asan::ByteMapASVT<AddressSpaceView>;
|
||||
typedef AsanMapUnmapCallback MapUnmapCallback;
|
||||
static const uptr kFlags = 0;
|
||||
};
|
||||
typedef SizeClassAllocator32<AP32> PrimaryAllocator;
|
||||
template <typename AddressSpaceView>
|
||||
using PrimaryAllocatorASVT = SizeClassAllocator32<AP32<AddressSpaceView> >;
|
||||
using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
|
||||
#endif // SANITIZER_CAN_USE_ALLOCATOR64
|
||||
|
||||
static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses;
|
||||
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
|
||||
typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
|
||||
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
|
||||
SecondaryAllocator> AsanAllocator;
|
||||
template <typename AddressSpaceView>
|
||||
using AllocatorCacheASVT =
|
||||
SizeClassAllocatorLocalCache<PrimaryAllocatorASVT<AddressSpaceView>>;
|
||||
using AllocatorCache = AllocatorCacheASVT<LocalAddressSpaceView>;
|
||||
|
||||
template <typename AddressSpaceView>
|
||||
using SecondaryAllocatorASVT =
|
||||
LargeMmapAllocator<AsanMapUnmapCallback, DefaultLargeMmapAllocatorPtrArray,
|
||||
AddressSpaceView>;
|
||||
template <typename AddressSpaceView>
|
||||
using AsanAllocatorASVT =
|
||||
CombinedAllocator<PrimaryAllocatorASVT<AddressSpaceView>,
|
||||
AllocatorCacheASVT<AddressSpaceView>,
|
||||
SecondaryAllocatorASVT<AddressSpaceView>>;
|
||||
using AsanAllocator = AsanAllocatorASVT<LocalAddressSpaceView>;
|
||||
|
||||
struct AsanThreadLocalMallocStorage {
|
||||
uptr quarantine_cache[16];
|
||||
|
@ -125,9 +125,8 @@ void ErrorAllocTypeMismatch::Print() {
|
||||
Decorator d;
|
||||
Printf("%s", d.Error());
|
||||
Report("ERROR: AddressSanitizer: %s (%s vs %s) on %p\n",
|
||||
scariness.GetDescription(),
|
||||
alloc_names[alloc_type], dealloc_names[dealloc_type],
|
||||
addr_description.addr);
|
||||
scariness.GetDescription(), alloc_names[alloc_type],
|
||||
dealloc_names[dealloc_type], addr_description.Address());
|
||||
Printf("%s", d.Default());
|
||||
CHECK_GT(dealloc_stack->size, 0);
|
||||
scariness.Print();
|
||||
|
@ -110,8 +110,8 @@ struct ErrorFreeNotMalloced : ErrorBase {
|
||||
|
||||
struct ErrorAllocTypeMismatch : ErrorBase {
|
||||
const BufferedStackTrace *dealloc_stack;
|
||||
HeapAddressDescription addr_description;
|
||||
AllocType alloc_type, dealloc_type;
|
||||
AddressDescription addr_description;
|
||||
|
||||
ErrorAllocTypeMismatch() = default; // (*)
|
||||
ErrorAllocTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
|
||||
@ -119,9 +119,8 @@ struct ErrorAllocTypeMismatch : ErrorBase {
|
||||
: ErrorBase(tid, 10, "alloc-dealloc-mismatch"),
|
||||
dealloc_stack(stack),
|
||||
alloc_type(alloc_type_),
|
||||
dealloc_type(dealloc_type_) {
|
||||
GetHeapAddressInformation(addr, 1, &addr_description);
|
||||
};
|
||||
dealloc_type(dealloc_type_),
|
||||
addr_description(addr, 1, false) {}
|
||||
void Print();
|
||||
};
|
||||
|
||||
|
@ -152,8 +152,6 @@ ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
|
||||
ASAN_FLAG(bool, halt_on_error, true,
|
||||
"Crash the program after printing the first error report "
|
||||
"(WARNING: USE AT YOUR OWN RISK!)")
|
||||
ASAN_FLAG(bool, use_odr_indicator, false,
|
||||
"Use special ODR indicator symbol for ODR violation detection")
|
||||
ASAN_FLAG(bool, allocator_frees_and_returns_null_on_realloc_zero, true,
|
||||
"realloc(p, 0) is equivalent to free(p) by default (Same as the "
|
||||
"POSIX standard). If set to false, realloc(p, 0) will return a "
|
||||
|
@ -190,6 +190,13 @@ static void ThreadExitHook(void *hook, uptr os_id) {
|
||||
AsanThread::TSDDtor(per_thread);
|
||||
}
|
||||
|
||||
bool HandleDlopenInit() {
|
||||
// Not supported on this platform.
|
||||
static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
|
||||
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
// These are declared (in extern "C") by <zircon/sanitizer.h>.
|
||||
|
@ -83,9 +83,11 @@ static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
|
||||
}
|
||||
|
||||
static void ReportGlobal(const Global &g, const char *prefix) {
|
||||
Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
|
||||
prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
|
||||
g.module_name, g.has_dynamic_init);
|
||||
Report(
|
||||
"%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu "
|
||||
"odr_indicator=%p\n",
|
||||
prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
|
||||
g.module_name, g.has_dynamic_init, (void *)g.odr_indicator);
|
||||
if (g.location) {
|
||||
Report(" location (%p): name=%s[%p], %d %d\n", g.location,
|
||||
g.location->filename, g.location->filename, g.location->line_no,
|
||||
@ -133,6 +135,9 @@ enum GlobalSymbolState {
|
||||
// this method in case compiler instruments global variables through their
|
||||
// local aliases.
|
||||
static void CheckODRViolationViaIndicator(const Global *g) {
|
||||
// Instrumentation requests to skip ODR check.
|
||||
if (g->odr_indicator == UINTPTR_MAX)
|
||||
return;
|
||||
u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
|
||||
if (*odr_indicator == UNREGISTERED) {
|
||||
*odr_indicator = REGISTERED;
|
||||
@ -183,9 +188,7 @@ static void CheckODRViolationViaPoisoning(const Global *g) {
|
||||
// This routine chooses between two different methods of ODR violation
|
||||
// detection.
|
||||
static inline bool UseODRIndicator(const Global *g) {
|
||||
// Use ODR indicator method iff use_odr_indicator flag is set and
|
||||
// indicator symbol address is not 0.
|
||||
return flags()->use_odr_indicator && g->odr_indicator > 0;
|
||||
return g->odr_indicator > 0;
|
||||
}
|
||||
|
||||
// Register a global variable.
|
||||
@ -248,7 +251,7 @@ static void UnregisterGlobal(const Global *g) {
|
||||
// implementation. It might not be worth doing anyway.
|
||||
|
||||
// Release ODR indicator.
|
||||
if (UseODRIndicator(g)) {
|
||||
if (UseODRIndicator(g) && g->odr_indicator != UINTPTR_MAX) {
|
||||
u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
|
||||
*odr_indicator = UNREGISTERED;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ static void call_on_globals(void (*hook)(__asan_global *, uptr)) {
|
||||
__asan_global *end = &__asan_globals_end;
|
||||
uptr bytediff = (uptr)end - (uptr)start;
|
||||
if (bytediff % sizeof(__asan_global) != 0) {
|
||||
#ifdef SANITIZER_DLL_THUNK
|
||||
#if defined(SANITIZER_DLL_THUNK) || defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
|
||||
__debugbreak();
|
||||
#else
|
||||
CHECK("corrupt asan global array");
|
||||
|
@ -111,6 +111,11 @@ void *AsanDlSymNext(const char *sym);
|
||||
|
||||
void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);
|
||||
|
||||
// Returns `true` iff most of ASan init process should be skipped due to the
|
||||
// ASan library being loaded via `dlopen()`. Platforms may perform any
|
||||
// `dlopen()` specific initialization inside this function.
|
||||
bool HandleDlopenInit();
|
||||
|
||||
// Add convenient macro for interface functions that may be represented as
|
||||
// weak hooks.
|
||||
#define ASAN_MALLOC_HOOK(ptr, size) \
|
||||
|
@ -248,6 +248,13 @@ void *AsanDlSymNext(const char *sym) {
|
||||
return dlsym(RTLD_NEXT, sym);
|
||||
}
|
||||
|
||||
bool HandleDlopenInit() {
|
||||
// Not supported on this platform.
|
||||
static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
|
||||
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
|
||||
|
@ -209,7 +209,7 @@ INTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, mallopt, int cmd, int value) {
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
#endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
|
||||
|
||||
|
@ -61,4 +61,25 @@ using namespace __asan;
|
||||
|
||||
#include "sanitizer_common/sanitizer_malloc_mac.inc"
|
||||
|
||||
namespace COMMON_MALLOC_NAMESPACE {
|
||||
bool HandleDlopenInit() {
|
||||
static_assert(SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
|
||||
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be true");
|
||||
// We have no reliable way of knowing how we are being loaded
|
||||
// so make it a requirement on Apple platforms to set this environment
|
||||
// variable to indicate that we want to perform initialization via
|
||||
// dlopen().
|
||||
auto init_str = GetEnv("APPLE_ASAN_INIT_FOR_DLOPEN");
|
||||
if (!init_str)
|
||||
return false;
|
||||
if (internal_strncmp(init_str, "1", 1) != 0)
|
||||
return false;
|
||||
// When we are loaded via `dlopen()` path we still initialize the malloc zone
|
||||
// so Symbolication clients (e.g. `leaks`) that load the ASan allocator can
|
||||
// find an initialized malloc zone.
|
||||
InitMallocZoneFields();
|
||||
return true;
|
||||
}
|
||||
} // namespace COMMON_MALLOC_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
@ -14,8 +14,17 @@
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#if SANITIZER_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
// Intentionally not including windows.h here, to avoid the risk of
|
||||
// pulling in conflicting declarations of these functions. (With mingw-w64,
|
||||
// there's a risk of windows.h pulling in stdint.h.)
|
||||
typedef int BOOL;
|
||||
typedef void *HANDLE;
|
||||
typedef const void *LPCVOID;
|
||||
typedef void *LPVOID;
|
||||
|
||||
#define HEAP_ZERO_MEMORY 0x00000008
|
||||
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010
|
||||
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_interceptors.h"
|
||||
@ -125,12 +134,17 @@ void *_recalloc_base(void *p, size_t n, size_t elem_size) {
|
||||
}
|
||||
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
size_t _msize(const void *ptr) {
|
||||
size_t _msize(void *ptr) {
|
||||
GET_CURRENT_PC_BP_SP;
|
||||
(void)sp;
|
||||
return asan_malloc_usable_size(ptr, pc, bp);
|
||||
}
|
||||
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
size_t _msize_base(void *ptr) {
|
||||
return _msize(ptr);
|
||||
}
|
||||
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *_expand(void *memblock, size_t size) {
|
||||
// _expand is used in realloc-like functions to resize the buffer if possible.
|
||||
@ -226,6 +240,7 @@ void ReplaceSystemMalloc() {
|
||||
TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
|
||||
TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
|
||||
TryToOverrideFunction("_msize", (uptr)_msize);
|
||||
TryToOverrideFunction("_msize_base", (uptr)_msize);
|
||||
TryToOverrideFunction("_expand", (uptr)_expand);
|
||||
TryToOverrideFunction("_expand_base", (uptr)_expand);
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
// anyway by passing extra -export flags to the linker, which is exactly that
|
||||
// dllexport would normally do. We need to export them in order to make the
|
||||
// VS2015 dynamic CRT (MD) work.
|
||||
#if SANITIZER_WINDOWS
|
||||
#if SANITIZER_WINDOWS && defined(_MSC_VER)
|
||||
#define CXX_OPERATOR_ATTRIBUTE
|
||||
#define COMMENT_EXPORT(sym) __pragma(comment(linker, "/export:" sym))
|
||||
#ifdef _WIN64
|
||||
|
@ -40,6 +40,51 @@ void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
|
||||
|
||||
// ---------------------- TSD ---------------- {{{1
|
||||
|
||||
#if SANITIZER_NETBSD || SANITIZER_FREEBSD
|
||||
// Thread Static Data cannot be used in early init on NetBSD and FreeBSD.
|
||||
// Reuse the Asan TSD API for compatibility with existing code
|
||||
// with an alternative implementation.
|
||||
|
||||
static void (*tsd_destructor)(void *tsd) = nullptr;
|
||||
|
||||
struct tsd_key {
|
||||
tsd_key() : key(nullptr) {}
|
||||
~tsd_key() {
|
||||
CHECK(tsd_destructor);
|
||||
if (key)
|
||||
(*tsd_destructor)(key);
|
||||
}
|
||||
void *key;
|
||||
};
|
||||
|
||||
static thread_local struct tsd_key key;
|
||||
|
||||
void AsanTSDInit(void (*destructor)(void *tsd)) {
|
||||
CHECK(!tsd_destructor);
|
||||
tsd_destructor = destructor;
|
||||
}
|
||||
|
||||
void *AsanTSDGet() {
|
||||
CHECK(tsd_destructor);
|
||||
return key.key;
|
||||
}
|
||||
|
||||
void AsanTSDSet(void *tsd) {
|
||||
CHECK(tsd_destructor);
|
||||
CHECK(tsd);
|
||||
CHECK(!key.key);
|
||||
key.key = tsd;
|
||||
}
|
||||
|
||||
void PlatformTSDDtor(void *tsd) {
|
||||
CHECK(tsd_destructor);
|
||||
CHECK_EQ(key.key, tsd);
|
||||
key.key = nullptr;
|
||||
// Make sure that signal handler can not see a stale current thread pointer.
|
||||
atomic_signal_fence(memory_order_seq_cst);
|
||||
AsanThread::TSDDtor(tsd);
|
||||
}
|
||||
#else
|
||||
static pthread_key_t tsd_key;
|
||||
static bool tsd_key_inited = false;
|
||||
void AsanTSDInit(void (*destructor)(void *tsd)) {
|
||||
@ -67,6 +112,7 @@ void PlatformTSDDtor(void *tsd) {
|
||||
}
|
||||
AsanThread::TSDDtor(tsd);
|
||||
}
|
||||
#endif
|
||||
} // namespace __asan
|
||||
|
||||
#endif // SANITIZER_POSIX
|
||||
|
@ -12,6 +12,9 @@
|
||||
// ASan-private header for error reporting functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ASAN_REPORT_H
|
||||
#define ASAN_REPORT_H
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_thread.h"
|
||||
@ -92,3 +95,4 @@ void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr,
|
||||
BufferedStackTrace *stack);
|
||||
|
||||
} // namespace __asan
|
||||
#endif // ASAN_REPORT_H
|
||||
|
@ -213,6 +213,12 @@ static void HandleExit() {
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleDlopenInit() {
|
||||
// Not supported on this platform.
|
||||
static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
|
||||
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
|
||||
return false;
|
||||
}
|
||||
} // namespace __asan
|
||||
|
||||
// These are declared (in extern "C") by <some_path/sanitizer.h>.
|
||||
|
@ -383,6 +383,19 @@ void PrintAddressSpaceLayout() {
|
||||
kHighShadowBeg > kMidMemEnd);
|
||||
}
|
||||
|
||||
#if defined(__thumb__) && defined(__linux__)
|
||||
#define START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
|
||||
#endif
|
||||
|
||||
#ifndef START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
|
||||
static bool UNUSED __local_asan_dyninit = [] {
|
||||
MaybeStartBackgroudThread();
|
||||
SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback);
|
||||
|
||||
return false;
|
||||
}();
|
||||
#endif
|
||||
|
||||
static void AsanInitInternal() {
|
||||
if (LIKELY(asan_inited)) return;
|
||||
SanitizerToolName = "AddressSanitizer";
|
||||
@ -396,6 +409,14 @@ static void AsanInitInternal() {
|
||||
// initialization steps look at flags().
|
||||
InitializeFlags();
|
||||
|
||||
// Stop performing init at this point if we are being loaded via
|
||||
// dlopen() and the platform supports it.
|
||||
if (SANITIZER_SUPPORTS_INIT_FOR_DLOPEN && UNLIKELY(HandleDlopenInit())) {
|
||||
asan_init_is_running = false;
|
||||
VReport(1, "AddressSanitizer init is being performed for dlopen().\n");
|
||||
return;
|
||||
}
|
||||
|
||||
AsanCheckIncompatibleRT();
|
||||
AsanCheckDynamicRTPrereqs();
|
||||
AvoidCVE_2016_2143();
|
||||
@ -420,6 +441,8 @@ static void AsanInitInternal() {
|
||||
__asan_option_detect_stack_use_after_return =
|
||||
flags()->detect_stack_use_after_return;
|
||||
|
||||
__sanitizer::InitializePlatformEarly();
|
||||
|
||||
// Re-exec ourselves if we need to set additional env or command line args.
|
||||
MaybeReexec();
|
||||
|
||||
@ -447,8 +470,10 @@ static void AsanInitInternal() {
|
||||
allocator_options.SetFrom(flags(), common_flags());
|
||||
InitializeAllocator(allocator_options);
|
||||
|
||||
#ifdef START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
|
||||
MaybeStartBackgroudThread();
|
||||
SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback);
|
||||
#endif
|
||||
|
||||
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
|
||||
// should be set to 1 prior to initializing the threads.
|
||||
|
@ -223,9 +223,11 @@ void AsanThread::Init(const InitOptions *options) {
|
||||
atomic_store(&stack_switching_, false, memory_order_release);
|
||||
CHECK_EQ(this->stack_size(), 0U);
|
||||
SetThreadStackAndTls(options);
|
||||
CHECK_GT(this->stack_size(), 0U);
|
||||
CHECK(AddrIsInMem(stack_bottom_));
|
||||
CHECK(AddrIsInMem(stack_top_ - 1));
|
||||
if (stack_top_ != stack_bottom_) {
|
||||
CHECK_GT(this->stack_size(), 0U);
|
||||
CHECK(AddrIsInMem(stack_bottom_));
|
||||
CHECK(AddrIsInMem(stack_top_ - 1));
|
||||
}
|
||||
ClearShadowForThreadStackAndTLS();
|
||||
fake_stack_ = nullptr;
|
||||
if (__asan_option_detect_stack_use_after_return)
|
||||
@ -289,20 +291,23 @@ void AsanThread::SetThreadStackAndTls(const InitOptions *options) {
|
||||
DCHECK_EQ(options, nullptr);
|
||||
uptr tls_size = 0;
|
||||
uptr stack_size = 0;
|
||||
GetThreadStackAndTls(tid() == 0, const_cast<uptr *>(&stack_bottom_),
|
||||
const_cast<uptr *>(&stack_size), &tls_begin_, &tls_size);
|
||||
GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size, &tls_begin_,
|
||||
&tls_size);
|
||||
stack_top_ = stack_bottom_ + stack_size;
|
||||
tls_end_ = tls_begin_ + tls_size;
|
||||
dtls_ = DTLS_Get();
|
||||
|
||||
int local;
|
||||
CHECK(AddrIsInStack((uptr)&local));
|
||||
if (stack_top_ != stack_bottom_) {
|
||||
int local;
|
||||
CHECK(AddrIsInStack((uptr)&local));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
|
||||
void AsanThread::ClearShadowForThreadStackAndTLS() {
|
||||
PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
|
||||
if (stack_top_ != stack_bottom_)
|
||||
PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
|
||||
if (tls_begin_ != tls_end_) {
|
||||
uptr tls_begin_aligned = RoundDownTo(tls_begin_, SHADOW_GRANULARITY);
|
||||
uptr tls_end_aligned = RoundUpTo(tls_end_, SHADOW_GRANULARITY);
|
||||
@ -314,6 +319,9 @@ void AsanThread::ClearShadowForThreadStackAndTLS() {
|
||||
|
||||
bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
|
||||
StackFrameAccess *access) {
|
||||
if (stack_top_ == stack_bottom_)
|
||||
return false;
|
||||
|
||||
uptr bottom = 0;
|
||||
if (AddrIsInStack(addr)) {
|
||||
bottom = stack_bottom();
|
||||
|
@ -159,6 +159,14 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
|
||||
namespace __asan {
|
||||
|
||||
void InitializePlatformInterceptors() {
|
||||
// The interceptors were not designed to be removable, so we have to keep this
|
||||
// module alive for the life of the process.
|
||||
HMODULE pinned;
|
||||
CHECK(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_PIN,
|
||||
(LPCWSTR)&InitializePlatformInterceptors,
|
||||
&pinned));
|
||||
|
||||
ASAN_INTERCEPT_FUNC(CreateThread);
|
||||
ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
|
||||
|
||||
@ -314,6 +322,13 @@ int __asan_set_seh_filter() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool HandleDlopenInit() {
|
||||
// Not supported on this platform.
|
||||
static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
|
||||
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !ASAN_DYNAMIC
|
||||
// The CRT runs initializers in this order:
|
||||
// - C initializers, from XIA to XIZ
|
||||
|
@ -48,6 +48,7 @@ INTERCEPT_WRAP_W_WWW(_recalloc)
|
||||
INTERCEPT_WRAP_W_WWW(_recalloc_base)
|
||||
|
||||
INTERCEPT_WRAP_W_W(_msize)
|
||||
INTERCEPT_WRAP_W_W(_msize_base)
|
||||
INTERCEPT_WRAP_W_W(_expand)
|
||||
INTERCEPT_WRAP_W_W(_expand_dbg)
|
||||
|
||||
|
@ -178,7 +178,7 @@ LOCAL_LABEL(do_substraction):
|
||||
|
||||
push {r0, r1, r2, r3}
|
||||
movs r0, r4
|
||||
bl __clzsi2
|
||||
bl SYMBOL_NAME(__clzsi2)
|
||||
movs r5, r0
|
||||
pop {r0, r1, r2, r3}
|
||||
// shift = rep_clz(aSignificand) - rep_clz(implicitBit << 3);
|
||||
|
@ -55,7 +55,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmpeq)
|
||||
mov ip, #APSR_C
|
||||
msr APSR_nzcvq, ip
|
||||
#else
|
||||
msr CPSR_f, #APSR_C
|
||||
msr APSR_nzcvq, #APSR_C
|
||||
#endif
|
||||
JMP(lr)
|
||||
#endif
|
||||
@ -115,11 +115,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmple)
|
||||
movne ip, #(APSR_C)
|
||||
|
||||
1:
|
||||
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
|
||||
msr APSR_nzcvq, ip
|
||||
#else
|
||||
msr CPSR_f, ip
|
||||
#endif
|
||||
pop {r0-r3}
|
||||
POP_PC()
|
||||
#endif
|
||||
|
@ -55,7 +55,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmpeq)
|
||||
mov ip, #APSR_C
|
||||
msr APSR_nzcvq, ip
|
||||
#else
|
||||
msr CPSR_f, #APSR_C
|
||||
msr APSR_nzcvq, #APSR_C
|
||||
#endif
|
||||
JMP(lr)
|
||||
#endif
|
||||
@ -115,11 +115,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmple)
|
||||
movne ip, #(APSR_C)
|
||||
|
||||
1:
|
||||
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
|
||||
msr APSR_nzcvq, ip
|
||||
#else
|
||||
msr CPSR_f, ip
|
||||
#endif
|
||||
pop {r0-r3}
|
||||
POP_PC()
|
||||
#endif
|
||||
|
@ -16,8 +16,13 @@
|
||||
|
||||
/* Returns: the number of leading 0-bits */
|
||||
|
||||
#if !defined(__clang__) && (defined(__sparc64__) || defined(__mips64) || defined(__riscv__))
|
||||
/* gcc resolves __builtin_clz -> __clzdi2 leading to infinite recursion */
|
||||
#if !defined(__clang__) && \
|
||||
((defined(__sparc__) && defined(__arch64__)) || \
|
||||
defined(__mips64) || \
|
||||
(defined(__riscv) && __SIZEOF_POINTER__ >= 8))
|
||||
/* On 64-bit architectures with neither a native clz instruction nor a native
|
||||
* ctz instruction, gcc resolves __builtin_clz to __clzdi2 rather than
|
||||
* __clzsi2, leading to infinite recursion. */
|
||||
#define __builtin_clz(a) __clzsi2(a)
|
||||
extern si_int __clzsi2(si_int);
|
||||
#endif
|
||||
|
@ -55,6 +55,9 @@ enum ProcessorTypes {
|
||||
AMD_BTVER2,
|
||||
AMDFAM17H,
|
||||
INTEL_KNM,
|
||||
INTEL_GOLDMONT,
|
||||
INTEL_GOLDMONT_PLUS,
|
||||
INTEL_TREMONT,
|
||||
CPU_TYPE_MAX
|
||||
};
|
||||
|
||||
@ -76,6 +79,8 @@ enum ProcessorSubtypes {
|
||||
INTEL_COREI7_SKYLAKE,
|
||||
INTEL_COREI7_SKYLAKE_AVX512,
|
||||
INTEL_COREI7_CANNONLAKE,
|
||||
INTEL_COREI7_ICELAKE_CLIENT,
|
||||
INTEL_COREI7_ICELAKE_SERVER,
|
||||
CPU_SUBTYPE_MAX
|
||||
};
|
||||
|
||||
@ -110,7 +115,12 @@ enum ProcessorFeatures {
|
||||
FEATURE_AVX512IFMA,
|
||||
FEATURE_AVX5124VNNIW,
|
||||
FEATURE_AVX5124FMAPS,
|
||||
FEATURE_AVX512VPOPCNTDQ
|
||||
FEATURE_AVX512VPOPCNTDQ,
|
||||
FEATURE_AVX512VBMI2,
|
||||
FEATURE_GFNI,
|
||||
FEATURE_VPCLMULQDQ,
|
||||
FEATURE_AVX512VNNI,
|
||||
FEATURE_AVX512BITALG
|
||||
};
|
||||
|
||||
// The check below for i386 was copied from clang's cpuid.h (__get_cpuid_max).
|
||||
@ -364,6 +374,14 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model,
|
||||
case 0x4c: // really airmont
|
||||
*Type = INTEL_SILVERMONT;
|
||||
break; // "silvermont"
|
||||
// Goldmont:
|
||||
case 0x5c: // Apollo Lake
|
||||
case 0x5f: // Denverton
|
||||
*Type = INTEL_GOLDMONT;
|
||||
break; // "goldmont"
|
||||
case 0x7a:
|
||||
*Type = INTEL_GOLDMONT_PLUS;
|
||||
break;
|
||||
|
||||
case 0x57:
|
||||
*Type = INTEL_KNL; // knl
|
||||
@ -438,35 +456,45 @@ static void getAMDProcessorTypeAndSubtype(unsigned Family, unsigned Model,
|
||||
}
|
||||
|
||||
static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf,
|
||||
unsigned *FeaturesOut) {
|
||||
unsigned *FeaturesOut,
|
||||
unsigned *Features2Out) {
|
||||
unsigned Features = 0;
|
||||
unsigned Features2 = 0;
|
||||
unsigned EAX, EBX;
|
||||
|
||||
#define setFeature(F) \
|
||||
do { \
|
||||
if (F < 32) \
|
||||
Features |= 1U << (F & 0x1f); \
|
||||
else if (F < 64) \
|
||||
Features2 |= 1U << ((F - 32) & 0x1f); \
|
||||
} while (0)
|
||||
|
||||
if ((EDX >> 15) & 1)
|
||||
Features |= 1 << FEATURE_CMOV;
|
||||
setFeature(FEATURE_CMOV);
|
||||
if ((EDX >> 23) & 1)
|
||||
Features |= 1 << FEATURE_MMX;
|
||||
setFeature(FEATURE_MMX);
|
||||
if ((EDX >> 25) & 1)
|
||||
Features |= 1 << FEATURE_SSE;
|
||||
setFeature(FEATURE_SSE);
|
||||
if ((EDX >> 26) & 1)
|
||||
Features |= 1 << FEATURE_SSE2;
|
||||
setFeature(FEATURE_SSE2);
|
||||
|
||||
if ((ECX >> 0) & 1)
|
||||
Features |= 1 << FEATURE_SSE3;
|
||||
setFeature(FEATURE_SSE3);
|
||||
if ((ECX >> 1) & 1)
|
||||
Features |= 1 << FEATURE_PCLMUL;
|
||||
setFeature(FEATURE_PCLMUL);
|
||||
if ((ECX >> 9) & 1)
|
||||
Features |= 1 << FEATURE_SSSE3;
|
||||
setFeature(FEATURE_SSSE3);
|
||||
if ((ECX >> 12) & 1)
|
||||
Features |= 1 << FEATURE_FMA;
|
||||
setFeature(FEATURE_FMA);
|
||||
if ((ECX >> 19) & 1)
|
||||
Features |= 1 << FEATURE_SSE4_1;
|
||||
setFeature(FEATURE_SSE4_1);
|
||||
if ((ECX >> 20) & 1)
|
||||
Features |= 1 << FEATURE_SSE4_2;
|
||||
setFeature(FEATURE_SSE4_2);
|
||||
if ((ECX >> 23) & 1)
|
||||
Features |= 1 << FEATURE_POPCNT;
|
||||
setFeature(FEATURE_POPCNT);
|
||||
if ((ECX >> 25) & 1)
|
||||
Features |= 1 << FEATURE_AES;
|
||||
setFeature(FEATURE_AES);
|
||||
|
||||
// If CPUID indicates support for XSAVE, XRESTORE and AVX, and XGETBV
|
||||
// indicates that the AVX registers will be saved and restored on context
|
||||
@ -477,43 +505,53 @@ static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf,
|
||||
bool HasAVX512Save = HasAVX && ((EAX & 0xe0) == 0xe0);
|
||||
|
||||
if (HasAVX)
|
||||
Features |= 1 << FEATURE_AVX;
|
||||
setFeature(FEATURE_AVX);
|
||||
|
||||
bool HasLeaf7 =
|
||||
MaxLeaf >= 0x7 && !getX86CpuIDAndInfoEx(0x7, 0x0, &EAX, &EBX, &ECX, &EDX);
|
||||
|
||||
if (HasLeaf7 && ((EBX >> 3) & 1))
|
||||
Features |= 1 << FEATURE_BMI;
|
||||
setFeature(FEATURE_BMI);
|
||||
if (HasLeaf7 && ((EBX >> 5) & 1) && HasAVX)
|
||||
Features |= 1 << FEATURE_AVX2;
|
||||
setFeature(FEATURE_AVX2);
|
||||
if (HasLeaf7 && ((EBX >> 9) & 1))
|
||||
Features |= 1 << FEATURE_BMI2;
|
||||
setFeature(FEATURE_BMI2);
|
||||
if (HasLeaf7 && ((EBX >> 16) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX512F;
|
||||
setFeature(FEATURE_AVX512F);
|
||||
if (HasLeaf7 && ((EBX >> 17) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX512DQ;
|
||||
setFeature(FEATURE_AVX512DQ);
|
||||
if (HasLeaf7 && ((EBX >> 21) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX512IFMA;
|
||||
setFeature(FEATURE_AVX512IFMA);
|
||||
if (HasLeaf7 && ((EBX >> 26) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX512PF;
|
||||
setFeature(FEATURE_AVX512PF);
|
||||
if (HasLeaf7 && ((EBX >> 27) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX512ER;
|
||||
setFeature(FEATURE_AVX512ER);
|
||||
if (HasLeaf7 && ((EBX >> 28) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX512CD;
|
||||
setFeature(FEATURE_AVX512CD);
|
||||
if (HasLeaf7 && ((EBX >> 30) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX512BW;
|
||||
setFeature(FEATURE_AVX512BW);
|
||||
if (HasLeaf7 && ((EBX >> 31) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX512VL;
|
||||
setFeature(FEATURE_AVX512VL);
|
||||
|
||||
if (HasLeaf7 && ((ECX >> 1) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX512VBMI;
|
||||
setFeature(FEATURE_AVX512VBMI);
|
||||
if (HasLeaf7 && ((ECX >> 6) & 1) && HasAVX512Save)
|
||||
setFeature(FEATURE_AVX512VBMI2);
|
||||
if (HasLeaf7 && ((ECX >> 8) & 1))
|
||||
setFeature(FEATURE_GFNI);
|
||||
if (HasLeaf7 && ((ECX >> 10) & 1) && HasAVX)
|
||||
setFeature(FEATURE_VPCLMULQDQ);
|
||||
if (HasLeaf7 && ((ECX >> 11) & 1) && HasAVX512Save)
|
||||
setFeature(FEATURE_AVX512VNNI);
|
||||
if (HasLeaf7 && ((ECX >> 12) & 1) && HasAVX512Save)
|
||||
setFeature(FEATURE_AVX512BITALG);
|
||||
if (HasLeaf7 && ((ECX >> 14) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX512VPOPCNTDQ;
|
||||
setFeature(FEATURE_AVX512VPOPCNTDQ);
|
||||
|
||||
if (HasLeaf7 && ((EDX >> 2) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX5124VNNIW;
|
||||
setFeature(FEATURE_AVX5124VNNIW);
|
||||
if (HasLeaf7 && ((EDX >> 3) & 1) && HasAVX512Save)
|
||||
Features |= 1 << FEATURE_AVX5124FMAPS;
|
||||
setFeature(FEATURE_AVX5124FMAPS);
|
||||
|
||||
unsigned MaxExtLevel;
|
||||
getX86CpuIDAndInfo(0x80000000, &MaxExtLevel, &EBX, &ECX, &EDX);
|
||||
@ -521,13 +559,15 @@ static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf,
|
||||
bool HasExtLeaf1 = MaxExtLevel >= 0x80000001 &&
|
||||
!getX86CpuIDAndInfo(0x80000001, &EAX, &EBX, &ECX, &EDX);
|
||||
if (HasExtLeaf1 && ((ECX >> 6) & 1))
|
||||
Features |= 1 << FEATURE_SSE4_A;
|
||||
setFeature(FEATURE_SSE4_A);
|
||||
if (HasExtLeaf1 && ((ECX >> 11) & 1))
|
||||
Features |= 1 << FEATURE_XOP;
|
||||
setFeature(FEATURE_XOP);
|
||||
if (HasExtLeaf1 && ((ECX >> 16) & 1))
|
||||
Features |= 1 << FEATURE_FMA4;
|
||||
setFeature(FEATURE_FMA4);
|
||||
|
||||
*FeaturesOut = Features;
|
||||
*Features2Out = Features2;
|
||||
#undef setFeature
|
||||
}
|
||||
|
||||
#if defined(HAVE_INIT_PRIORITY)
|
||||
@ -548,8 +588,9 @@ struct __processor_model {
|
||||
unsigned int __cpu_subtype;
|
||||
unsigned int __cpu_features[1];
|
||||
} __cpu_model = {0, 0, 0, {0}};
|
||||
unsigned int __cpu_features2;
|
||||
|
||||
/* A constructor function that is sets __cpu_model and __cpu_features with
|
||||
/* A constructor function that is sets __cpu_model and __cpu_features2 with
|
||||
the right values. This needs to run only once. This constructor is
|
||||
given the highest priority and it should run before constructors without
|
||||
the priority set. However, it still runs after ifunc initializers and
|
||||
@ -562,6 +603,7 @@ __cpu_indicator_init(void) {
|
||||
unsigned Vendor;
|
||||
unsigned Model, Family, Brand_id;
|
||||
unsigned Features = 0;
|
||||
unsigned Features2 = 0;
|
||||
|
||||
/* This function needs to run just once. */
|
||||
if (__cpu_model.__cpu_vendor)
|
||||
@ -580,8 +622,9 @@ __cpu_indicator_init(void) {
|
||||
Brand_id = EBX & 0xff;
|
||||
|
||||
/* Find available features. */
|
||||
getAvailableFeatures(ECX, EDX, MaxLeaf, &Features);
|
||||
getAvailableFeatures(ECX, EDX, MaxLeaf, &Features, &Features2);
|
||||
__cpu_model.__cpu_features[0] = Features;
|
||||
__cpu_features2 = Features2;
|
||||
|
||||
if (Vendor == SIG_INTEL) {
|
||||
/* Get CPU type. */
|
||||
|
@ -16,8 +16,13 @@
|
||||
|
||||
/* Returns: the number of trailing 0-bits */
|
||||
|
||||
#if !defined(__clang__) && (defined(__sparc64__) || defined(__mips64) || defined(__riscv__))
|
||||
/* gcc resolves __builtin_ctz -> __ctzdi2 leading to infinite recursion */
|
||||
#if !defined(__clang__) && \
|
||||
((defined(__sparc__) && defined(__arch64__)) || \
|
||||
defined(__mips64) || \
|
||||
(defined(__riscv) && __SIZEOF_POINTER__ >= 8))
|
||||
/* On 64-bit architectures with neither a native clz instruction nor a native
|
||||
* ctz instruction, gcc resolves __builtin_ctz to __ctzdi2 rather than
|
||||
* __ctzsi2, leading to infinite recursion. */
|
||||
#define __builtin_ctz(a) __ctzsi2(a)
|
||||
extern si_int __ctzsi2(si_int);
|
||||
#endif
|
||||
|
@ -12,6 +12,8 @@
|
||||
* ===----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#define DOUBLE_PRECISION
|
||||
#include "fp_lib.h"
|
||||
#include "int_lib.h"
|
||||
#include "int_math.h"
|
||||
|
||||
@ -21,7 +23,7 @@ COMPILER_RT_ABI Dcomplex
|
||||
__divdc3(double __a, double __b, double __c, double __d)
|
||||
{
|
||||
int __ilogbw = 0;
|
||||
double __logbw = crt_logb(crt_fmax(crt_fabs(__c), crt_fabs(__d)));
|
||||
double __logbw = __compiler_rt_logb(crt_fmax(crt_fabs(__c), crt_fabs(__d)));
|
||||
if (crt_isfinite(__logbw))
|
||||
{
|
||||
__ilogbw = (int)__logbw;
|
||||
|
@ -21,36 +21,36 @@
|
||||
|
||||
COMPILER_RT_ABI fp_t
|
||||
__divdf3(fp_t a, fp_t b) {
|
||||
|
||||
|
||||
const unsigned int aExponent = toRep(a) >> significandBits & maxExponent;
|
||||
const unsigned int bExponent = toRep(b) >> significandBits & maxExponent;
|
||||
const rep_t quotientSign = (toRep(a) ^ toRep(b)) & signBit;
|
||||
|
||||
|
||||
rep_t aSignificand = toRep(a) & significandMask;
|
||||
rep_t bSignificand = toRep(b) & significandMask;
|
||||
int scale = 0;
|
||||
|
||||
|
||||
// Detect if a or b is zero, denormal, infinity, or NaN.
|
||||
if (aExponent-1U >= maxExponent-1U || bExponent-1U >= maxExponent-1U) {
|
||||
|
||||
|
||||
const rep_t aAbs = toRep(a) & absMask;
|
||||
const rep_t bAbs = toRep(b) & absMask;
|
||||
|
||||
|
||||
// NaN / anything = qNaN
|
||||
if (aAbs > infRep) return fromRep(toRep(a) | quietBit);
|
||||
// anything / NaN = qNaN
|
||||
if (bAbs > infRep) return fromRep(toRep(b) | quietBit);
|
||||
|
||||
|
||||
if (aAbs == infRep) {
|
||||
// infinity / infinity = NaN
|
||||
if (bAbs == infRep) return fromRep(qnanRep);
|
||||
// infinity / anything else = +/- infinity
|
||||
else return fromRep(aAbs | quotientSign);
|
||||
}
|
||||
|
||||
|
||||
// anything else / infinity = +/- 0
|
||||
if (bAbs == infRep) return fromRep(quotientSign);
|
||||
|
||||
|
||||
if (!aAbs) {
|
||||
// zero / zero = NaN
|
||||
if (!bAbs) return fromRep(qnanRep);
|
||||
@ -59,28 +59,28 @@ __divdf3(fp_t a, fp_t b) {
|
||||
}
|
||||
// anything else / zero = +/- infinity
|
||||
if (!bAbs) return fromRep(infRep | quotientSign);
|
||||
|
||||
|
||||
// one or both of a or b is denormal, the other (if applicable) is a
|
||||
// normal number. Renormalize one or both of a and b, and set scale to
|
||||
// include the necessary exponent adjustment.
|
||||
if (aAbs < implicitBit) scale += normalize(&aSignificand);
|
||||
if (bAbs < implicitBit) scale -= normalize(&bSignificand);
|
||||
}
|
||||
|
||||
|
||||
// Or in the implicit significand bit. (If we fell through from the
|
||||
// denormal path it was already set by normalize( ), but setting it twice
|
||||
// won't hurt anything.)
|
||||
aSignificand |= implicitBit;
|
||||
bSignificand |= implicitBit;
|
||||
int quotientExponent = aExponent - bExponent + scale;
|
||||
|
||||
|
||||
// Align the significand of b as a Q31 fixed-point number in the range
|
||||
// [1, 2.0) and get a Q32 approximate reciprocal using a small minimax
|
||||
// polynomial approximation: reciprocal = 3/4 + 1/sqrt(2) - b/2. This
|
||||
// is accurate to about 3.5 binary digits.
|
||||
const uint32_t q31b = bSignificand >> 21;
|
||||
uint32_t recip32 = UINT32_C(0x7504f333) - q31b;
|
||||
|
||||
|
||||
// Now refine the reciprocal estimate using a Newton-Raphson iteration:
|
||||
//
|
||||
// x1 = x0 * (2 - x0 * b)
|
||||
@ -95,13 +95,13 @@ __divdf3(fp_t a, fp_t b) {
|
||||
recip32 = (uint64_t)recip32 * correction32 >> 31;
|
||||
correction32 = -((uint64_t)recip32 * q31b >> 32);
|
||||
recip32 = (uint64_t)recip32 * correction32 >> 31;
|
||||
|
||||
|
||||
// recip32 might have overflowed to exactly zero in the preceding
|
||||
// computation if the high word of b is exactly 1.0. This would sabotage
|
||||
// the full-width final stage of the computation that follows, so we adjust
|
||||
// recip32 downward by one bit.
|
||||
recip32--;
|
||||
|
||||
|
||||
// We need to perform one more iteration to get us to 56 binary digits;
|
||||
// The last iteration needs to happen with extra precision.
|
||||
const uint32_t q63blo = bSignificand << 11;
|
||||
@ -110,14 +110,14 @@ __divdf3(fp_t a, fp_t b) {
|
||||
uint32_t cHi = correction >> 32;
|
||||
uint32_t cLo = correction;
|
||||
reciprocal = (uint64_t)recip32*cHi + ((uint64_t)recip32*cLo >> 32);
|
||||
|
||||
|
||||
// We already adjusted the 32-bit estimate, now we need to adjust the final
|
||||
// 64-bit reciprocal estimate downward to ensure that it is strictly smaller
|
||||
// than the infinitely precise exact reciprocal. Because the computation
|
||||
// of the Newton-Raphson step is truncating at every step, this adjustment
|
||||
// is small; most of the work is already done.
|
||||
reciprocal -= 2;
|
||||
|
||||
|
||||
// The numerical reciprocal is accurate to within 2^-56, lies in the
|
||||
// interval [0.5, 1.0), and is strictly smaller than the true reciprocal
|
||||
// of b. Multiplying a by this reciprocal thus gives a numerical q = a/b
|
||||
@ -127,12 +127,12 @@ __divdf3(fp_t a, fp_t b) {
|
||||
// 2. q is in the interval [0.5, 2.0)
|
||||
// 3. the error in q is bounded away from 2^-53 (actually, we have a
|
||||
// couple of bits to spare, but this is all we need).
|
||||
|
||||
|
||||
// We need a 64 x 64 multiply high to compute q, which isn't a basic
|
||||
// operation in C, so we need to be a little bit fussy.
|
||||
rep_t quotient, quotientLo;
|
||||
wideMultiply(aSignificand << 2, reciprocal, "ient, "ientLo);
|
||||
|
||||
|
||||
// Two cases: quotient is in [0.5, 1.0) or quotient is in [1.0, 2.0).
|
||||
// In either case, we are going to compute a residual of the form
|
||||
//
|
||||
@ -141,7 +141,7 @@ __divdf3(fp_t a, fp_t b) {
|
||||
// We know from the construction of q that r satisfies:
|
||||
//
|
||||
// 0 <= r < ulp(q)*b
|
||||
//
|
||||
//
|
||||
// if r is greater than 1/2 ulp(q)*b, then q rounds up. Otherwise, we
|
||||
// already have the correct result. The exact halfway case cannot occur.
|
||||
// We also take this time to right shift quotient if it falls in the [1,2)
|
||||
@ -154,20 +154,20 @@ __divdf3(fp_t a, fp_t b) {
|
||||
quotient >>= 1;
|
||||
residual = (aSignificand << 52) - quotient * bSignificand;
|
||||
}
|
||||
|
||||
|
||||
const int writtenExponent = quotientExponent + exponentBias;
|
||||
|
||||
|
||||
if (writtenExponent >= maxExponent) {
|
||||
// If we have overflowed the exponent, return infinity.
|
||||
return fromRep(infRep | quotientSign);
|
||||
}
|
||||
|
||||
|
||||
else if (writtenExponent < 1) {
|
||||
// Flush denormals to zero. In the future, it would be nice to add
|
||||
// code to round them correctly.
|
||||
return fromRep(quotientSign);
|
||||
}
|
||||
|
||||
|
||||
else {
|
||||
const bool round = (residual << 1) > bSignificand;
|
||||
// Clear the implicit bit
|
||||
|
@ -12,6 +12,8 @@
|
||||
*===----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#define SINGLE_PRECISION
|
||||
#include "fp_lib.h"
|
||||
#include "int_lib.h"
|
||||
#include "int_math.h"
|
||||
|
||||
@ -21,7 +23,8 @@ COMPILER_RT_ABI Fcomplex
|
||||
__divsc3(float __a, float __b, float __c, float __d)
|
||||
{
|
||||
int __ilogbw = 0;
|
||||
float __logbw = crt_logbf(crt_fmaxf(crt_fabsf(__c), crt_fabsf(__d)));
|
||||
float __logbw =
|
||||
__compiler_rt_logbf(crt_fmaxf(crt_fabsf(__c), crt_fabsf(__d)));
|
||||
if (crt_isfinite(__logbw))
|
||||
{
|
||||
__ilogbw = (int)__logbw;
|
||||
|
@ -21,36 +21,36 @@
|
||||
|
||||
COMPILER_RT_ABI fp_t
|
||||
__divsf3(fp_t a, fp_t b) {
|
||||
|
||||
|
||||
const unsigned int aExponent = toRep(a) >> significandBits & maxExponent;
|
||||
const unsigned int bExponent = toRep(b) >> significandBits & maxExponent;
|
||||
const rep_t quotientSign = (toRep(a) ^ toRep(b)) & signBit;
|
||||
|
||||
|
||||
rep_t aSignificand = toRep(a) & significandMask;
|
||||
rep_t bSignificand = toRep(b) & significandMask;
|
||||
int scale = 0;
|
||||
|
||||
|
||||
// Detect if a or b is zero, denormal, infinity, or NaN.
|
||||
if (aExponent-1U >= maxExponent-1U || bExponent-1U >= maxExponent-1U) {
|
||||
|
||||
|
||||
const rep_t aAbs = toRep(a) & absMask;
|
||||
const rep_t bAbs = toRep(b) & absMask;
|
||||
|
||||
|
||||
// NaN / anything = qNaN
|
||||
if (aAbs > infRep) return fromRep(toRep(a) | quietBit);
|
||||
// anything / NaN = qNaN
|
||||
if (bAbs > infRep) return fromRep(toRep(b) | quietBit);
|
||||
|
||||
|
||||
if (aAbs == infRep) {
|
||||
// infinity / infinity = NaN
|
||||
if (bAbs == infRep) return fromRep(qnanRep);
|
||||
// infinity / anything else = +/- infinity
|
||||
else return fromRep(aAbs | quotientSign);
|
||||
}
|
||||
|
||||
|
||||
// anything else / infinity = +/- 0
|
||||
if (bAbs == infRep) return fromRep(quotientSign);
|
||||
|
||||
|
||||
if (!aAbs) {
|
||||
// zero / zero = NaN
|
||||
if (!bAbs) return fromRep(qnanRep);
|
||||
@ -59,28 +59,28 @@ __divsf3(fp_t a, fp_t b) {
|
||||
}
|
||||
// anything else / zero = +/- infinity
|
||||
if (!bAbs) return fromRep(infRep | quotientSign);
|
||||
|
||||
|
||||
// one or both of a or b is denormal, the other (if applicable) is a
|
||||
// normal number. Renormalize one or both of a and b, and set scale to
|
||||
// include the necessary exponent adjustment.
|
||||
if (aAbs < implicitBit) scale += normalize(&aSignificand);
|
||||
if (bAbs < implicitBit) scale -= normalize(&bSignificand);
|
||||
}
|
||||
|
||||
|
||||
// Or in the implicit significand bit. (If we fell through from the
|
||||
// denormal path it was already set by normalize( ), but setting it twice
|
||||
// won't hurt anything.)
|
||||
aSignificand |= implicitBit;
|
||||
bSignificand |= implicitBit;
|
||||
int quotientExponent = aExponent - bExponent + scale;
|
||||
|
||||
|
||||
// Align the significand of b as a Q31 fixed-point number in the range
|
||||
// [1, 2.0) and get a Q32 approximate reciprocal using a small minimax
|
||||
// polynomial approximation: reciprocal = 3/4 + 1/sqrt(2) - b/2. This
|
||||
// is accurate to about 3.5 binary digits.
|
||||
uint32_t q31b = bSignificand << 8;
|
||||
uint32_t reciprocal = UINT32_C(0x7504f333) - q31b;
|
||||
|
||||
|
||||
// Now refine the reciprocal estimate using a Newton-Raphson iteration:
|
||||
//
|
||||
// x1 = x0 * (2 - x0 * b)
|
||||
@ -95,7 +95,7 @@ __divsf3(fp_t a, fp_t b) {
|
||||
reciprocal = (uint64_t)reciprocal * correction >> 31;
|
||||
correction = -((uint64_t)reciprocal * q31b >> 32);
|
||||
reciprocal = (uint64_t)reciprocal * correction >> 31;
|
||||
|
||||
|
||||
// Exhaustive testing shows that the error in reciprocal after three steps
|
||||
// is in the interval [-0x1.f58108p-31, 0x1.d0e48cp-29], in line with our
|
||||
// expectations. We bump the reciprocal by a tiny value to force the error
|
||||
@ -103,7 +103,7 @@ __divsf3(fp_t a, fp_t b) {
|
||||
// be specific). This also causes 1/1 to give a sensible approximation
|
||||
// instead of zero (due to overflow).
|
||||
reciprocal -= 2;
|
||||
|
||||
|
||||
// The numerical reciprocal is accurate to within 2^-28, lies in the
|
||||
// interval [0x1.000000eep-1, 0x1.fffffffcp-1], and is strictly smaller
|
||||
// than the true reciprocal of b. Multiplying a by this reciprocal thus
|
||||
@ -115,9 +115,9 @@ __divsf3(fp_t a, fp_t b) {
|
||||
// from the fact that we truncate the product, and the 2^27 term
|
||||
// is the error in the reciprocal of b scaled by the maximum
|
||||
// possible value of a. As a consequence of this error bound,
|
||||
// either q or nextafter(q) is the correctly rounded
|
||||
// either q or nextafter(q) is the correctly rounded
|
||||
rep_t quotient = (uint64_t)reciprocal*(aSignificand << 1) >> 32;
|
||||
|
||||
|
||||
// Two cases: quotient is in [0.5, 1.0) or quotient is in [1.0, 2.0).
|
||||
// In either case, we are going to compute a residual of the form
|
||||
//
|
||||
@ -126,7 +126,7 @@ __divsf3(fp_t a, fp_t b) {
|
||||
// We know from the construction of q that r satisfies:
|
||||
//
|
||||
// 0 <= r < ulp(q)*b
|
||||
//
|
||||
//
|
||||
// if r is greater than 1/2 ulp(q)*b, then q rounds up. Otherwise, we
|
||||
// already have the correct result. The exact halfway case cannot occur.
|
||||
// We also take this time to right shift quotient if it falls in the [1,2)
|
||||
@ -141,18 +141,18 @@ __divsf3(fp_t a, fp_t b) {
|
||||
}
|
||||
|
||||
const int writtenExponent = quotientExponent + exponentBias;
|
||||
|
||||
|
||||
if (writtenExponent >= maxExponent) {
|
||||
// If we have overflowed the exponent, return infinity.
|
||||
return fromRep(infRep | quotientSign);
|
||||
}
|
||||
|
||||
|
||||
else if (writtenExponent < 1) {
|
||||
// Flush denormals to zero. In the future, it would be nice to add
|
||||
// code to round them correctly.
|
||||
return fromRep(quotientSign);
|
||||
}
|
||||
|
||||
|
||||
else {
|
||||
const bool round = (residual << 1) > bSignificand;
|
||||
// Clear the implicit bit
|
||||
|
@ -12,6 +12,8 @@
|
||||
*===----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#define QUAD_PRECISION
|
||||
#include "fp_lib.h"
|
||||
#include "int_lib.h"
|
||||
#include "int_math.h"
|
||||
|
||||
@ -21,7 +23,8 @@ COMPILER_RT_ABI Lcomplex
|
||||
__divtc3(long double __a, long double __b, long double __c, long double __d)
|
||||
{
|
||||
int __ilogbw = 0;
|
||||
long double __logbw = crt_logbl(crt_fmaxl(crt_fabsl(__c), crt_fabsl(__d)));
|
||||
long double __logbw =
|
||||
__compiler_rt_logbl(crt_fmaxl(crt_fabsl(__c), crt_fabsl(__d)));
|
||||
if (crt_isfinite(__logbw))
|
||||
{
|
||||
__ilogbw = (int)__logbw;
|
||||
|
@ -42,6 +42,7 @@ static void emutls_shutdown(emutls_address_array *array);
|
||||
|
||||
static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_key_t emutls_pthread_key;
|
||||
static bool emutls_key_created = false;
|
||||
|
||||
typedef unsigned int gcc_word __attribute__((mode(word)));
|
||||
typedef unsigned int gcc_pointer __attribute__((mode(pointer)));
|
||||
@ -109,6 +110,7 @@ static void emutls_key_destructor(void* ptr) {
|
||||
static __inline void emutls_init(void) {
|
||||
if (pthread_key_create(&emutls_pthread_key, emutls_key_destructor) != 0)
|
||||
abort();
|
||||
emutls_key_created = true;
|
||||
}
|
||||
|
||||
static __inline void emutls_init_once(void) {
|
||||
@ -390,3 +392,14 @@ void* __emutls_get_address(__emutls_control* control) {
|
||||
array->data[index] = emutls_allocate_object(control);
|
||||
return array->data[index];
|
||||
}
|
||||
|
||||
#ifdef __BIONIC__
|
||||
/* Called by Bionic on dlclose to delete the emutls pthread key. */
|
||||
__attribute__((visibility("hidden")))
|
||||
void __emutls_unregister_key(void) {
|
||||
if (emutls_key_created) {
|
||||
pthread_key_delete(emutls_pthread_key);
|
||||
emutls_key_created = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include "int_lib.h"
|
||||
#include "int_math.h"
|
||||
|
||||
// x86_64 FreeBSD prior v9.3 define fixed-width types incorrectly in
|
||||
// 32-bit mode.
|
||||
@ -265,6 +266,62 @@ static __inline void wideRightShiftWithSticky(rep_t *hi, rep_t *lo, unsigned int
|
||||
*hi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Implements logb methods (logb, logbf, logbl) for IEEE-754. This avoids
|
||||
// pulling in a libm dependency from compiler-rt, but is not meant to replace
|
||||
// it (i.e. code calling logb() should get the one from libm, not this), hence
|
||||
// the __compiler_rt prefix.
|
||||
static __inline fp_t __compiler_rt_logbX(fp_t x) {
|
||||
rep_t rep = toRep(x);
|
||||
int exp = (rep & exponentMask) >> significandBits;
|
||||
|
||||
// Abnormal cases:
|
||||
// 1) +/- inf returns +inf; NaN returns NaN
|
||||
// 2) 0.0 returns -inf
|
||||
if (exp == maxExponent) {
|
||||
if (((rep & signBit) == 0) || (x != x)) {
|
||||
return x; // NaN or +inf: return x
|
||||
} else {
|
||||
return -x; // -inf: return -x
|
||||
}
|
||||
} else if (x == 0.0) {
|
||||
// 0.0: return -inf
|
||||
return fromRep(infRep | signBit);
|
||||
}
|
||||
|
||||
if (exp != 0) {
|
||||
// Normal number
|
||||
return exp - exponentBias; // Unbias exponent
|
||||
} else {
|
||||
// Subnormal number; normalize and repeat
|
||||
rep &= absMask;
|
||||
const int shift = 1 - normalize(&rep);
|
||||
exp = (rep & exponentMask) >> significandBits;
|
||||
return exp - exponentBias - shift; // Unbias exponent
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SINGLE_PRECISION)
|
||||
static __inline fp_t __compiler_rt_logbf(fp_t x) {
|
||||
return __compiler_rt_logbX(x);
|
||||
}
|
||||
#elif defined(DOUBLE_PRECISION)
|
||||
static __inline fp_t __compiler_rt_logb(fp_t x) {
|
||||
return __compiler_rt_logbX(x);
|
||||
}
|
||||
#elif defined(QUAD_PRECISION)
|
||||
#if defined(CRT_LDBL_128BIT)
|
||||
static __inline fp_t __compiler_rt_logbl(fp_t x) {
|
||||
return __compiler_rt_logbX(x);
|
||||
}
|
||||
#else
|
||||
// The generic implementation only works for ieee754 floating point. For other
|
||||
// floating point types, continue to rely on the libm implementation for now.
|
||||
static __inline long double __compiler_rt_logbl(long double x) {
|
||||
return crt_logbl(x);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // FP_LIB_HEADER
|
||||
|
@ -206,8 +206,8 @@ __gcc_personality_v0(int version, _Unwind_Action actions,
|
||||
if ( lsda == (uint8_t*) 0 )
|
||||
return continueUnwind(exceptionObject, context);
|
||||
|
||||
uintptr_t pc = _Unwind_GetIP(context)-1;
|
||||
uintptr_t funcStart = _Unwind_GetRegionStart(context);
|
||||
uintptr_t pc = (uintptr_t)_Unwind_GetIP(context)-1;
|
||||
uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context);
|
||||
uintptr_t pcOffset = pc - funcStart;
|
||||
|
||||
/* Parse LSDA header. */
|
||||
@ -249,4 +249,3 @@ __gcc_personality_v0(int version, _Unwind_Action actions,
|
||||
/* No landing pad found, continue unwinding. */
|
||||
return continueUnwind(exceptionObject, context);
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
#define AEABI_RTABI __attribute__((__pcs__("aapcs")))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#define ALWAYS_INLINE __forceinline
|
||||
#define NOINLINE __declspec(noinline)
|
||||
#define NORETURN __declspec(noreturn)
|
||||
|
@ -92,12 +92,8 @@
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#define crt_logb(x) logb((x))
|
||||
#define crt_logbf(x) logbf((x))
|
||||
#define crt_logbl(x) logbl((x))
|
||||
#else
|
||||
#define crt_logb(x) __builtin_logb((x))
|
||||
#define crt_logbf(x) __builtin_logbf((x))
|
||||
#define crt_logbl(x) __builtin_logbl((x))
|
||||
#endif
|
||||
|
||||
|
@ -60,10 +60,19 @@ typedef union
|
||||
}s;
|
||||
} udwords;
|
||||
|
||||
#if (defined(__LP64__) || defined(__wasm__) || defined(__mips64)) || defined(__riscv)
|
||||
#if defined(__LP64__) || defined(__wasm__) || defined(__mips64) || \
|
||||
defined(__riscv) || defined(_WIN64)
|
||||
#define CRT_HAS_128BIT
|
||||
#endif
|
||||
|
||||
/* MSVC doesn't have a working 128bit integer type. Users should really compile
|
||||
* compiler-rt with clang, but if they happen to be doing a standalone build for
|
||||
* asan or something else, disable the 128 bit parts so things sort of work.
|
||||
*/
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#undef CRT_HAS_128BIT
|
||||
#endif
|
||||
|
||||
#ifdef CRT_HAS_128BIT
|
||||
typedef int ti_int __attribute__ ((mode (TI)));
|
||||
typedef unsigned tu_int __attribute__ ((mode (TI)));
|
||||
@ -139,6 +148,18 @@ typedef struct
|
||||
#endif /* _YUGA_LITTLE_ENDIAN */
|
||||
} uqwords;
|
||||
|
||||
/* Check if the target supports 80 bit extended precision long doubles.
|
||||
* Notably, on x86 Windows, MSVC only provides a 64-bit long double, but GCC
|
||||
* still makes it 80 bits. Clang will match whatever compiler it is trying to
|
||||
* be compatible with.
|
||||
*/
|
||||
#if ((defined(__i386__) || defined(__x86_64__)) && !defined(_MSC_VER)) || \
|
||||
defined(__m68k__) || defined(__ia64__)
|
||||
#define HAS_80_BIT_LONG_DOUBLE 1
|
||||
#else
|
||||
#define HAS_80_BIT_LONG_DOUBLE 0
|
||||
#endif
|
||||
|
||||
#ifndef _STANDALONE
|
||||
typedef union
|
||||
{
|
||||
|
@ -27,7 +27,7 @@ NORETURN extern void panic(const char *, ...);
|
||||
#ifndef _WIN32
|
||||
__attribute__((visibility("hidden")))
|
||||
#endif
|
||||
void compilerrt_abort_impl(const char *file, int line, const char *function) {
|
||||
void __compilerrt_abort_impl(const char *file, int line, const char *function) {
|
||||
panic("%s:%d: abort in %s", file, line, function);
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ NORETURN extern void __assert_rtn(const char *func, const char *file, int line,
|
||||
__attribute__((weak))
|
||||
__attribute__((visibility("hidden")))
|
||||
#endif
|
||||
void compilerrt_abort_impl(const char *file, int line, const char *function) {
|
||||
void __compilerrt_abort_impl(const char *file, int line, const char *function) {
|
||||
__assert_rtn(function, file, line, "libcompiler_rt abort");
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ void compilerrt_abort_impl(const char *file, int line, const char *function) {
|
||||
__attribute__((weak))
|
||||
__attribute__((visibility("hidden")))
|
||||
#endif
|
||||
void compilerrt_abort_impl(const char *file, int line, const char *function) {
|
||||
void __compilerrt_abort_impl(const char *file, int line, const char *function) {
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ void compilerrt_abort_impl(const char *file, int line, const char *function) {
|
||||
__attribute__((weak))
|
||||
__attribute__((visibility("hidden")))
|
||||
#endif
|
||||
void compilerrt_abort_impl(const char *file, int line, const char *function) {
|
||||
void __compilerrt_abort_impl(const char *file, int line, const char *function) {
|
||||
abort();
|
||||
}
|
||||
|
||||
|
@ -20,10 +20,10 @@
|
||||
#define INT_UTIL_H
|
||||
|
||||
/** \brief Trigger a program abort (or panic for kernel code). */
|
||||
#define compilerrt_abort() compilerrt_abort_impl(__FILE__, __LINE__, __func__)
|
||||
#define compilerrt_abort() __compilerrt_abort_impl(__FILE__, __LINE__, __func__)
|
||||
|
||||
NORETURN void compilerrt_abort_impl(const char *file, int line,
|
||||
const char *function);
|
||||
NORETURN void __compilerrt_abort_impl(const char *file, int line,
|
||||
const char *function);
|
||||
|
||||
#define COMPILE_TIME_ASSERT(expr) COMPILE_TIME_ASSERT1(expr, __COUNTER__)
|
||||
#define COMPILE_TIME_ASSERT1(expr, cnt) COMPILE_TIME_ASSERT2(expr, cnt)
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <TargetConditionals.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <dlfcn.h>
|
||||
@ -28,6 +27,33 @@
|
||||
static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
|
||||
static dispatch_once_t DispatchOnceCounter;
|
||||
|
||||
/* We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
|
||||
* just forward declare everything that we need from it. */
|
||||
|
||||
typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
|
||||
*CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
|
||||
|
||||
#if __LLP64__
|
||||
typedef unsigned long long CFTypeID;
|
||||
typedef unsigned long long CFOptionFlags;
|
||||
typedef signed long long CFIndex;
|
||||
#else
|
||||
typedef unsigned long CFTypeID;
|
||||
typedef unsigned long CFOptionFlags;
|
||||
typedef signed long CFIndex;
|
||||
#endif
|
||||
|
||||
typedef unsigned char UInt8;
|
||||
typedef _Bool Boolean;
|
||||
typedef CFIndex CFPropertyListFormat;
|
||||
typedef uint32_t CFStringEncoding;
|
||||
|
||||
/* kCFStringEncodingASCII analog. */
|
||||
#define CF_STRING_ENCODING_ASCII 0x0600
|
||||
/* kCFStringEncodingUTF8 analog. */
|
||||
#define CF_STRING_ENCODING_UTF8 0x08000100
|
||||
#define CF_PROPERTY_LIST_IMMUTABLE 0
|
||||
|
||||
typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,
|
||||
const UInt8 *, CFIndex,
|
||||
CFAllocatorRef);
|
||||
@ -55,8 +81,7 @@ static void parseSystemVersionPList(void *Unused) {
|
||||
const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
|
||||
if (!NullAllocator)
|
||||
return;
|
||||
const CFAllocatorRef kCFAllocatorNull =
|
||||
*(const CFAllocatorRef *)NullAllocator;
|
||||
const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;
|
||||
CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =
|
||||
(CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,
|
||||
"CFDataCreateWithBytesNoCopy");
|
||||
@ -140,21 +165,21 @@ static void parseSystemVersionPList(void *Unused) {
|
||||
/* Get the file buffer into CF's format. We pass in a null allocator here *
|
||||
* because we free PListBuf ourselves */
|
||||
FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
|
||||
NULL, PListBuf, (CFIndex)NumRead, kCFAllocatorNull);
|
||||
NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);
|
||||
if (!FileContentsRef)
|
||||
goto Fail;
|
||||
|
||||
if (CFPropertyListCreateWithDataFunc)
|
||||
PListRef = (*CFPropertyListCreateWithDataFunc)(
|
||||
NULL, FileContentsRef, kCFPropertyListImmutable, NULL, NULL);
|
||||
NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
|
||||
else
|
||||
PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
|
||||
NULL, FileContentsRef, kCFPropertyListImmutable, NULL);
|
||||
NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
|
||||
if (!PListRef)
|
||||
goto Fail;
|
||||
|
||||
CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
|
||||
NULL, "ProductVersion", kCFStringEncodingASCII, kCFAllocatorNull);
|
||||
NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
|
||||
if (!ProductVersion)
|
||||
goto Fail;
|
||||
CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
|
||||
@ -165,7 +190,7 @@ static void parseSystemVersionPList(void *Unused) {
|
||||
|
||||
char VersionStr[32];
|
||||
if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
|
||||
sizeof(VersionStr), kCFStringEncodingUTF8))
|
||||
sizeof(VersionStr), CF_STRING_ENCODING_UTF8))
|
||||
goto Fail;
|
||||
sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
|
||||
|
||||
|
@ -4,6 +4,11 @@
|
||||
|
||||
#include "DD.h"
|
||||
#include "../int_math.h"
|
||||
// Use DOUBLE_PRECISION because the soft-fp method we use is logb (on the upper
|
||||
// half of the long doubles), even though this file defines complex division for
|
||||
// 128-bit floats.
|
||||
#define DOUBLE_PRECISION
|
||||
#include "../fp_lib.h"
|
||||
|
||||
#if !defined(CRT_INFINITY) && defined(HUGE_VAL)
|
||||
#define CRT_INFINITY HUGE_VAL
|
||||
@ -21,9 +26,10 @@ __divtc3(long double a, long double b, long double c, long double d)
|
||||
DD dDD = { .ld = d };
|
||||
|
||||
int ilogbw = 0;
|
||||
const double logbw = crt_logb(crt_fmax(crt_fabs(cDD.s.hi), crt_fabs(dDD.s.hi) ));
|
||||
|
||||
if (crt_isfinite(logbw))
|
||||
const double logbw = __compiler_rt_logb(
|
||||
crt_fmax(crt_fabs(cDD.s.hi), crt_fabs(dDD.s.hi)));
|
||||
|
||||
if (crt_isfinite(logbw))
|
||||
{
|
||||
ilogbw = (int)logbw;
|
||||
|
||||
|
106
contrib/compiler-rt/lib/builtins/ppc/fixunstfti.c
Normal file
106
contrib/compiler-rt/lib/builtins/ppc/fixunstfti.c
Normal file
@ -0,0 +1,106 @@
|
||||
//===-- lib/builtins/ppc/fixunstfti.c - Convert long double->int128 *-C -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements converting the 128bit IBM/PowerPC long double (double-
|
||||
// double) data type to an unsigned 128 bit integer.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../int_math.h"
|
||||
#define BIAS 1023
|
||||
|
||||
/* Convert long double into an unsigned 128-bit integer. */
|
||||
__uint128_t __fixunstfti(long double input) {
|
||||
|
||||
/* If we are trying to convert a NaN, return the NaN bit pattern. */
|
||||
if (crt_isnan(input)) {
|
||||
return ((__uint128_t)0x7FF8000000000000ll) << 64 |
|
||||
(__uint128_t)0x0000000000000000ll;
|
||||
}
|
||||
|
||||
__uint128_t result, hiResult, loResult;
|
||||
int hiExponent, loExponent, shift;
|
||||
/* The long double representation, with the high and low portions of
|
||||
* the long double, and the corresponding bit patterns of each double. */
|
||||
union {
|
||||
long double ld;
|
||||
double d[2]; /* [0] is the high double, [1] is the low double. */
|
||||
unsigned long long ull[2]; /* High and low doubles as 64-bit integers. */
|
||||
} ldUnion;
|
||||
|
||||
/* If the long double is less than 1.0 or negative,
|
||||
* return 0.0. */
|
||||
if (input < 1.0)
|
||||
return 0.0;
|
||||
|
||||
/* Retrieve the 64-bit patterns of high and low doubles.
|
||||
* Compute the unbiased exponent of both high and low doubles by
|
||||
* removing the signs, isolating the exponent, and subtracting
|
||||
* the bias from it. */
|
||||
ldUnion.ld = input;
|
||||
hiExponent = ((ldUnion.ull[0] & 0x7FFFFFFFFFFFFFFFll) >> 52) - BIAS;
|
||||
loExponent = ((ldUnion.ull[1] & 0x7FFFFFFFFFFFFFFFll) >> 52) - BIAS;
|
||||
|
||||
/* Convert each double into int64; they will be added to the int128 result.
|
||||
* CASE 1: High or low double fits in int64
|
||||
* - Convert the each double normally into int64.
|
||||
*
|
||||
* CASE 2: High or low double does not fit in int64
|
||||
* - Scale the double to fit within a 64-bit integer
|
||||
* - Calculate the shift (amount to scale the double by in the int128)
|
||||
* - Clear all the bits of the exponent (with 0x800FFFFFFFFFFFFF)
|
||||
* - Add BIAS+53 (0x4350000000000000) to exponent to correct the value
|
||||
* - Scale (move) the double to the correct place in the int128
|
||||
* (Move it by 2^53 places)
|
||||
*
|
||||
* Note: If the high double is assumed to be positive, an unsigned conversion
|
||||
* from long double to 64-bit integer is needed. The low double can be either
|
||||
* positive or negative, so a signed conversion is needed to retain the result
|
||||
* of the low double and to ensure it does not simply get converted to 0. */
|
||||
|
||||
/* CASE 1 - High double fits in int64. */
|
||||
if (hiExponent < 63) {
|
||||
hiResult = (unsigned long long)ldUnion.d[0];
|
||||
} else if (hiExponent < 128) {
|
||||
/* CASE 2 - High double does not fit in int64, scale and convert it. */
|
||||
shift = hiExponent - 54;
|
||||
ldUnion.ull[0] &= 0x800FFFFFFFFFFFFFll;
|
||||
ldUnion.ull[0] |= 0x4350000000000000ll;
|
||||
hiResult = (unsigned long long)ldUnion.d[0];
|
||||
hiResult <<= shift;
|
||||
} else {
|
||||
/* Detect cases for overflow. When the exponent of the high
|
||||
* double is greater than 128 bits and when the long double
|
||||
* input is positive, return the max 128-bit integer.
|
||||
* For negative inputs with exponents > 128, return 1, like gcc. */
|
||||
if (ldUnion.d[0] > 0) {
|
||||
return ((__uint128_t)0xFFFFFFFFFFFFFFFFll) << 64 |
|
||||
(__uint128_t)0xFFFFFFFFFFFFFFFFll;
|
||||
} else {
|
||||
return ((__uint128_t)0x0000000000000000ll) << 64 |
|
||||
(__uint128_t)0x0000000000000001ll;
|
||||
}
|
||||
}
|
||||
|
||||
/* CASE 1 - Low double fits in int64. */
|
||||
if (loExponent < 63) {
|
||||
loResult = (long long)ldUnion.d[1];
|
||||
} else {
|
||||
/* CASE 2 - Low double does not fit in int64, scale and convert it. */
|
||||
shift = loExponent - 54;
|
||||
ldUnion.ull[1] &= 0x800FFFFFFFFFFFFFll;
|
||||
ldUnion.ull[1] |= 0x4350000000000000ll;
|
||||
loResult = (long long)ldUnion.d[1];
|
||||
loResult <<= shift;
|
||||
}
|
||||
|
||||
/* Add the high and low doublewords together to form a 128 bit integer. */
|
||||
result = loResult + hiResult;
|
||||
return result;
|
||||
}
|
48
contrib/compiler-rt/lib/builtins/ppc/floattitf.c
Normal file
48
contrib/compiler-rt/lib/builtins/ppc/floattitf.c
Normal file
@ -0,0 +1,48 @@
|
||||
//===-- lib/builtins/ppc/floattitf.c - Convert int128->long double -*-C -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements converting a signed 128 bit integer to a 128bit IBM /
|
||||
// PowerPC long double (double-double) value.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Conversions from signed and unsigned 64-bit int to long double. */
|
||||
long double __floatditf(int64_t);
|
||||
long double __floatunditf(uint64_t);
|
||||
|
||||
/* Convert a signed 128-bit integer to long double.
|
||||
* This uses the following property: Let hi and lo be 64-bits each,
|
||||
* and let signed_val_k() and unsigned_val_k() be the value of the
|
||||
* argument interpreted as a signed or unsigned k-bit integer. Then,
|
||||
*
|
||||
* signed_val_128(hi,lo) = signed_val_64(hi) * 2^64 + unsigned_val_64(lo)
|
||||
* = (long double)hi * 2^64 + (long double)lo,
|
||||
*
|
||||
* where (long double)hi and (long double)lo are signed and
|
||||
* unsigned 64-bit integer to long double conversions, respectively.
|
||||
*/
|
||||
long double __floattitf(__int128_t arg) {
|
||||
/* Split the int128 argument into 64-bit high and low int64 parts. */
|
||||
int64_t ArgHiPart = (int64_t)(arg >> 64);
|
||||
uint64_t ArgLoPart = (uint64_t)arg;
|
||||
|
||||
/* Convert each 64-bit part into long double. The high part
|
||||
* must be a signed conversion and the low part an unsigned conversion
|
||||
* to ensure the correct result. */
|
||||
long double ConvertedHiPart = __floatditf(ArgHiPart);
|
||||
long double ConvertedLoPart = __floatunditf(ArgLoPart);
|
||||
|
||||
/* The low bit of ArgHiPart corresponds to the 2^64 bit in arg.
|
||||
* Multiply the high part by 2^64 to undo the right shift by 64-bits
|
||||
* done in the splitting. Then, add to the low part to obtain the
|
||||
* final result. */
|
||||
return ((ConvertedHiPart * 0x1.0p64) + ConvertedLoPart);
|
||||
}
|
@ -13,15 +13,33 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#if SANITIZER_FREEBSD
|
||||
#include <sys/link_elf.h>
|
||||
#endif
|
||||
#include <link.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
typedef ElfW(Phdr) Elf_Phdr;
|
||||
typedef ElfW(Ehdr) Elf_Ehdr;
|
||||
typedef ElfW(Addr) Elf_Addr;
|
||||
typedef ElfW(Sym) Elf_Sym;
|
||||
typedef ElfW(Dyn) Elf_Dyn;
|
||||
#elif SANITIZER_FREEBSD
|
||||
#if SANITIZER_WORDSIZE == 64
|
||||
#define ElfW64_Dyn Elf_Dyn
|
||||
#define ElfW64_Sym Elf_Sym
|
||||
#else
|
||||
#define ElfW32_Dyn Elf_Dyn
|
||||
#define ElfW32_Sym Elf_Sym
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_flag_parser.h"
|
||||
#include "ubsan/ubsan_init.h"
|
||||
#include "ubsan/ubsan_flags.h"
|
||||
@ -154,15 +172,25 @@ void ShadowBuilder::Add(uptr begin, uptr end, uptr cfi_check) {
|
||||
*s = sv;
|
||||
}
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD
|
||||
void ShadowBuilder::Install() {
|
||||
MprotectReadOnly(shadow_, GetShadowSize());
|
||||
uptr main_shadow = GetShadow();
|
||||
if (main_shadow) {
|
||||
// Update.
|
||||
#if SANITIZER_LINUX
|
||||
void *res = mremap((void *)shadow_, GetShadowSize(), GetShadowSize(),
|
||||
MREMAP_MAYMOVE | MREMAP_FIXED, (void *)main_shadow);
|
||||
CHECK(res != MAP_FAILED);
|
||||
#elif SANITIZER_NETBSD
|
||||
void *res = mremap((void *)shadow_, GetShadowSize(), (void *)main_shadow,
|
||||
GetShadowSize(), MAP_FIXED);
|
||||
CHECK(res != MAP_FAILED);
|
||||
#else
|
||||
void *res = MmapFixedOrDie(shadow_, GetShadowSize());
|
||||
CHECK(res != MAP_FAILED);
|
||||
::memcpy(&shadow_, &main_shadow, GetShadowSize());
|
||||
#endif
|
||||
} else {
|
||||
// Initial setup.
|
||||
CHECK_EQ(kCfiShadowLimitsStorageSize, GetPageSizeCached());
|
||||
@ -183,17 +211,17 @@ void ShadowBuilder::Install() {
|
||||
// dlopen(RTLD_NOLOAD | RTLD_LAZY)
|
||||
// dlsym("__cfi_check").
|
||||
uptr find_cfi_check_in_dso(dl_phdr_info *info) {
|
||||
const ElfW(Dyn) *dynamic = nullptr;
|
||||
const Elf_Dyn *dynamic = nullptr;
|
||||
for (int i = 0; i < info->dlpi_phnum; ++i) {
|
||||
if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
|
||||
dynamic =
|
||||
(const ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
|
||||
(const Elf_Dyn *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dynamic) return 0;
|
||||
uptr strtab = 0, symtab = 0, strsz = 0;
|
||||
for (const ElfW(Dyn) *p = dynamic; p->d_tag != PT_NULL; ++p) {
|
||||
for (const Elf_Dyn *p = dynamic; p->d_tag != PT_NULL; ++p) {
|
||||
if (p->d_tag == DT_SYMTAB)
|
||||
symtab = p->d_un.d_ptr;
|
||||
else if (p->d_tag == DT_STRTAB)
|
||||
@ -227,7 +255,7 @@ uptr find_cfi_check_in_dso(dl_phdr_info *info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const ElfW(Sym) *p = (const ElfW(Sym) *)symtab; (ElfW(Addr))p < strtab;
|
||||
for (const Elf_Sym *p = (const Elf_Sym *)symtab; (Elf_Addr)p < strtab;
|
||||
++p) {
|
||||
// There is no reliable way to find the end of the symbol table. In
|
||||
// lld-produces files, there are other sections between symtab and strtab.
|
||||
|
@ -1,13 +1,11 @@
|
||||
[cfi-unrelated-cast]
|
||||
# The specification of std::get_temporary_buffer mandates a cast to
|
||||
# uninitialized T* (libstdc++, libc++, MSVC stdlib).
|
||||
# uninitialized T* (libstdc++, MSVC stdlib).
|
||||
fun:_ZSt20get_temporary_buffer*
|
||||
fun:_ZNSt3__120get_temporary_buffer*
|
||||
fun:*get_temporary_buffer@.*@std@@*
|
||||
|
||||
# STL address-of magic (libstdc++, libc++).
|
||||
# STL address-of magic (libstdc++).
|
||||
fun:*__addressof*
|
||||
fun:_ZNSt3__19addressof*
|
||||
|
||||
# Windows C++ stdlib headers that contain bad unrelated casts.
|
||||
src:*xmemory0
|
||||
|
@ -423,7 +423,7 @@ static void dfsan_fini() {
|
||||
static void dfsan_init(int argc, char **argv, char **envp) {
|
||||
InitializeFlags();
|
||||
|
||||
InitializePlatformEarly();
|
||||
::InitializePlatformEarly();
|
||||
|
||||
if (!MmapFixedNoReserve(ShadowAddr(), UnusedAddr() - ShadowAddr()))
|
||||
Die();
|
||||
|
@ -327,7 +327,7 @@ INTERCEPTOR(int, rmdir, char *path) {
|
||||
// Signal-related interceptors
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
#if SANITIZER_LINUX || SANITIZER_FREEBSD
|
||||
typedef void (*signal_handler_t)(int);
|
||||
INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) {
|
||||
void *ctx;
|
||||
@ -344,7 +344,7 @@ INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) {
|
||||
#define ESAN_MAYBE_INTERCEPT_SIGNAL
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
#if SANITIZER_LINUX || SANITIZER_FREEBSD
|
||||
DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact)
|
||||
INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
|
||||
@ -363,7 +363,11 @@ int real_sigaction(int signum, const void *act, void *oldact) {
|
||||
if (REAL(sigaction) == nullptr) {
|
||||
// With an instrumented allocator, this is called during interceptor init
|
||||
// and we need a raw syscall solution.
|
||||
#if SANITIZER_LINUX
|
||||
return internal_sigaction_syscall(signum, act, oldact);
|
||||
#else
|
||||
return internal_sigaction(signum, act, oldact);
|
||||
#endif
|
||||
}
|
||||
return REAL(sigaction)(signum, (const struct sigaction *)act,
|
||||
(struct sigaction *)oldact);
|
||||
@ -376,7 +380,7 @@ int real_sigaction(int signum, const void *act, void *oldact) {
|
||||
#define ESAN_MAYBE_INTERCEPT_SIGACTION
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
#if SANITIZER_LINUX || SANITIZER_FREEBSD
|
||||
INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set,
|
||||
__sanitizer_sigset_t *oldset) {
|
||||
void *ctx;
|
||||
|
@ -30,7 +30,7 @@ struct ApplicationRegion {
|
||||
bool ShadowMergedWithPrev;
|
||||
};
|
||||
|
||||
#if SANITIZER_LINUX && defined(__x86_64__)
|
||||
#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && defined(__x86_64__)
|
||||
// Linux x86_64
|
||||
//
|
||||
// Application memory falls into these 5 regions (ignoring the corner case
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_platform_limits_freebsd.h"
|
||||
#include "sanitizer_common/sanitizer_platform_limits_posix.h"
|
||||
|
||||
namespace __esan {
|
||||
|
35
contrib/compiler-rt/lib/esan/esan_sideline_bsd.cpp
Normal file
35
contrib/compiler-rt/lib/esan/esan_sideline_bsd.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
//===-- esan_sideline_bsd.cpp -----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of EfficiencySanitizer, a family of performance tuners.
|
||||
//
|
||||
// Support for a separate or "sideline" tool thread on FreeBSD.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#if SANITIZER_FREEBSD
|
||||
|
||||
#include "esan_sideline.h"
|
||||
|
||||
namespace __esan {
|
||||
|
||||
static SidelineThread *TheThread;
|
||||
|
||||
bool SidelineThread::launchThread(SidelineFunc takeSample, void *Arg,
|
||||
u32 FreqMilliSec) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SidelineThread::joinThread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace __esan
|
||||
|
||||
#endif // SANITIZER_FREEBSD
|
36
contrib/compiler-rt/lib/fuzzer/FuzzerBuiltins.h
Normal file
36
contrib/compiler-rt/lib/fuzzer/FuzzerBuiltins.h
Normal file
@ -0,0 +1,36 @@
|
||||
//===- FuzzerBuiltins.h - Internal header for builtins ----------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Wrapper functions and marcos around builtin functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_BUILTINS_H
|
||||
#define LLVM_FUZZER_BUILTINS_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
#if !LIBFUZZER_MSVC
|
||||
#include <cstdint>
|
||||
|
||||
#define GET_CALLER_PC() __builtin_return_address(0)
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
inline uint8_t Bswap(uint8_t x) { return x; }
|
||||
inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
|
||||
inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
|
||||
inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
|
||||
|
||||
inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); }
|
||||
inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); }
|
||||
inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); }
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // !LIBFUZZER_MSVC
|
||||
#endif // LLVM_FUZZER_BUILTINS_H
|
59
contrib/compiler-rt/lib/fuzzer/FuzzerBuiltinsMsvc.h
Normal file
59
contrib/compiler-rt/lib/fuzzer/FuzzerBuiltinsMsvc.h
Normal file
@ -0,0 +1,59 @@
|
||||
//===- FuzzerBuiltinsMSVC.h - Internal header for builtins ------*- C++ -* ===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Wrapper functions and marcos that use intrinsics instead of builtin functions
|
||||
// which cannot be compiled by MSVC.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_FUZZER_BUILTINS_MSVC_H
|
||||
#define LLVM_FUZZER_BUILTINS_MSVC_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
#if LIBFUZZER_MSVC
|
||||
#if !defined(_M_ARM) && !defined(_M_X64)
|
||||
#error "_BitScanReverse64 unavailable on this platform so MSVC is unsupported."
|
||||
#endif
|
||||
#include <intrin.h>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
// __builtin_return_address() cannot be compiled with MSVC. Use the equivalent
|
||||
// from <intrin.h>
|
||||
#define GET_CALLER_PC() reinterpret_cast<uintptr_t>(_ReturnAddress())
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
inline uint8_t Bswap(uint8_t x) { return x; }
|
||||
// Use alternatives to __builtin functions from <stdlib.h> and <intrin.h> on
|
||||
// Windows since the builtins are not supported by MSVC.
|
||||
inline uint16_t Bswap(uint16_t x) { return _byteswap_ushort(x); }
|
||||
inline uint32_t Bswap(uint32_t x) { return _byteswap_ulong(x); }
|
||||
inline uint64_t Bswap(uint64_t x) { return _byteswap_uint64(x); }
|
||||
|
||||
// The functions below were mostly copied from
|
||||
// compiler-rt/lib/builtins/int_lib.h which defines the __builtin functions used
|
||||
// outside of Windows.
|
||||
inline uint32_t Clzll(uint64_t X) {
|
||||
unsigned long LeadZeroIdx = 0;
|
||||
if (_BitScanReverse64(&LeadZeroIdx, X)) return 63 - LeadZeroIdx;
|
||||
return 64;
|
||||
}
|
||||
|
||||
inline uint32_t Clz(uint32_t X) {
|
||||
unsigned long LeadZeroIdx = 0;
|
||||
if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx;
|
||||
return 32;
|
||||
}
|
||||
|
||||
inline int Popcountll(unsigned long long X) { return __popcnt64(X); }
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZER_MSVC
|
||||
#endif // LLVM_FUZZER_BUILTINS_MSVC_H
|
@ -81,7 +81,7 @@ public:
|
||||
}
|
||||
|
||||
// Like hasArgument, but checks for "-[Flag]=...".
|
||||
bool hasFlag(const std::string &Flag) {
|
||||
bool hasFlag(const std::string &Flag) const {
|
||||
std::string Arg("-" + Flag + "=");
|
||||
auto IsMatch = [&](const std::string &Other) {
|
||||
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
|
||||
@ -92,7 +92,7 @@ public:
|
||||
// Returns the value of the first instance of a given flag, or an empty string
|
||||
// if the flag isn't present. Ignores any occurrences after
|
||||
// "-ignore_remaining_args=1", if present.
|
||||
std::string getFlagValue(const std::string &Flag) {
|
||||
std::string getFlagValue(const std::string &Flag) const {
|
||||
std::string Arg("-" + Flag + "=");
|
||||
auto IsMatch = [&](const std::string &Other) {
|
||||
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
|
||||
|
@ -238,12 +238,6 @@ class InputCorpus {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsFeatureNew(size_t Idx, uint32_t NewSize, bool Shrink) {
|
||||
assert(NewSize);
|
||||
uint32_t OldSize = GetFeature(Idx % kFeatureSetSize);
|
||||
return OldSize == 0 || (Shrink && OldSize > NewSize);
|
||||
}
|
||||
|
||||
size_t NumFeatures() const { return NumAddedFeatures; }
|
||||
size_t NumFeatureUpdates() const { return NumUpdatedFeatures; }
|
||||
|
||||
|
@ -82,6 +82,13 @@
|
||||
#error "Support for your platform has not been implemented"
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
// MSVC compiler is being used.
|
||||
#define LIBFUZZER_MSVC 1
|
||||
#else
|
||||
#define LIBFUZZER_MSVC 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0
|
||||
#endif
|
||||
@ -129,8 +136,15 @@
|
||||
|
||||
#if LIBFUZZER_WINDOWS
|
||||
#define ATTRIBUTE_INTERFACE __declspec(dllexport)
|
||||
// This is used for __sancov_lowest_stack which is needed for
|
||||
// -fsanitize-coverage=stack-depth. That feature is not yet available on
|
||||
// Windows, so make the symbol static to avoid linking errors.
|
||||
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
|
||||
__attribute__((tls_model("initial-exec"))) thread_local static
|
||||
#else
|
||||
#define ATTRIBUTE_INTERFACE __attribute__((visibility("default")))
|
||||
#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
|
||||
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local
|
||||
#endif
|
||||
|
||||
namespace fuzzer {
|
||||
@ -176,11 +190,6 @@ typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
|
||||
|
||||
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
|
||||
|
||||
inline uint8_t Bswap(uint8_t x) { return x; }
|
||||
inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
|
||||
inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
|
||||
inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
|
||||
|
||||
uint8_t *ExtraCountersBegin();
|
||||
uint8_t *ExtraCountersEnd();
|
||||
void ClearExtraCounters();
|
||||
|
@ -615,13 +615,8 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
|
||||
Options.PrintNewCovPcs = Flags.print_pcs;
|
||||
Options.PrintNewCovFuncs = Flags.print_funcs;
|
||||
Options.PrintFinalStats = Flags.print_final_stats;
|
||||
Options.PrintMutationStats = Flags.print_mutation_stats;
|
||||
Options.PrintCorpusStats = Flags.print_corpus_stats;
|
||||
Options.PrintCoverage = Flags.print_coverage;
|
||||
Options.PrintUnstableStats = Flags.print_unstable_stats;
|
||||
if (Flags.handle_unstable == TracePC::MinUnstable ||
|
||||
Flags.handle_unstable == TracePC::ZeroUnstable)
|
||||
Options.HandleUnstable = Flags.handle_unstable;
|
||||
Options.DumpCoverage = Flags.dump_coverage;
|
||||
if (Flags.exit_on_src_pos)
|
||||
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
|
||||
|
@ -1,62 +0,0 @@
|
||||
//===- FuzzerExtFunctionsDlsymWin.cpp - Interface to external functions ---===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation using dynamic loading for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
#include "Windows.h"
|
||||
|
||||
// This must be included after Windows.h.
|
||||
#include "Psapi.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
HMODULE Modules[1024];
|
||||
DWORD BytesNeeded;
|
||||
HANDLE CurrentProcess = GetCurrentProcess();
|
||||
|
||||
if (!EnumProcessModules(CurrentProcess, Modules, sizeof(Modules),
|
||||
&BytesNeeded)) {
|
||||
Printf("EnumProcessModules failed (error: %d).\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (sizeof(Modules) < BytesNeeded) {
|
||||
Printf("Error: the array is not big enough to hold all loaded modules.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < (BytesNeeded / sizeof(HMODULE)); i++)
|
||||
{
|
||||
FARPROC Fn;
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
if (this->NAME == nullptr) { \
|
||||
Fn = GetProcAddress(Modules[i], #NAME); \
|
||||
if (Fn == nullptr) \
|
||||
Fn = GetProcAddress(Modules[i], #NAME "__dll"); \
|
||||
this->NAME = (decltype(ExternalFunctions::NAME)) Fn; \
|
||||
}
|
||||
#include "FuzzerExtFunctions.def"
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
if (this->NAME == nullptr && WARN) \
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", #NAME);
|
||||
#include "FuzzerExtFunctions.def"
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
@ -22,7 +22,7 @@
|
||||
extern "C" {
|
||||
// Declare these symbols as weak to allow them to be optionally defined.
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
__attribute__((weak)) RETURN_TYPE NAME FUNC_SIG
|
||||
__attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
|
@ -1,56 +0,0 @@
|
||||
//===- FuzzerExtFunctionsWeakAlias.cpp - Interface to external functions --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation using weak aliases. Works for Windows.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
extern "C" {
|
||||
// Declare these symbols as weak to allow them to be optionally defined.
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
RETURN_TYPE NAME##Def FUNC_SIG { \
|
||||
Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
|
||||
exit(1); \
|
||||
} \
|
||||
RETURN_TYPE NAME FUNC_SIG __attribute__((weak, alias(#NAME "Def")));
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
|
||||
if (Fun == FunDef) {
|
||||
if (WarnIfMissing)
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
|
||||
return nullptr;
|
||||
}
|
||||
return Fun;
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
83
contrib/compiler-rt/lib/fuzzer/FuzzerExtFunctionsWindows.cpp
Normal file
83
contrib/compiler-rt/lib/fuzzer/FuzzerExtFunctionsWindows.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
//=== FuzzerExtWindows.cpp - Interface to external functions --------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation of FuzzerExtFunctions for Windows. Uses alternatename when
|
||||
// compiled with MSVC. Uses weak aliases when compiled with clang. Unfortunately
|
||||
// the method each compiler supports is not supported by the other.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "FuzzerDefs.h"
|
||||
#if LIBFUZZER_WINDOWS
|
||||
|
||||
#include "FuzzerExtFunctions.h"
|
||||
#include "FuzzerIO.h"
|
||||
|
||||
using namespace fuzzer;
|
||||
|
||||
// Intermediate macro to ensure the parameter is expanded before stringified.
|
||||
#define STRINGIFY_(A) #A
|
||||
#define STRINGIFY(A) STRINGIFY_(A)
|
||||
|
||||
#if LIBFUZZER_MSVC
|
||||
// Copied from compiler-rt/lib/sanitizer_common/sanitizer_win_defs.h
|
||||
#if defined(_M_IX86) || defined(__i386__)
|
||||
#define WIN_SYM_PREFIX "_"
|
||||
#else
|
||||
#define WIN_SYM_PREFIX
|
||||
#endif
|
||||
|
||||
// Declare external functions as having alternativenames, so that we can
|
||||
// determine if they are not defined.
|
||||
#define EXTERNAL_FUNC(Name, Default) \
|
||||
__pragma(comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \
|
||||
Name) "=" WIN_SYM_PREFIX STRINGIFY(Default)))
|
||||
#else
|
||||
// Declare external functions as weak to allow them to default to a specified
|
||||
// function if not defined explicitly. We must use weak symbols because clang's
|
||||
// support for alternatename is not 100%, see
|
||||
// https://bugs.llvm.org/show_bug.cgi?id=40218 for more details.
|
||||
#define EXTERNAL_FUNC(Name, Default) \
|
||||
__attribute__((weak, alias(STRINGIFY(Default))))
|
||||
#endif // LIBFUZZER_MSVC
|
||||
|
||||
extern "C" {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
RETURN_TYPE NAME##Def FUNC_SIG { \
|
||||
Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
|
||||
exit(1); \
|
||||
} \
|
||||
EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG;
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
|
||||
if (Fun == FunDef) {
|
||||
if (WarnIfMissing)
|
||||
Printf("WARNING: Failed to find function \"%s\".\n", FnName);
|
||||
return nullptr;
|
||||
}
|
||||
return Fun;
|
||||
}
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
ExternalFunctions::ExternalFunctions() {
|
||||
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
|
||||
this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
|
||||
|
||||
#include "FuzzerExtFunctions.def"
|
||||
|
||||
#undef EXT_FUNC
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
#endif // LIBFUZZER_WINDOWS
|
@ -17,7 +17,7 @@ FUZZER_FLAG_INT(runs, -1,
|
||||
FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. "
|
||||
"If 0, libFuzzer tries to guess a good value based on the corpus "
|
||||
"and reports it. ")
|
||||
FUZZER_FLAG_INT(len_control, 1000, "Try generating small inputs first, "
|
||||
FUZZER_FLAG_INT(len_control, 100, "Try generating small inputs first, "
|
||||
"then try larger inputs over time. Specifies the rate at which the length "
|
||||
"limit is increased (smaller == faster). If 0, immediately try inputs with "
|
||||
"size up to max_len.")
|
||||
@ -110,15 +110,6 @@ FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text"
|
||||
FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated."
|
||||
" If 1, dump coverage information as a"
|
||||
" .sancov file at exit.")
|
||||
FUZZER_FLAG_INT(handle_unstable, 0, "Experimental."
|
||||
" Executes every input 3 times in total if a unique feature"
|
||||
" is found during the first execution."
|
||||
" If 1, we only use the minimum hit count from the 3 runs"
|
||||
" to determine whether an input is interesting."
|
||||
" If 2, we disregard edges that are found unstable for"
|
||||
" feature collection.")
|
||||
FUZZER_FLAG_INT(print_unstable_stats, 0, "Experimental."
|
||||
" If 1, print unstable statistics at exit.")
|
||||
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
|
||||
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.")
|
||||
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
|
||||
@ -162,4 +153,3 @@ FUZZER_DEPRECATED_FLAG(use_equivalence_server)
|
||||
FUZZER_FLAG_INT(analyze_dict, 0, "Experimental")
|
||||
FUZZER_DEPRECATED_FLAG(use_clang_coverage)
|
||||
FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace")
|
||||
FUZZER_FLAG_INT(print_mutation_stats, 0, "Experimental")
|
||||
|
@ -31,7 +31,7 @@ long GetEpoch(const std::string &Path) {
|
||||
}
|
||||
|
||||
Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
|
||||
std::ifstream T(Path);
|
||||
std::ifstream T(Path, std::ios::binary);
|
||||
if (ExitOnError && !T) {
|
||||
Printf("No such directory: %s; exiting\n", Path.c_str());
|
||||
exit(1);
|
||||
@ -51,7 +51,7 @@ Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
|
||||
}
|
||||
|
||||
std::string FileToString(const std::string &Path) {
|
||||
std::ifstream T(Path);
|
||||
std::ifstream T(Path, std::ios::binary);
|
||||
return std::string((std::istreambuf_iterator<char>(T)),
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
@ -100,14 +100,6 @@ std::string DirPlusFile(const std::string &DirPath,
|
||||
return DirPath + GetSeparator() + FileName;
|
||||
}
|
||||
|
||||
std::string Basename(const std::string &Path, char Separator) {
|
||||
size_t Pos = Path.rfind(Separator);
|
||||
if (Pos == std::string::npos)
|
||||
return Path;
|
||||
assert(Pos < Path.size());
|
||||
return Path.substr(Pos + 1);
|
||||
}
|
||||
|
||||
void DupAndCloseStderr() {
|
||||
int OutputFd = DuplicateFile(2);
|
||||
if (OutputFd > 0) {
|
||||
|
@ -68,7 +68,7 @@ void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
|
||||
|
||||
char GetSeparator();
|
||||
// Similar to the basename utility: returns the file name w/o the dir prefix.
|
||||
std::string Basename(const std::string &Path, char Separator = GetSeparator());
|
||||
std::string Basename(const std::string &Path);
|
||||
|
||||
FILE* OpenFile(int Fd, const char *Mode);
|
||||
|
||||
|
@ -46,6 +46,13 @@ size_t FileSize(const std::string &Path) {
|
||||
return St.st_size;
|
||||
}
|
||||
|
||||
std::string Basename(const std::string &Path) {
|
||||
size_t Pos = Path.rfind(GetSeparator());
|
||||
if (Pos == std::string::npos) return Path;
|
||||
assert(Pos < Path.size());
|
||||
return Path.substr(Pos + 1);
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
Vector<std::string> *V, bool TopDir) {
|
||||
auto E = GetEpoch(Dir);
|
||||
|
@ -72,6 +72,26 @@ bool IsFile(const std::string &Path) {
|
||||
return IsFile(Path, Att);
|
||||
}
|
||||
|
||||
std::string Basename(const std::string &Path) {
|
||||
size_t Pos = Path.find_last_of("/\\");
|
||||
if (Pos == std::string::npos) return Path;
|
||||
assert(Pos < Path.size());
|
||||
return Path.substr(Pos + 1);
|
||||
}
|
||||
|
||||
size_t FileSize(const std::string &Path) {
|
||||
WIN32_FILE_ATTRIBUTE_DATA attr;
|
||||
if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) {
|
||||
Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
|
||||
Path.c_str(), GetLastError());
|
||||
return 0;
|
||||
}
|
||||
ULARGE_INTEGER size;
|
||||
size.HighPart = attr.nFileSizeHigh;
|
||||
size.LowPart = attr.nFileSizeLow;
|
||||
return size.QuadPart;
|
||||
}
|
||||
|
||||
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
Vector<std::string> *V, bool TopDir) {
|
||||
auto E = GetEpoch(Dir);
|
||||
@ -91,7 +111,7 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
|
||||
{
|
||||
if (GetLastError() == ERROR_FILE_NOT_FOUND)
|
||||
return;
|
||||
Printf("No such directory: %s; exiting\n", Dir.c_str());
|
||||
Printf("No such file or directory: %s; exiting\n", Dir.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,6 @@ public:
|
||||
static void StaticGracefulExitCallback();
|
||||
|
||||
void ExecuteCallback(const uint8_t *Data, size_t Size);
|
||||
void CheckForUnstableCounters(const uint8_t *Data, size_t Size);
|
||||
bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
|
||||
InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr);
|
||||
|
||||
|
@ -275,7 +275,8 @@ NO_SANITIZE_MEMORY
|
||||
void Fuzzer::AlarmCallback() {
|
||||
assert(Options.UnitTimeoutSec > 0);
|
||||
// In Windows Alarm callback is executed by a different thread.
|
||||
#if !LIBFUZZER_WINDOWS
|
||||
// NetBSD's current behavior needs this change too.
|
||||
#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD
|
||||
if (!InFuzzingThread())
|
||||
return;
|
||||
#endif
|
||||
@ -354,13 +355,10 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
|
||||
void Fuzzer::PrintFinalStats() {
|
||||
if (Options.PrintCoverage)
|
||||
TPC.PrintCoverage();
|
||||
if (Options.PrintUnstableStats)
|
||||
TPC.PrintUnstableStats();
|
||||
if (Options.DumpCoverage)
|
||||
TPC.DumpCoverage();
|
||||
if (Options.PrintCorpusStats)
|
||||
Corpus.PrintStats();
|
||||
if (Options.PrintMutationStats) MD.PrintMutationStats();
|
||||
if (!Options.PrintFinalStats)
|
||||
return;
|
||||
size_t ExecPerSec = execPerSec();
|
||||
@ -449,34 +447,6 @@ void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {
|
||||
}
|
||||
}
|
||||
|
||||
void Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size) {
|
||||
auto CBSetupAndRun = [&]() {
|
||||
ScopedEnableMsanInterceptorChecks S;
|
||||
UnitStartTime = system_clock::now();
|
||||
TPC.ResetMaps();
|
||||
RunningUserCallback = true;
|
||||
CB(Data, Size);
|
||||
RunningUserCallback = false;
|
||||
UnitStopTime = system_clock::now();
|
||||
};
|
||||
|
||||
// Copy original run counters into our unstable counters
|
||||
TPC.InitializeUnstableCounters();
|
||||
|
||||
// First Rerun
|
||||
CBSetupAndRun();
|
||||
TPC.UpdateUnstableCounters(Options.HandleUnstable);
|
||||
|
||||
// Second Rerun
|
||||
CBSetupAndRun();
|
||||
TPC.UpdateUnstableCounters(Options.HandleUnstable);
|
||||
|
||||
// Move minimum hit counts back to ModuleInline8bitCounters
|
||||
if (Options.HandleUnstable == TracePC::MinUnstable ||
|
||||
Options.HandleUnstable == TracePC::ZeroUnstable)
|
||||
TPC.ApplyUnstableCounters();
|
||||
}
|
||||
|
||||
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
|
||||
InputInfo *II, bool *FoundUniqFeatures) {
|
||||
if (!Size)
|
||||
@ -487,17 +457,6 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
|
||||
UniqFeatureSetTmp.clear();
|
||||
size_t FoundUniqFeaturesOfII = 0;
|
||||
size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
|
||||
bool NewFeaturesUnstable = false;
|
||||
|
||||
if (Options.HandleUnstable || Options.PrintUnstableStats) {
|
||||
TPC.CollectFeatures([&](size_t Feature) {
|
||||
if (Corpus.IsFeatureNew(Feature, Size, Options.Shrink))
|
||||
NewFeaturesUnstable = true;
|
||||
});
|
||||
if (NewFeaturesUnstable)
|
||||
CheckForUnstableCounters(Data, Size);
|
||||
}
|
||||
|
||||
TPC.CollectFeatures([&](size_t Feature) {
|
||||
if (Corpus.AddFeature(Feature, Size, Options.Shrink))
|
||||
UniqFeatureSetTmp.push_back(Feature);
|
||||
@ -506,12 +465,10 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
|
||||
II->UniqFeatureSet.end(), Feature))
|
||||
FoundUniqFeaturesOfII++;
|
||||
});
|
||||
|
||||
if (FoundUniqFeatures)
|
||||
*FoundUniqFeatures = FoundUniqFeaturesOfII;
|
||||
PrintPulseAndReportSlowInput(Data, Size);
|
||||
size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;
|
||||
|
||||
if (NumNewFeatures) {
|
||||
TPC.UpdateObservedPCs();
|
||||
Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
|
||||
|
@ -30,36 +30,34 @@ MutationDispatcher::MutationDispatcher(Random &Rand,
|
||||
DefaultMutators.insert(
|
||||
DefaultMutators.begin(),
|
||||
{
|
||||
{&MutationDispatcher::Mutate_EraseBytes, "EraseBytes", 0, 0},
|
||||
{&MutationDispatcher::Mutate_InsertByte, "InsertByte", 0, 0},
|
||||
{&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
|
||||
{&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
|
||||
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
|
||||
"InsertRepeatedBytes", 0, 0},
|
||||
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte", 0, 0},
|
||||
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit", 0, 0},
|
||||
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes", 0, 0},
|
||||
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt", 0,
|
||||
0},
|
||||
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt", 0,
|
||||
0},
|
||||
{&MutationDispatcher::Mutate_CopyPart, "CopyPart", 0, 0},
|
||||
{&MutationDispatcher::Mutate_CrossOver, "CrossOver", 0, 0},
|
||||
"InsertRepeatedBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
|
||||
{&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
|
||||
{&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
|
||||
{&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
|
||||
{&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
|
||||
{&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
|
||||
{&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
|
||||
"ManualDict", 0, 0},
|
||||
"ManualDict"},
|
||||
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
|
||||
"PersAutoDict", 0, 0},
|
||||
"PersAutoDict"},
|
||||
});
|
||||
if(Options.UseCmp)
|
||||
DefaultMutators.push_back(
|
||||
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP", 0, 0});
|
||||
{&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
|
||||
|
||||
if (EF->LLVMFuzzerCustomMutator)
|
||||
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom", 0, 0});
|
||||
Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
|
||||
else
|
||||
Mutators = DefaultMutators;
|
||||
|
||||
if (EF->LLVMFuzzerCustomCrossOver)
|
||||
Mutators.push_back(
|
||||
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver", 0, 0});
|
||||
{&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
|
||||
}
|
||||
|
||||
static char RandCh(Random &Rand) {
|
||||
@ -466,7 +464,6 @@ void MutationDispatcher::RecordSuccessfulMutationSequence() {
|
||||
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
|
||||
PersistentAutoDictionary.push_back({DE->GetW(), 1});
|
||||
}
|
||||
RecordUsefulMutations();
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintRecommendedDictionary() {
|
||||
@ -487,7 +484,8 @@ void MutationDispatcher::PrintRecommendedDictionary() {
|
||||
|
||||
void MutationDispatcher::PrintMutationSequence() {
|
||||
Printf("MS: %zd ", CurrentMutatorSequence.size());
|
||||
for (auto M : CurrentMutatorSequence) Printf("%s-", M->Name);
|
||||
for (auto M : CurrentMutatorSequence)
|
||||
Printf("%s-", M.Name);
|
||||
if (!CurrentDictionaryEntrySequence.empty()) {
|
||||
Printf(" DE: ");
|
||||
for (auto DE : CurrentDictionaryEntrySequence) {
|
||||
@ -515,13 +513,12 @@ size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
|
||||
// in which case they will return 0.
|
||||
// Try several times before returning un-mutated data.
|
||||
for (int Iter = 0; Iter < 100; Iter++) {
|
||||
auto M = &Mutators[Rand(Mutators.size())];
|
||||
size_t NewSize = (this->*(M->Fn))(Data, Size, MaxSize);
|
||||
auto M = Mutators[Rand(Mutators.size())];
|
||||
size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
|
||||
if (NewSize && NewSize <= MaxSize) {
|
||||
if (Options.OnlyASCII)
|
||||
ToASCII(Data, NewSize);
|
||||
CurrentMutatorSequence.push_back(M);
|
||||
M->TotalCount++;
|
||||
return NewSize;
|
||||
}
|
||||
}
|
||||
@ -562,21 +559,4 @@ void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
|
||||
{W, std::numeric_limits<size_t>::max()});
|
||||
}
|
||||
|
||||
void MutationDispatcher::RecordUsefulMutations() {
|
||||
for (auto M : CurrentMutatorSequence) M->UsefulCount++;
|
||||
}
|
||||
|
||||
void MutationDispatcher::PrintMutationStats() {
|
||||
Printf("\nstat::mutation_usefulness: ");
|
||||
for (size_t i = 0; i < Mutators.size(); i++) {
|
||||
double UsefulPercentage =
|
||||
Mutators[i].TotalCount
|
||||
? (100.0 * Mutators[i].UsefulCount) / Mutators[i].TotalCount
|
||||
: 0;
|
||||
Printf("%.3f", UsefulPercentage);
|
||||
if (i < Mutators.size() - 1) Printf(",");
|
||||
}
|
||||
Printf("\n");
|
||||
}
|
||||
|
||||
} // namespace fuzzer
|
||||
|
@ -93,16 +93,10 @@ public:
|
||||
|
||||
Random &GetRand() { return Rand; }
|
||||
|
||||
void PrintMutationStats();
|
||||
|
||||
void RecordUsefulMutations();
|
||||
|
||||
private:
|
||||
struct Mutator {
|
||||
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
|
||||
const char *Name;
|
||||
uint64_t UsefulCount;
|
||||
uint64_t TotalCount;
|
||||
};
|
||||
|
||||
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
|
||||
@ -141,7 +135,6 @@ public:
|
||||
Dictionary PersistentAutoDictionary;
|
||||
|
||||
Vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
|
||||
Vector<Mutator *> CurrentMutatorSequence;
|
||||
|
||||
static const size_t kCmpDictionaryEntriesDequeSize = 16;
|
||||
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
|
||||
@ -156,6 +149,7 @@ public:
|
||||
|
||||
Vector<Mutator> Mutators;
|
||||
Vector<Mutator> DefaultMutators;
|
||||
Vector<Mutator> CurrentMutatorSequence;
|
||||
};
|
||||
|
||||
} // namespace fuzzer
|
||||
|
@ -52,11 +52,8 @@ struct FuzzingOptions {
|
||||
bool PrintNewCovPcs = false;
|
||||
int PrintNewCovFuncs = 0;
|
||||
bool PrintFinalStats = false;
|
||||
bool PrintMutationStats = false;
|
||||
bool PrintCorpusStats = false;
|
||||
bool PrintCoverage = false;
|
||||
bool PrintUnstableStats = false;
|
||||
int HandleUnstable = 0;
|
||||
bool DumpCoverage = false;
|
||||
bool DetectLeaks = true;
|
||||
int PurgeAllocatorIntervalSec = 1;
|
||||
|
@ -13,6 +13,8 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "FuzzerTracePC.h"
|
||||
#include "FuzzerBuiltins.h"
|
||||
#include "FuzzerBuiltinsMsvc.h"
|
||||
#include "FuzzerCorpus.h"
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerDictionary.h"
|
||||
@ -32,8 +34,7 @@ ATTRIBUTE_INTERFACE
|
||||
uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs];
|
||||
|
||||
// Used by -fsanitize-coverage=stack-depth to track stack depth
|
||||
ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec")))
|
||||
thread_local uintptr_t __sancov_lowest_stack;
|
||||
ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack;
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
@ -57,49 +58,6 @@ size_t TracePC::GetTotalPCCoverage() {
|
||||
return Res;
|
||||
}
|
||||
|
||||
template<class CallBack>
|
||||
void TracePC::IterateInline8bitCounters(CallBack CB) const {
|
||||
if (NumInline8bitCounters && NumInline8bitCounters == NumPCsInPCTables) {
|
||||
size_t CounterIdx = 0;
|
||||
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
|
||||
uint8_t *Beg = ModuleCounters[i].Start;
|
||||
size_t Size = ModuleCounters[i].Stop - Beg;
|
||||
assert(Size == (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
|
||||
for (size_t j = 0; j < Size; j++, CounterIdx++)
|
||||
CB(i, j, CounterIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes unstable counters by copying Inline8bitCounters to unstable
|
||||
// counters.
|
||||
void TracePC::InitializeUnstableCounters() {
|
||||
IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
|
||||
UnstableCounters[UnstableIdx].Counter = ModuleCounters[i].Start[j];
|
||||
});
|
||||
}
|
||||
|
||||
// Compares the current counters with counters from previous runs
|
||||
// and records differences as unstable edges.
|
||||
void TracePC::UpdateUnstableCounters(int UnstableMode) {
|
||||
IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
|
||||
if (ModuleCounters[i].Start[j] != UnstableCounters[UnstableIdx].Counter) {
|
||||
UnstableCounters[UnstableIdx].IsUnstable = true;
|
||||
if (UnstableMode == ZeroUnstable)
|
||||
UnstableCounters[UnstableIdx].Counter = 0;
|
||||
else if (UnstableMode == MinUnstable)
|
||||
UnstableCounters[UnstableIdx].Counter = std::min(
|
||||
ModuleCounters[i].Start[j], UnstableCounters[UnstableIdx].Counter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Moves the minimum hit counts to ModuleCounters.
|
||||
void TracePC::ApplyUnstableCounters() {
|
||||
IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
|
||||
ModuleCounters[i].Start[j] = UnstableCounters[UnstableIdx].Counter;
|
||||
});
|
||||
}
|
||||
|
||||
void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) {
|
||||
if (Start == Stop) return;
|
||||
@ -185,11 +143,42 @@ void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
|
||||
ValueProfileMap.AddValueModPrime(Idx);
|
||||
}
|
||||
|
||||
/// \return the address of the previous instruction.
|
||||
/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.h`
|
||||
inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
|
||||
#if defined(__arm__)
|
||||
// T32 (Thumb) branch instructions might be 16 or 32 bit long,
|
||||
// so we return (pc-2) in that case in order to be safe.
|
||||
// For A32 mode we return (pc-4) because all instructions are 32 bit long.
|
||||
return (PC - 3) & (~1);
|
||||
#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__)
|
||||
// PCs are always 4 byte aligned.
|
||||
return PC - 4;
|
||||
#elif defined(__sparc__) || defined(__mips__)
|
||||
return PC - 8;
|
||||
#else
|
||||
return PC - 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// \return the address of the next instruction.
|
||||
/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cc`
|
||||
inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) {
|
||||
#if defined(__mips__)
|
||||
return PC + 8;
|
||||
#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \
|
||||
defined(__aarch64__)
|
||||
return PC + 4;
|
||||
#else
|
||||
return PC + 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void TracePC::UpdateObservedPCs() {
|
||||
Vector<uintptr_t> CoveredFuncs;
|
||||
auto ObservePC = [&](uintptr_t PC) {
|
||||
if (ObservedPCs.insert(PC).second && DoPrintNewPCs) {
|
||||
PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", PC + 1);
|
||||
PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", GetNextInstructionPc(PC));
|
||||
Printf("\n");
|
||||
}
|
||||
};
|
||||
@ -203,10 +192,15 @@ void TracePC::UpdateObservedPCs() {
|
||||
|
||||
if (NumPCsInPCTables) {
|
||||
if (NumInline8bitCounters == NumPCsInPCTables) {
|
||||
IterateInline8bitCounters([&](int i, int j, int CounterIdx) {
|
||||
if (ModuleCounters[i].Start[j])
|
||||
Observe(ModulePCTable[i].Start[j]);
|
||||
});
|
||||
for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
|
||||
uint8_t *Beg = ModuleCounters[i].Start;
|
||||
size_t Size = ModuleCounters[i].Stop - Beg;
|
||||
assert(Size ==
|
||||
(size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
|
||||
for (size_t j = 0; j < Size; j++)
|
||||
if (Beg[j])
|
||||
Observe(ModulePCTable[i].Start[j]);
|
||||
}
|
||||
} else if (NumGuards == NumPCsInPCTables) {
|
||||
size_t GuardIdx = 1;
|
||||
for (size_t i = 0; i < NumModules; i++) {
|
||||
@ -224,22 +218,11 @@ void TracePC::UpdateObservedPCs() {
|
||||
for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N;
|
||||
i++) {
|
||||
Printf("\tNEW_FUNC[%zd/%zd]: ", i + 1, CoveredFuncs.size());
|
||||
PrintPC("%p %F %L", "%p", CoveredFuncs[i] + 1);
|
||||
PrintPC("%p %F %L", "%p", GetNextInstructionPc(CoveredFuncs[i]));
|
||||
Printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
|
||||
// TODO: this implementation is x86 only.
|
||||
// see sanitizer_common GetPreviousInstructionPc for full implementation.
|
||||
return PC - 1;
|
||||
}
|
||||
|
||||
inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) {
|
||||
// TODO: this implementation is x86 only.
|
||||
// see sanitizer_common GetPreviousInstructionPc for full implementation.
|
||||
return PC + 1;
|
||||
}
|
||||
|
||||
static std::string GetModuleName(uintptr_t PC) {
|
||||
char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
|
||||
@ -349,15 +332,6 @@ void TracePC::DumpCoverage() {
|
||||
}
|
||||
}
|
||||
|
||||
void TracePC::PrintUnstableStats() {
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < NumInline8bitCounters; i++)
|
||||
if (UnstableCounters[i].IsUnstable)
|
||||
count++;
|
||||
Printf("stat::stability_rate: %.2f\n",
|
||||
100 - static_cast<float>(count * 100) / NumInline8bitCounters);
|
||||
}
|
||||
|
||||
// Value profile.
|
||||
// We keep track of various values that affect control flow.
|
||||
// These values are inserted into a bit-set-based hash map.
|
||||
@ -401,20 +375,14 @@ ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) {
|
||||
uint64_t ArgXor = Arg1 ^ Arg2;
|
||||
uint64_t ArgDistance = __builtin_popcountll(ArgXor) + 1; // [1,65]
|
||||
uintptr_t Idx = ((PC & 4095) + 1) * ArgDistance;
|
||||
if (sizeof(T) == 4)
|
||||
TORC4.Insert(ArgXor, Arg1, Arg2);
|
||||
else if (sizeof(T) == 8)
|
||||
TORC8.Insert(ArgXor, Arg1, Arg2);
|
||||
// TODO: remove these flags and instead use all metrics at once.
|
||||
if (UseValueProfileMask & 1)
|
||||
ValueProfileMap.AddValue(Idx);
|
||||
if (UseValueProfileMask & 2)
|
||||
ValueProfileMap.AddValue(
|
||||
PC * 64 + (Arg1 == Arg2 ? 0 : __builtin_clzll(Arg1 - Arg2) + 1));
|
||||
if (UseValueProfileMask & 4) // alternative way to use the hamming distance
|
||||
ValueProfileMap.AddValue(PC * 64 + ArgDistance);
|
||||
uint64_t HammingDistance = Popcountll(ArgXor); // [0,64]
|
||||
uint64_t AbsoluteDistance = (Arg1 == Arg2 ? 0 : Clzll(Arg1 - Arg2) + 1);
|
||||
ValueProfileMap.AddValue(PC * 128 + HammingDistance);
|
||||
ValueProfileMap.AddValue(PC * 128 + 64 + AbsoluteDistance);
|
||||
}
|
||||
|
||||
static size_t InternalStrnlen(const char *S, size_t MaxLen) {
|
||||
@ -455,7 +423,7 @@ extern "C" {
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
uint32_t Idx = *Guard;
|
||||
__sancov_trace_pc_pcs[Idx] = PC;
|
||||
__sancov_trace_pc_guard_8bit_counters[Idx]++;
|
||||
@ -466,7 +434,7 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void __sanitizer_cov_trace_pc() {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
uintptr_t Idx = PC & (((uintptr_t)1 << fuzzer::TracePC::kTracePcBits) - 1);
|
||||
__sancov_trace_pc_pcs[Idx] = PC;
|
||||
__sancov_trace_pc_guard_8bit_counters[Idx]++;
|
||||
@ -491,7 +459,7 @@ void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
|
||||
ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCallerCallee(PC, Callee);
|
||||
}
|
||||
|
||||
@ -499,7 +467,7 @@ ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
@ -510,7 +478,7 @@ ATTRIBUTE_TARGET_POPCNT
|
||||
// the behaviour of __sanitizer_cov_trace_cmp[1248] ones. This, however,
|
||||
// should be changed later to make full use of instrumentation.
|
||||
void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
@ -518,7 +486,7 @@ ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
@ -526,7 +494,7 @@ ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
@ -534,7 +502,7 @@ ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
@ -542,7 +510,7 @@ ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
@ -550,7 +518,7 @@ ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
@ -558,7 +526,7 @@ ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
|
||||
}
|
||||
|
||||
@ -572,7 +540,7 @@ void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
|
||||
// Skip the most common and the most boring case.
|
||||
if (Vals[N - 1] < 256 && Val < 256)
|
||||
return;
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
size_t i;
|
||||
uint64_t Token = 0;
|
||||
for (i = 0; i < N; i++) {
|
||||
@ -593,7 +561,7 @@ ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_div4(uint32_t Val) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0);
|
||||
}
|
||||
|
||||
@ -601,7 +569,7 @@ ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_div8(uint64_t Val) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0);
|
||||
}
|
||||
|
||||
@ -609,7 +577,7 @@ ATTRIBUTE_INTERFACE
|
||||
ATTRIBUTE_NO_SANITIZE_ALL
|
||||
ATTRIBUTE_TARGET_POPCNT
|
||||
void __sanitizer_cov_trace_gep(uintptr_t Idx) {
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
|
||||
uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
|
||||
fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0);
|
||||
}
|
||||
|
||||
|
@ -74,11 +74,6 @@ class TracePC {
|
||||
// How many bits of PC are used from __sanitizer_cov_trace_pc.
|
||||
static const size_t kTracePcBits = 18;
|
||||
|
||||
enum HandleUnstableOptions {
|
||||
MinUnstable = 1,
|
||||
ZeroUnstable = 2,
|
||||
};
|
||||
|
||||
void HandleInit(uint32_t *Start, uint32_t *Stop);
|
||||
void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop);
|
||||
void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop);
|
||||
@ -109,7 +104,6 @@ class TracePC {
|
||||
|
||||
void PrintCoverage();
|
||||
void DumpCoverage();
|
||||
void PrintUnstableStats();
|
||||
|
||||
template<class CallBack>
|
||||
void IterateCoveredFunctions(CallBack CB);
|
||||
@ -142,18 +136,7 @@ class TracePC {
|
||||
void SetFocusFunction(const std::string &FuncName);
|
||||
bool ObservedFocusFunction();
|
||||
|
||||
void InitializeUnstableCounters();
|
||||
void UpdateUnstableCounters(int UnstableMode);
|
||||
void ApplyUnstableCounters();
|
||||
|
||||
private:
|
||||
struct UnstableEdge {
|
||||
uint8_t Counter;
|
||||
bool IsUnstable;
|
||||
};
|
||||
|
||||
UnstableEdge UnstableCounters[kNumPCs];
|
||||
|
||||
bool UseCounters = false;
|
||||
uint32_t UseValueProfileMask = false;
|
||||
bool DoPrintNewPCs = false;
|
||||
@ -185,9 +168,6 @@ private:
|
||||
Set<uintptr_t> ObservedPCs;
|
||||
std::unordered_map<uintptr_t, uintptr_t> ObservedFuncs; // PC => Counter.
|
||||
|
||||
template <class Callback>
|
||||
void IterateInline8bitCounters(Callback CB) const;
|
||||
|
||||
std::pair<size_t, size_t> FocusFunction = {-1, -1}; // Module and PC IDs.
|
||||
|
||||
ValueBitMap ValueProfileMap;
|
||||
|
@ -12,8 +12,10 @@
|
||||
#ifndef LLVM_FUZZER_UTIL_H
|
||||
#define LLVM_FUZZER_UTIL_H
|
||||
|
||||
#include "FuzzerDefs.h"
|
||||
#include "FuzzerBuiltins.h"
|
||||
#include "FuzzerBuiltinsMsvc.h"
|
||||
#include "FuzzerCommand.h"
|
||||
#include "FuzzerDefs.h"
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
@ -84,7 +86,7 @@ std::string SearchRegexCmd(const std::string &Regex);
|
||||
|
||||
size_t SimpleFastHash(const uint8_t *Data, size_t Size);
|
||||
|
||||
inline uint32_t Log(uint32_t X) { return 32 - __builtin_clz(X) - 1; }
|
||||
inline uint32_t Log(uint32_t X) { return 32 - Clz(X) - 1; }
|
||||
|
||||
} // namespace fuzzer
|
||||
|
||||
|
@ -49,9 +49,6 @@ void CrashTrampolineAsm() __asm__("CrashTrampolineAsm");
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO(phosek): remove this and replace it with ZX_TIME_INFINITE
|
||||
#define ZX_TIME_INFINITE_OLD INT64_MAX
|
||||
|
||||
// A magic value for the Zircon exception port, chosen to spell 'FUZZING'
|
||||
// when interpreted as a byte sequence on little-endian platforms.
|
||||
const uint64_t kFuzzingCrash = 0x474e495a5a5546;
|
||||
@ -237,7 +234,7 @@ void CrashHandler(zx_handle_t *Event) {
|
||||
"_zx_object_signal");
|
||||
|
||||
zx_port_packet_t Packet;
|
||||
ExitOnErr(_zx_port_wait(Port.Handle, ZX_TIME_INFINITE_OLD, &Packet),
|
||||
ExitOnErr(_zx_port_wait(Port.Handle, ZX_TIME_INFINITE, &Packet),
|
||||
"_zx_port_wait");
|
||||
|
||||
// At this point, we want to get the state of the crashing thread, but
|
||||
@ -315,8 +312,8 @@ void SetSignalHandler(const FuzzingOptions &Options) {
|
||||
ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create");
|
||||
|
||||
std::thread T(CrashHandler, &Event);
|
||||
zx_status_t Status = _zx_object_wait_one(Event, ZX_USER_SIGNAL_0,
|
||||
ZX_TIME_INFINITE_OLD, nullptr);
|
||||
zx_status_t Status =
|
||||
_zx_object_wait_one(Event, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr);
|
||||
_zx_handle_close(Event);
|
||||
ExitOnErr(Status, "_zx_object_wait_one");
|
||||
|
||||
@ -378,19 +375,28 @@ int ExecuteCommand(const Command &Cmd) {
|
||||
Argv[i] = Args[i].c_str();
|
||||
Argv[Argc] = nullptr;
|
||||
|
||||
// Determine stdout
|
||||
// Determine output. On Fuchsia, the fuzzer is typically run as a component
|
||||
// that lacks a mutable working directory. Fortunately, when this is the case
|
||||
// a mutable output directory must be specified using "-artifact_prefix=...",
|
||||
// so write the log file(s) there.
|
||||
int FdOut = STDOUT_FILENO;
|
||||
|
||||
if (Cmd.hasOutputFile()) {
|
||||
auto Filename = Cmd.getOutputFile();
|
||||
FdOut = open(Filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);
|
||||
std::string Path;
|
||||
if (Cmd.hasFlag("artifact_prefix"))
|
||||
Path = Cmd.getFlagValue("artifact_prefix") + "/" + Cmd.getOutputFile();
|
||||
else
|
||||
Path = Cmd.getOutputFile();
|
||||
FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);
|
||||
if (FdOut == -1) {
|
||||
Printf("libFuzzer: failed to open %s: %s\n", Filename.c_str(),
|
||||
Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(),
|
||||
strerror(errno));
|
||||
return ZX_ERR_IO;
|
||||
}
|
||||
}
|
||||
auto CloseFdOut = at_scope_exit([&]() { close(FdOut); } );
|
||||
auto CloseFdOut = at_scope_exit([FdOut]() {
|
||||
if (FdOut != STDOUT_FILENO)
|
||||
close(FdOut);
|
||||
});
|
||||
|
||||
// Determine stderr
|
||||
int FdErr = STDERR_FILENO;
|
||||
@ -440,7 +446,7 @@ int ExecuteCommand(const Command &Cmd) {
|
||||
|
||||
// Now join the process and return the exit status.
|
||||
if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED,
|
||||
ZX_TIME_INFINITE_OLD, nullptr)) != ZX_OK) {
|
||||
ZX_TIME_INFINITE, nullptr)) != ZX_OK) {
|
||||
Printf("libFuzzer: failed to join '%s': %s\n", Argv[0],
|
||||
_zx_status_get_string(rc));
|
||||
return rc;
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include <windows.h>
|
||||
|
||||
// This must be included after windows.h.
|
||||
#include <Psapi.h>
|
||||
#include <psapi.h>
|
||||
|
||||
namespace fuzzer {
|
||||
|
||||
@ -179,7 +179,9 @@ const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
|
||||
}
|
||||
|
||||
std::string DisassembleCmd(const std::string &FileName) {
|
||||
if (ExecuteCommand("dumpbin /summary > nul") == 0)
|
||||
Vector<std::string> command_vector;
|
||||
command_vector.push_back("dumpbin /summary > nul");
|
||||
if (ExecuteCommand(Command(command_vector)) == 0)
|
||||
return "dumpbin /disasm " + FileName;
|
||||
Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n");
|
||||
exit(1);
|
||||
|
@ -13,19 +13,20 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "hwasan.h"
|
||||
#include "hwasan_mapping.h"
|
||||
#include "hwasan_checks.h"
|
||||
#include "hwasan_poisoning.h"
|
||||
#include "hwasan_report.h"
|
||||
#include "hwasan_thread.h"
|
||||
#include "hwasan_thread_list.h"
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
#include "sanitizer_common/sanitizer_flag_parser.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_procmaps.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
#include "ubsan/ubsan_flags.h"
|
||||
#include "ubsan/ubsan_init.h"
|
||||
|
||||
@ -36,17 +37,17 @@ using namespace __sanitizer;
|
||||
namespace __hwasan {
|
||||
|
||||
void EnterSymbolizer() {
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
Thread *t = GetCurrentThread();
|
||||
CHECK(t);
|
||||
t->EnterSymbolizer();
|
||||
}
|
||||
void ExitSymbolizer() {
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
Thread *t = GetCurrentThread();
|
||||
CHECK(t);
|
||||
t->LeaveSymbolizer();
|
||||
}
|
||||
bool IsInSymbolizer() {
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
Thread *t = GetCurrentThread();
|
||||
return t && t->InSymbolizer();
|
||||
}
|
||||
|
||||
@ -57,6 +58,7 @@ Flags *flags() {
|
||||
}
|
||||
|
||||
int hwasan_inited = 0;
|
||||
int hwasan_shadow_inited = 0;
|
||||
bool hwasan_init_is_running;
|
||||
|
||||
int hwasan_report_count = 0;
|
||||
@ -86,7 +88,18 @@ static void InitializeFlags() {
|
||||
cf.check_printf = false;
|
||||
cf.intercept_tls_get_addr = true;
|
||||
cf.exitcode = 99;
|
||||
// Sigtrap is used in error reporting.
|
||||
cf.handle_sigtrap = kHandleSignalExclusive;
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
// Let platform handle other signals. It is better at reporting them then we
|
||||
// are.
|
||||
cf.handle_segv = kHandleSignalNo;
|
||||
cf.handle_sigbus = kHandleSignalNo;
|
||||
cf.handle_abort = kHandleSignalNo;
|
||||
cf.handle_sigill = kHandleSignalNo;
|
||||
cf.handle_sigfpe = kHandleSignalNo;
|
||||
#endif
|
||||
OverrideCommonFlags(cf);
|
||||
}
|
||||
|
||||
@ -119,7 +132,8 @@ static void InitializeFlags() {
|
||||
#if HWASAN_CONTAINS_UBSAN
|
||||
ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
|
||||
#endif
|
||||
VPrintf(1, "HWASAN_OPTIONS: %s\n", hwasan_options ? hwasan_options : "<empty>");
|
||||
VPrintf(1, "HWASAN_OPTIONS: %s\n",
|
||||
hwasan_options ? hwasan_options : "<empty>");
|
||||
|
||||
InitializeCommonFlags();
|
||||
|
||||
@ -130,8 +144,13 @@ static void InitializeFlags() {
|
||||
|
||||
void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp,
|
||||
void *context, bool request_fast_unwind) {
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
if (!t || !StackTrace::WillUseFastUnwind(request_fast_unwind)) {
|
||||
Thread *t = GetCurrentThread();
|
||||
if (!t) {
|
||||
// the thread is still being created.
|
||||
stack->size = 0;
|
||||
return;
|
||||
}
|
||||
if (!StackTrace::WillUseFastUnwind(request_fast_unwind)) {
|
||||
// Block reports from our interceptors during _Unwind_Backtrace.
|
||||
SymbolizerScope sym_scope;
|
||||
return stack->Unwind(max_s, pc, bp, context, 0, 0, request_fast_unwind);
|
||||
@ -140,11 +159,6 @@ void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp,
|
||||
request_fast_unwind);
|
||||
}
|
||||
|
||||
void PrintWarning(uptr pc, uptr bp) {
|
||||
GET_FATAL_STACK_TRACE_PC_BP(pc, bp);
|
||||
ReportInvalidAccess(&stack, 0);
|
||||
}
|
||||
|
||||
static void HWAsanCheckFailed(const char *file, int line, const char *cond,
|
||||
u64 v1, u64 v2) {
|
||||
Report("HWAddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file,
|
||||
@ -153,6 +167,84 @@ static void HWAsanCheckFailed(const char *file, int line, const char *cond,
|
||||
Die();
|
||||
}
|
||||
|
||||
static constexpr uptr kMemoryUsageBufferSize = 4096;
|
||||
|
||||
static void HwasanFormatMemoryUsage(InternalScopedString &s) {
|
||||
HwasanThreadList &thread_list = hwasanThreadList();
|
||||
auto thread_stats = thread_list.GetThreadStats();
|
||||
auto *sds = StackDepotGetStats();
|
||||
AllocatorStatCounters asc;
|
||||
GetAllocatorStats(asc);
|
||||
s.append(
|
||||
"HWASAN pid: %d rss: %zd threads: %zd stacks: %zd"
|
||||
" thr_aux: %zd stack_depot: %zd uniq_stacks: %zd"
|
||||
" heap: %zd",
|
||||
internal_getpid(), GetRSS(), thread_stats.n_live_threads,
|
||||
thread_stats.total_stack_size,
|
||||
thread_stats.n_live_threads * thread_list.MemoryUsedPerThread(),
|
||||
sds->allocated, sds->n_uniq_ids, asc[AllocatorStatMapped]);
|
||||
}
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
static char *memory_usage_buffer = nullptr;
|
||||
|
||||
#define PR_SET_VMA 0x53564d41
|
||||
#define PR_SET_VMA_ANON_NAME 0
|
||||
|
||||
static void InitMemoryUsage() {
|
||||
memory_usage_buffer =
|
||||
(char *)MmapOrDie(kMemoryUsageBufferSize, "memory usage string");
|
||||
CHECK(memory_usage_buffer);
|
||||
memory_usage_buffer[0] = '\0';
|
||||
CHECK(internal_prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
|
||||
(uptr)memory_usage_buffer, kMemoryUsageBufferSize,
|
||||
(uptr)memory_usage_buffer) == 0);
|
||||
}
|
||||
|
||||
void UpdateMemoryUsage() {
|
||||
if (!flags()->export_memory_stats)
|
||||
return;
|
||||
if (!memory_usage_buffer)
|
||||
InitMemoryUsage();
|
||||
InternalScopedString s(kMemoryUsageBufferSize);
|
||||
HwasanFormatMemoryUsage(s);
|
||||
internal_strncpy(memory_usage_buffer, s.data(), kMemoryUsageBufferSize - 1);
|
||||
memory_usage_buffer[kMemoryUsageBufferSize - 1] = '\0';
|
||||
}
|
||||
#else
|
||||
void UpdateMemoryUsage() {}
|
||||
#endif
|
||||
|
||||
struct FrameDescription {
|
||||
uptr PC;
|
||||
const char *Descr;
|
||||
};
|
||||
|
||||
struct FrameDescriptionArray {
|
||||
FrameDescription *beg, *end;
|
||||
};
|
||||
|
||||
static InternalMmapVectorNoCtor<FrameDescriptionArray> AllFrames;
|
||||
|
||||
void InitFrameDescriptors(uptr b, uptr e) {
|
||||
FrameDescription *beg = reinterpret_cast<FrameDescription *>(b);
|
||||
FrameDescription *end = reinterpret_cast<FrameDescription *>(e);
|
||||
if (beg == end)
|
||||
return;
|
||||
AllFrames.push_back({beg, end});
|
||||
if (Verbosity())
|
||||
for (FrameDescription *frame_descr = beg; frame_descr < end; frame_descr++)
|
||||
Printf("Frame: %p %s\n", frame_descr->PC, frame_descr->Descr);
|
||||
}
|
||||
|
||||
const char *GetStackFrameDescr(uptr pc) {
|
||||
for (uptr i = 0, n = AllFrames.size(); i < n; i++)
|
||||
for (auto p = AllFrames[i].beg; p < AllFrames[i].end; p++)
|
||||
if (p->PC == pc)
|
||||
return p->Descr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
// Interface.
|
||||
@ -161,6 +253,20 @@ using namespace __hwasan;
|
||||
|
||||
uptr __hwasan_shadow_memory_dynamic_address; // Global interface symbol.
|
||||
|
||||
void __hwasan_shadow_init() {
|
||||
if (hwasan_shadow_inited) return;
|
||||
if (!InitShadow()) {
|
||||
Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
|
||||
DumpProcessMap();
|
||||
Die();
|
||||
}
|
||||
hwasan_shadow_inited = 1;
|
||||
}
|
||||
|
||||
void __hwasan_init_frames(uptr beg, uptr end) {
|
||||
InitFrameDescriptors(beg, end);
|
||||
}
|
||||
|
||||
void __hwasan_init() {
|
||||
CHECK(!hwasan_init_is_running);
|
||||
if (hwasan_inited) return;
|
||||
@ -177,18 +283,20 @@ void __hwasan_init() {
|
||||
|
||||
__sanitizer_set_report_path(common_flags()->log_path);
|
||||
|
||||
AndroidTestTlsSlot();
|
||||
|
||||
DisableCoreDumperIfNecessary();
|
||||
if (!InitShadow()) {
|
||||
Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
|
||||
if (HWASAN_FIXED_MAPPING) {
|
||||
Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
|
||||
Printf("FATAL: Disabling ASLR is known to cause this error.\n");
|
||||
Printf("FATAL: If running under GDB, try "
|
||||
"'set disable-randomization off'.\n");
|
||||
}
|
||||
DumpProcessMap();
|
||||
Die();
|
||||
}
|
||||
|
||||
__hwasan_shadow_init();
|
||||
|
||||
InitThreads();
|
||||
hwasanThreadList().CreateCurrentThread();
|
||||
|
||||
MadviseShadow();
|
||||
|
||||
SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
|
||||
// This may call libc -> needs initialized shadow.
|
||||
AndroidLogInit();
|
||||
|
||||
InitializeInterceptors();
|
||||
InstallDeadlySignalHandlers(HwasanOnDeadlySignal);
|
||||
@ -198,14 +306,11 @@ void __hwasan_init() {
|
||||
|
||||
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
|
||||
|
||||
HwasanTSDInit(HwasanTSDDtor);
|
||||
HwasanTSDInit();
|
||||
HwasanTSDThreadInit();
|
||||
|
||||
HwasanAllocatorInit();
|
||||
|
||||
HwasanThread *main_thread = HwasanThread::Create(nullptr, nullptr);
|
||||
SetCurrentThread(main_thread);
|
||||
main_thread->ThreadStart();
|
||||
|
||||
#if HWASAN_CONTAINS_UBSAN
|
||||
__ubsan::InitAsPlugin();
|
||||
#endif
|
||||
@ -216,9 +321,14 @@ void __hwasan_init() {
|
||||
hwasan_inited = 1;
|
||||
}
|
||||
|
||||
void __hwasan_print_shadow(const void *x, uptr size) {
|
||||
// FIXME:
|
||||
Printf("FIXME: __hwasan_print_shadow unimplemented\n");
|
||||
void __hwasan_print_shadow(const void *p, uptr sz) {
|
||||
uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
|
||||
uptr shadow_first = MemToShadow(ptr_raw);
|
||||
uptr shadow_last = MemToShadow(ptr_raw + sz - 1);
|
||||
Printf("HWASan shadow map for %zx .. %zx (pointer tag %x)\n", ptr_raw,
|
||||
ptr_raw + sz, GetTagFromPointer((uptr)p));
|
||||
for (uptr s = shadow_first; s <= shadow_last; ++s)
|
||||
Printf(" %zx: %x\n", ShadowToMem(s), *(tag_t *)s);
|
||||
}
|
||||
|
||||
sptr __hwasan_test_shadow(const void *p, uptr sz) {
|
||||
@ -227,12 +337,12 @@ sptr __hwasan_test_shadow(const void *p, uptr sz) {
|
||||
tag_t ptr_tag = GetTagFromPointer((uptr)p);
|
||||
if (ptr_tag == 0)
|
||||
return -1;
|
||||
uptr ptr_raw = GetAddressFromPointer((uptr)p);
|
||||
uptr shadow_first = MEM_TO_SHADOW(ptr_raw);
|
||||
uptr shadow_last = MEM_TO_SHADOW(ptr_raw + sz - 1);
|
||||
uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
|
||||
uptr shadow_first = MemToShadow(ptr_raw);
|
||||
uptr shadow_last = MemToShadow(ptr_raw + sz - 1);
|
||||
for (uptr s = shadow_first; s <= shadow_last; ++s)
|
||||
if (*(tag_t*)s != ptr_tag)
|
||||
return SHADOW_TO_MEM(s) - ptr_raw;
|
||||
return ShadowToMem(s) - ptr_raw;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -255,63 +365,6 @@ void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
|
||||
*p = x;
|
||||
}
|
||||
|
||||
template<unsigned X>
|
||||
__attribute__((always_inline))
|
||||
static void SigTrap(uptr p) {
|
||||
#if defined(__aarch64__)
|
||||
(void)p;
|
||||
// 0x900 is added to do not interfere with the kernel use of lower values of
|
||||
// brk immediate.
|
||||
// FIXME: Add a constraint to put the pointer into x0, the same as x86 branch.
|
||||
asm("brk %0\n\t" ::"n"(0x900 + X));
|
||||
#elif defined(__x86_64__)
|
||||
// INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes
|
||||
// total. The pointer is passed via rdi.
|
||||
// 0x40 is added as a safeguard, to help distinguish our trap from others and
|
||||
// to avoid 0 offsets in the command (otherwise it'll be reduced to a
|
||||
// different nop command, the three bytes one).
|
||||
asm volatile(
|
||||
"int3\n"
|
||||
"nopl %c0(%%rax)\n"
|
||||
:: "n"(0x40 + X), "D"(p));
|
||||
#else
|
||||
// FIXME: not always sigill.
|
||||
__builtin_trap();
|
||||
#endif
|
||||
// __builtin_unreachable();
|
||||
}
|
||||
|
||||
enum class ErrorAction { Abort, Recover };
|
||||
enum class AccessType { Load, Store };
|
||||
|
||||
template <ErrorAction EA, AccessType AT, unsigned LogSize>
|
||||
__attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) {
|
||||
tag_t ptr_tag = GetTagFromPointer(p);
|
||||
uptr ptr_raw = p & ~kAddressTagMask;
|
||||
tag_t mem_tag = *(tag_t *)MEM_TO_SHADOW(ptr_raw);
|
||||
if (UNLIKELY(ptr_tag != mem_tag)) {
|
||||
SigTrap<0x20 * (EA == ErrorAction::Recover) +
|
||||
0x10 * (AT == AccessType::Store) + LogSize>(p);
|
||||
if (EA == ErrorAction::Abort) __builtin_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <ErrorAction EA, AccessType AT>
|
||||
__attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
|
||||
uptr sz) {
|
||||
CHECK_NE(0, sz);
|
||||
tag_t ptr_tag = GetTagFromPointer(p);
|
||||
uptr ptr_raw = p & ~kAddressTagMask;
|
||||
tag_t *shadow_first = (tag_t *)MEM_TO_SHADOW(ptr_raw);
|
||||
tag_t *shadow_last = (tag_t *)MEM_TO_SHADOW(ptr_raw + sz - 1);
|
||||
for (tag_t *t = shadow_first; t <= shadow_last; ++t)
|
||||
if (UNLIKELY(ptr_tag != *t)) {
|
||||
SigTrap<0x20 * (EA == ErrorAction::Recover) +
|
||||
0x10 * (AT == AccessType::Store) + 0xf>(p);
|
||||
if (EA == ErrorAction::Abort) __builtin_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
void __hwasan_loadN(uptr p, uptr sz) {
|
||||
CheckAddressSized<ErrorAction::Abort, AccessType::Load>(p, sz);
|
||||
}
|
||||
@ -392,10 +445,38 @@ void __hwasan_tag_memory(uptr p, u8 tag, uptr sz) {
|
||||
TagMemoryAligned(p, sz, tag);
|
||||
}
|
||||
|
||||
uptr __hwasan_tag_pointer(uptr p, u8 tag) {
|
||||
return AddTagToPointer(p, tag);
|
||||
}
|
||||
|
||||
void __hwasan_handle_longjmp(const void *sp_dst) {
|
||||
uptr dst = (uptr)sp_dst;
|
||||
// HWASan does not support tagged SP.
|
||||
CHECK(GetTagFromPointer(dst) == 0);
|
||||
|
||||
uptr sp = (uptr)__builtin_frame_address(0);
|
||||
static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M
|
||||
if (dst < sp || dst - sp > kMaxExpectedCleanupSize) {
|
||||
Report(
|
||||
"WARNING: HWASan is ignoring requested __hwasan_handle_longjmp: "
|
||||
"stack top: %p; target %p; distance: %p (%zd)\n"
|
||||
"False positive error reports may follow\n",
|
||||
(void *)sp, (void *)dst, dst - sp);
|
||||
return;
|
||||
}
|
||||
TagMemory(sp, dst - sp, 0);
|
||||
}
|
||||
|
||||
void __hwasan_print_memory_usage() {
|
||||
InternalScopedString s(kMemoryUsageBufferSize);
|
||||
HwasanFormatMemoryUsage(s);
|
||||
Printf("%s\n", s.data());
|
||||
}
|
||||
|
||||
static const u8 kFallbackTag = 0xBB;
|
||||
|
||||
u8 __hwasan_generate_tag() {
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
Thread *t = GetCurrentThread();
|
||||
if (!t) return kFallbackTag;
|
||||
return t->GenerateRandomTag();
|
||||
}
|
||||
|
@ -30,6 +30,10 @@
|
||||
# define HWASAN_CONTAINS_UBSAN CAN_SANITIZE_UB
|
||||
#endif
|
||||
|
||||
#ifndef HWASAN_WITH_INTERCEPTORS
|
||||
#define HWASAN_WITH_INTERCEPTORS 0
|
||||
#endif
|
||||
|
||||
typedef u8 tag_t;
|
||||
|
||||
// TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address
|
||||
@ -37,16 +41,21 @@ typedef u8 tag_t;
|
||||
const unsigned kAddressTagShift = 56;
|
||||
const uptr kAddressTagMask = 0xFFUL << kAddressTagShift;
|
||||
|
||||
// Minimal alignment of the shadow base address. Determines the space available
|
||||
// for threads and stack histories. This is an ABI constant.
|
||||
const unsigned kShadowBaseAlignment = 32;
|
||||
|
||||
static inline tag_t GetTagFromPointer(uptr p) {
|
||||
return p >> kAddressTagShift;
|
||||
}
|
||||
|
||||
static inline uptr GetAddressFromPointer(uptr p) {
|
||||
return p & ~kAddressTagMask;
|
||||
static inline uptr UntagAddr(uptr tagged_addr) {
|
||||
return tagged_addr & ~kAddressTagMask;
|
||||
}
|
||||
|
||||
static inline void * GetAddressFromPointer(const void *p) {
|
||||
return (void *)((uptr)p & ~kAddressTagMask);
|
||||
static inline void *UntagPtr(const void *tagged_ptr) {
|
||||
return reinterpret_cast<void *>(
|
||||
UntagAddr(reinterpret_cast<uptr>(tagged_ptr)));
|
||||
}
|
||||
|
||||
static inline uptr AddTagToPointer(uptr p, tag_t tag) {
|
||||
@ -61,12 +70,13 @@ extern int hwasan_report_count;
|
||||
|
||||
bool ProtectRange(uptr beg, uptr end);
|
||||
bool InitShadow();
|
||||
void InitThreads();
|
||||
void MadviseShadow();
|
||||
char *GetProcSelfMaps();
|
||||
void InitializeInterceptors();
|
||||
|
||||
void HwasanAllocatorInit();
|
||||
void HwasanAllocatorThreadFinish();
|
||||
void HwasanDeallocate(StackTrace *stack, void *ptr);
|
||||
|
||||
void *hwasan_malloc(uptr size, StackTrace *stack);
|
||||
void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack);
|
||||
@ -77,11 +87,13 @@ void *hwasan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack);
|
||||
void *hwasan_memalign(uptr alignment, uptr size, StackTrace *stack);
|
||||
int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
|
||||
StackTrace *stack);
|
||||
void hwasan_free(void *ptr, StackTrace *stack);
|
||||
|
||||
void InstallTrapHandler();
|
||||
void InstallAtExitHandler();
|
||||
|
||||
const char *GetStackOriginDescr(u32 id, uptr *pc);
|
||||
const char *GetStackFrameDescr(uptr pc);
|
||||
|
||||
void EnterSymbolizer();
|
||||
void ExitSymbolizer();
|
||||
@ -92,8 +104,6 @@ struct SymbolizerScope {
|
||||
~SymbolizerScope() { ExitSymbolizer(); }
|
||||
};
|
||||
|
||||
void PrintWarning(uptr pc, uptr bp);
|
||||
|
||||
void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp,
|
||||
void *context, bool request_fast_unwind);
|
||||
|
||||
@ -135,13 +145,17 @@ class ScopedThreadLocalStateBackup {
|
||||
u64 va_arg_overflow_size_tls;
|
||||
};
|
||||
|
||||
void HwasanTSDInit(void (*destructor)(void *tsd));
|
||||
void *HwasanTSDGet();
|
||||
void HwasanTSDSet(void *tsd);
|
||||
void HwasanTSDDtor(void *tsd);
|
||||
void HwasanTSDInit();
|
||||
void HwasanTSDThreadInit();
|
||||
|
||||
void HwasanOnDeadlySignal(int signo, void *info, void *context);
|
||||
|
||||
void UpdateMemoryUsage();
|
||||
|
||||
void AppendToErrorMessageBuffer(const char *buffer);
|
||||
|
||||
void AndroidTestTlsSlot();
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
#define HWASAN_MALLOC_HOOK(ptr, size) \
|
||||
|
@ -12,10 +12,6 @@
|
||||
// HWAddressSanitizer allocator.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common/sanitizer_allocator.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_checks.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_interface.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_report.h"
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_errno.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
@ -23,30 +19,53 @@
|
||||
#include "hwasan_allocator.h"
|
||||
#include "hwasan_mapping.h"
|
||||
#include "hwasan_thread.h"
|
||||
#include "hwasan_poisoning.h"
|
||||
#include "hwasan_report.h"
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
DEFINE_REAL(void *, realloc, void *ptr, uptr size)
|
||||
DEFINE_REAL(void, free, void *ptr)
|
||||
#endif
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
enum {
|
||||
CHUNK_INVALID = 0,
|
||||
CHUNK_FREE = 1,
|
||||
CHUNK_ALLOCATED = 2
|
||||
static Allocator allocator;
|
||||
static AllocatorCache fallback_allocator_cache;
|
||||
static SpinMutex fallback_mutex;
|
||||
static atomic_uint8_t hwasan_allocator_tagging_enabled;
|
||||
|
||||
static const tag_t kFallbackAllocTag = 0xBB;
|
||||
static const tag_t kFallbackFreeTag = 0xBC;
|
||||
|
||||
enum RightAlignMode {
|
||||
kRightAlignNever,
|
||||
kRightAlignSometimes,
|
||||
kRightAlignAlways
|
||||
};
|
||||
|
||||
struct Metadata {
|
||||
u64 state : 2;
|
||||
u64 requested_size : 62;
|
||||
u32 alloc_context_id;
|
||||
u32 free_context_id;
|
||||
};
|
||||
// These two variables are initialized from flags()->malloc_align_right
|
||||
// in HwasanAllocatorInit and are never changed afterwards.
|
||||
static RightAlignMode right_align_mode = kRightAlignNever;
|
||||
static bool right_align_8 = false;
|
||||
|
||||
// Initialized in HwasanAllocatorInit, an never changed.
|
||||
static ALIGNED(16) u8 tail_magic[kShadowAlignment];
|
||||
|
||||
bool HwasanChunkView::IsValid() const {
|
||||
return metadata_ && metadata_->state != CHUNK_INVALID;
|
||||
}
|
||||
bool HwasanChunkView::IsAllocated() const {
|
||||
return metadata_ && metadata_->state == CHUNK_ALLOCATED;
|
||||
return metadata_ && metadata_->alloc_context_id && metadata_->requested_size;
|
||||
}
|
||||
|
||||
// Aligns the 'addr' right to the granule boundary.
|
||||
static uptr AlignRight(uptr addr, uptr requested_size) {
|
||||
uptr tail_size = requested_size % kShadowAlignment;
|
||||
if (!tail_size) return addr;
|
||||
if (right_align_8)
|
||||
return tail_size > 8 ? addr : addr + 8;
|
||||
return addr + kShadowAlignment - tail_size;
|
||||
}
|
||||
|
||||
uptr HwasanChunkView::Beg() const {
|
||||
if (metadata_ && metadata_->right_aligned)
|
||||
return AlignRight(block_, metadata_->requested_size);
|
||||
return block_;
|
||||
}
|
||||
uptr HwasanChunkView::End() const {
|
||||
@ -58,88 +77,79 @@ uptr HwasanChunkView::UsedSize() const {
|
||||
u32 HwasanChunkView::GetAllocStackId() const {
|
||||
return metadata_->alloc_context_id;
|
||||
}
|
||||
u32 HwasanChunkView::GetFreeStackId() const {
|
||||
return metadata_->free_context_id;
|
||||
|
||||
uptr HwasanChunkView::ActualSize() const {
|
||||
return allocator.GetActuallyAllocatedSize(reinterpret_cast<void *>(block_));
|
||||
}
|
||||
|
||||
struct HwasanMapUnmapCallback {
|
||||
void OnMap(uptr p, uptr size) const {}
|
||||
void OnUnmap(uptr p, uptr size) const {
|
||||
// We are about to unmap a chunk of user memory.
|
||||
// It can return as user-requested mmap() or another thread stack.
|
||||
// Make it accessible with zero-tagged pointer.
|
||||
TagMemory(p, size, 0);
|
||||
}
|
||||
};
|
||||
bool HwasanChunkView::FromSmallHeap() const {
|
||||
return allocator.FromPrimary(reinterpret_cast<void *>(block_));
|
||||
}
|
||||
|
||||
#if !defined(__aarch64__) && !defined(__x86_64__)
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
|
||||
static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G
|
||||
static const uptr kRegionSizeLog = 20;
|
||||
static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
|
||||
typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
|
||||
|
||||
struct AP32 {
|
||||
static const uptr kSpaceBeg = 0;
|
||||
static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
|
||||
static const uptr kMetadataSize = sizeof(Metadata);
|
||||
typedef __sanitizer::CompactSizeClassMap SizeClassMap;
|
||||
static const uptr kRegionSizeLog = __hwasan::kRegionSizeLog;
|
||||
typedef __hwasan::ByteMap ByteMap;
|
||||
typedef HwasanMapUnmapCallback MapUnmapCallback;
|
||||
static const uptr kFlags = 0;
|
||||
};
|
||||
typedef SizeClassAllocator32<AP32> PrimaryAllocator;
|
||||
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
|
||||
typedef LargeMmapAllocator<HwasanMapUnmapCallback> SecondaryAllocator;
|
||||
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
|
||||
SecondaryAllocator> Allocator;
|
||||
|
||||
static Allocator allocator;
|
||||
static AllocatorCache fallback_allocator_cache;
|
||||
static SpinMutex fallback_mutex;
|
||||
static atomic_uint8_t hwasan_allocator_tagging_enabled;
|
||||
|
||||
static const tag_t kFallbackAllocTag = 0xBB;
|
||||
static const tag_t kFallbackFreeTag = 0xBC;
|
||||
void GetAllocatorStats(AllocatorStatCounters s) {
|
||||
allocator.GetStats(s);
|
||||
}
|
||||
|
||||
void HwasanAllocatorInit() {
|
||||
atomic_store_relaxed(&hwasan_allocator_tagging_enabled,
|
||||
!flags()->disable_allocator_tagging);
|
||||
SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
|
||||
allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
|
||||
switch (flags()->malloc_align_right) {
|
||||
case 0: break;
|
||||
case 1:
|
||||
right_align_mode = kRightAlignSometimes;
|
||||
right_align_8 = false;
|
||||
break;
|
||||
case 2:
|
||||
right_align_mode = kRightAlignAlways;
|
||||
right_align_8 = false;
|
||||
break;
|
||||
case 8:
|
||||
right_align_mode = kRightAlignSometimes;
|
||||
right_align_8 = true;
|
||||
break;
|
||||
case 9:
|
||||
right_align_mode = kRightAlignAlways;
|
||||
right_align_8 = true;
|
||||
break;
|
||||
default:
|
||||
Report("ERROR: unsupported value of malloc_align_right flag: %d\n",
|
||||
flags()->malloc_align_right);
|
||||
Die();
|
||||
}
|
||||
for (uptr i = 0; i < kShadowAlignment; i++)
|
||||
tail_magic[i] = GetCurrentThread()->GenerateRandomTag();
|
||||
}
|
||||
|
||||
AllocatorCache *GetAllocatorCache(HwasanThreadLocalMallocStorage *ms) {
|
||||
CHECK(ms);
|
||||
CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache));
|
||||
return reinterpret_cast<AllocatorCache *>(ms->allocator_cache);
|
||||
void AllocatorSwallowThreadLocalCache(AllocatorCache *cache) {
|
||||
allocator.SwallowCache(cache);
|
||||
}
|
||||
|
||||
void HwasanThreadLocalMallocStorage::CommitBack() {
|
||||
allocator.SwallowCache(GetAllocatorCache(this));
|
||||
static uptr TaggedSize(uptr size) {
|
||||
if (!size) size = 1;
|
||||
uptr new_size = RoundUpTo(size, kShadowAlignment);
|
||||
CHECK_GE(new_size, size);
|
||||
return new_size;
|
||||
}
|
||||
|
||||
static void *HwasanAllocate(StackTrace *stack, uptr size, uptr alignment,
|
||||
bool zeroise) {
|
||||
alignment = Max(alignment, kShadowAlignment);
|
||||
size = RoundUpTo(size, kShadowAlignment);
|
||||
|
||||
if (size > kMaxAllowedMallocSize) {
|
||||
static void *HwasanAllocate(StackTrace *stack, uptr orig_size, uptr alignment,
|
||||
bool zeroise) {
|
||||
if (orig_size > kMaxAllowedMallocSize) {
|
||||
if (AllocatorMayReturnNull()) {
|
||||
Report("WARNING: HWAddressSanitizer failed to allocate 0x%zx bytes\n",
|
||||
size);
|
||||
orig_size);
|
||||
return nullptr;
|
||||
}
|
||||
ReportAllocationSizeTooBig(size, kMaxAllowedMallocSize, stack);
|
||||
ReportAllocationSizeTooBig(orig_size, kMaxAllowedMallocSize, stack);
|
||||
}
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
|
||||
alignment = Max(alignment, kShadowAlignment);
|
||||
uptr size = TaggedSize(orig_size);
|
||||
Thread *t = GetCurrentThread();
|
||||
void *allocated;
|
||||
if (t) {
|
||||
AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
|
||||
allocated = allocator.Allocate(cache, size, alignment);
|
||||
allocated = allocator.Allocate(t->allocator_cache(), size, alignment);
|
||||
} else {
|
||||
SpinMutexLock l(&fallback_mutex);
|
||||
AllocatorCache *cache = &fallback_allocator_cache;
|
||||
@ -153,11 +163,18 @@ static void *HwasanAllocate(StackTrace *stack, uptr size, uptr alignment,
|
||||
}
|
||||
Metadata *meta =
|
||||
reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));
|
||||
meta->state = CHUNK_ALLOCATED;
|
||||
meta->requested_size = size;
|
||||
meta->requested_size = static_cast<u32>(orig_size);
|
||||
meta->alloc_context_id = StackDepotPut(*stack);
|
||||
if (zeroise)
|
||||
meta->right_aligned = false;
|
||||
if (zeroise) {
|
||||
internal_memset(allocated, 0, size);
|
||||
} else if (flags()->max_malloc_fill_size > 0) {
|
||||
uptr fill_size = Min(size, (uptr)flags()->max_malloc_fill_size);
|
||||
internal_memset(allocated, flags()->malloc_fill_byte, fill_size);
|
||||
}
|
||||
if (!right_align_mode)
|
||||
internal_memcpy(reinterpret_cast<u8 *>(allocated) + orig_size, tail_magic,
|
||||
size - orig_size);
|
||||
|
||||
void *user_ptr = allocated;
|
||||
if (flags()->tag_in_malloc &&
|
||||
@ -165,74 +182,101 @@ static void *HwasanAllocate(StackTrace *stack, uptr size, uptr alignment,
|
||||
user_ptr = (void *)TagMemoryAligned(
|
||||
(uptr)user_ptr, size, t ? t->GenerateRandomTag() : kFallbackAllocTag);
|
||||
|
||||
if ((orig_size % kShadowAlignment) && (alignment <= kShadowAlignment) &&
|
||||
right_align_mode) {
|
||||
uptr as_uptr = reinterpret_cast<uptr>(user_ptr);
|
||||
if (right_align_mode == kRightAlignAlways ||
|
||||
GetTagFromPointer(as_uptr) & 1) { // use a tag bit as a random bit.
|
||||
user_ptr = reinterpret_cast<void *>(AlignRight(as_uptr, orig_size));
|
||||
meta->right_aligned = 1;
|
||||
}
|
||||
}
|
||||
|
||||
HWASAN_MALLOC_HOOK(user_ptr, size);
|
||||
return user_ptr;
|
||||
}
|
||||
|
||||
void HwasanDeallocate(StackTrace *stack, void *user_ptr) {
|
||||
CHECK(user_ptr);
|
||||
HWASAN_FREE_HOOK(user_ptr);
|
||||
static bool PointerAndMemoryTagsMatch(void *tagged_ptr) {
|
||||
CHECK(tagged_ptr);
|
||||
tag_t ptr_tag = GetTagFromPointer(reinterpret_cast<uptr>(tagged_ptr));
|
||||
tag_t mem_tag = *reinterpret_cast<tag_t *>(
|
||||
MemToShadow(reinterpret_cast<uptr>(UntagPtr(tagged_ptr))));
|
||||
return ptr_tag == mem_tag;
|
||||
}
|
||||
|
||||
static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
|
||||
CHECK(tagged_ptr);
|
||||
HWASAN_FREE_HOOK(tagged_ptr);
|
||||
|
||||
if (!PointerAndMemoryTagsMatch(tagged_ptr))
|
||||
ReportInvalidFree(stack, reinterpret_cast<uptr>(tagged_ptr));
|
||||
|
||||
void *untagged_ptr = UntagPtr(tagged_ptr);
|
||||
void *aligned_ptr = reinterpret_cast<void *>(
|
||||
RoundDownTo(reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment));
|
||||
Metadata *meta =
|
||||
reinterpret_cast<Metadata *>(allocator.GetMetaData(aligned_ptr));
|
||||
uptr orig_size = meta->requested_size;
|
||||
u32 free_context_id = StackDepotPut(*stack);
|
||||
u32 alloc_context_id = meta->alloc_context_id;
|
||||
|
||||
// Check tail magic.
|
||||
uptr tagged_size = TaggedSize(orig_size);
|
||||
if (flags()->free_checks_tail_magic && !right_align_mode && orig_size) {
|
||||
uptr tail_size = tagged_size - orig_size;
|
||||
CHECK_LT(tail_size, kShadowAlignment);
|
||||
void *tail_beg = reinterpret_cast<void *>(
|
||||
reinterpret_cast<uptr>(aligned_ptr) + orig_size);
|
||||
if (tail_size && internal_memcmp(tail_beg, tail_magic, tail_size))
|
||||
ReportTailOverwritten(stack, reinterpret_cast<uptr>(tagged_ptr),
|
||||
orig_size, tail_size, tail_magic);
|
||||
}
|
||||
|
||||
void *p = GetAddressFromPointer(user_ptr);
|
||||
Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p));
|
||||
uptr size = meta->requested_size;
|
||||
meta->state = CHUNK_FREE;
|
||||
meta->requested_size = 0;
|
||||
meta->free_context_id = StackDepotPut(*stack);
|
||||
meta->alloc_context_id = 0;
|
||||
// This memory will not be reused by anyone else, so we are free to keep it
|
||||
// poisoned.
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
Thread *t = GetCurrentThread();
|
||||
if (flags()->max_free_fill_size > 0) {
|
||||
uptr fill_size =
|
||||
Min(TaggedSize(orig_size), (uptr)flags()->max_free_fill_size);
|
||||
internal_memset(aligned_ptr, flags()->free_fill_byte, fill_size);
|
||||
}
|
||||
if (flags()->tag_in_free &&
|
||||
atomic_load_relaxed(&hwasan_allocator_tagging_enabled))
|
||||
TagMemoryAligned((uptr)p, size,
|
||||
TagMemoryAligned(reinterpret_cast<uptr>(aligned_ptr), TaggedSize(orig_size),
|
||||
t ? t->GenerateRandomTag() : kFallbackFreeTag);
|
||||
if (t) {
|
||||
AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
|
||||
allocator.Deallocate(cache, p);
|
||||
allocator.Deallocate(t->allocator_cache(), aligned_ptr);
|
||||
if (auto *ha = t->heap_allocations())
|
||||
ha->push({reinterpret_cast<uptr>(tagged_ptr), alloc_context_id,
|
||||
free_context_id, static_cast<u32>(orig_size)});
|
||||
} else {
|
||||
SpinMutexLock l(&fallback_mutex);
|
||||
AllocatorCache *cache = &fallback_allocator_cache;
|
||||
allocator.Deallocate(cache, p);
|
||||
allocator.Deallocate(cache, aligned_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void *HwasanReallocate(StackTrace *stack, void *user_old_p, uptr new_size,
|
||||
uptr alignment) {
|
||||
alignment = Max(alignment, kShadowAlignment);
|
||||
new_size = RoundUpTo(new_size, kShadowAlignment);
|
||||
static void *HwasanReallocate(StackTrace *stack, void *tagged_ptr_old,
|
||||
uptr new_size, uptr alignment) {
|
||||
if (!PointerAndMemoryTagsMatch(tagged_ptr_old))
|
||||
ReportInvalidFree(stack, reinterpret_cast<uptr>(tagged_ptr_old));
|
||||
|
||||
void *old_p = GetAddressFromPointer(user_old_p);
|
||||
Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(old_p));
|
||||
uptr old_size = meta->requested_size;
|
||||
uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p);
|
||||
if (new_size <= actually_allocated_size) {
|
||||
// We are not reallocating here.
|
||||
// FIXME: update stack trace for the allocation?
|
||||
meta->requested_size = new_size;
|
||||
if (!atomic_load_relaxed(&hwasan_allocator_tagging_enabled))
|
||||
return user_old_p;
|
||||
if (flags()->retag_in_realloc) {
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
return (void *)TagMemoryAligned(
|
||||
(uptr)old_p, new_size,
|
||||
t ? t->GenerateRandomTag() : kFallbackAllocTag);
|
||||
}
|
||||
if (new_size > old_size) {
|
||||
tag_t tag = GetTagFromPointer((uptr)user_old_p);
|
||||
TagMemoryAligned((uptr)old_p + old_size, new_size - old_size, tag);
|
||||
}
|
||||
return user_old_p;
|
||||
void *tagged_ptr_new =
|
||||
HwasanAllocate(stack, new_size, alignment, false /*zeroise*/);
|
||||
if (tagged_ptr_old && tagged_ptr_new) {
|
||||
void *untagged_ptr_old = UntagPtr(tagged_ptr_old);
|
||||
Metadata *meta =
|
||||
reinterpret_cast<Metadata *>(allocator.GetMetaData(untagged_ptr_old));
|
||||
internal_memcpy(UntagPtr(tagged_ptr_new), untagged_ptr_old,
|
||||
Min(new_size, static_cast<uptr>(meta->requested_size)));
|
||||
HwasanDeallocate(stack, tagged_ptr_old);
|
||||
}
|
||||
uptr memcpy_size = Min(new_size, old_size);
|
||||
void *new_p = HwasanAllocate(stack, new_size, alignment, false /*zeroise*/);
|
||||
if (new_p) {
|
||||
internal_memcpy(new_p, old_p, memcpy_size);
|
||||
HwasanDeallocate(stack, old_p);
|
||||
}
|
||||
return new_p;
|
||||
return tagged_ptr_new;
|
||||
}
|
||||
|
||||
void *HwasanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
|
||||
static void *HwasanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
|
||||
if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
|
||||
if (AllocatorMayReturnNull())
|
||||
return nullptr;
|
||||
@ -250,12 +294,18 @@ HwasanChunkView FindHeapChunkByAddress(uptr address) {
|
||||
return HwasanChunkView(reinterpret_cast<uptr>(block), metadata);
|
||||
}
|
||||
|
||||
static uptr AllocationSize(const void *user_ptr) {
|
||||
const void *p = GetAddressFromPointer(user_ptr);
|
||||
if (!p) return 0;
|
||||
const void *beg = allocator.GetBlockBegin(p);
|
||||
if (beg != p) return 0;
|
||||
Metadata *b = (Metadata *)allocator.GetMetaData(p);
|
||||
static uptr AllocationSize(const void *tagged_ptr) {
|
||||
const void *untagged_ptr = UntagPtr(tagged_ptr);
|
||||
if (!untagged_ptr) return 0;
|
||||
const void *beg = allocator.GetBlockBegin(untagged_ptr);
|
||||
Metadata *b = (Metadata *)allocator.GetMetaData(untagged_ptr);
|
||||
if (b->right_aligned) {
|
||||
if (beg != reinterpret_cast<void *>(RoundDownTo(
|
||||
reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment)))
|
||||
return 0;
|
||||
} else {
|
||||
if (beg != untagged_ptr) return 0;
|
||||
}
|
||||
return b->requested_size;
|
||||
}
|
||||
|
||||
@ -270,6 +320,14 @@ void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
|
||||
void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack) {
|
||||
if (!ptr)
|
||||
return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
// A tag of 0 means that this is a system allocator allocation, so we must use
|
||||
// the system allocator to realloc it.
|
||||
if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0)
|
||||
return REAL(realloc)(ptr, size);
|
||||
#endif
|
||||
|
||||
if (size == 0) {
|
||||
HwasanDeallocate(stack, ptr);
|
||||
return nullptr;
|
||||
@ -331,6 +389,17 @@ int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hwasan_free(void *ptr, StackTrace *stack) {
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
// A tag of 0 means that this is a system allocator allocation, so we must use
|
||||
// the system allocator to free it.
|
||||
if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0)
|
||||
return REAL(free)(ptr);
|
||||
#endif
|
||||
|
||||
return HwasanDeallocate(stack, ptr);
|
||||
}
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
using namespace __hwasan;
|
||||
@ -340,6 +409,15 @@ void __hwasan_enable_allocator_tagging() {
|
||||
}
|
||||
|
||||
void __hwasan_disable_allocator_tagging() {
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
// Allocator tagging must be enabled for the system allocator fallback to work
|
||||
// correctly. This means that we can't disable it at runtime if it was enabled
|
||||
// at startup since that might result in our deallocations going to the system
|
||||
// allocator. If tagging was disabled at startup we avoid this problem by
|
||||
// disabling the fallback altogether.
|
||||
CHECK(flags()->disable_allocator_tagging);
|
||||
#endif
|
||||
|
||||
atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 0);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- hwasan_allocator.h ----------------------------------------*- C++ -*-===//
|
||||
//===-- hwasan_allocator.h --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -14,35 +14,73 @@
|
||||
#ifndef HWASAN_ALLOCATOR_H
|
||||
#define HWASAN_ALLOCATOR_H
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_allocator.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_checks.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_interface.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_report.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_ring_buffer.h"
|
||||
#include "hwasan_poisoning.h"
|
||||
|
||||
#if !defined(__aarch64__) && !defined(__x86_64__)
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
DECLARE_REAL(void *, realloc, void *ptr, uptr size)
|
||||
DECLARE_REAL(void, free, void *ptr)
|
||||
#endif
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
struct HwasanThreadLocalMallocStorage {
|
||||
uptr quarantine_cache[16];
|
||||
// Allocator cache contains atomic_uint64_t which must be 8-byte aligned.
|
||||
ALIGNED(8) uptr allocator_cache[96 * (512 * 8 + 16)]; // Opaque.
|
||||
void CommitBack();
|
||||
|
||||
private:
|
||||
// These objects are allocated via mmap() and are zero-initialized.
|
||||
HwasanThreadLocalMallocStorage() {}
|
||||
struct Metadata {
|
||||
u32 requested_size : 31; // sizes are < 2G.
|
||||
u32 right_aligned : 1;
|
||||
u32 alloc_context_id;
|
||||
};
|
||||
|
||||
struct Metadata;
|
||||
struct HwasanMapUnmapCallback {
|
||||
void OnMap(uptr p, uptr size) const { UpdateMemoryUsage(); }
|
||||
void OnUnmap(uptr p, uptr size) const {
|
||||
// We are about to unmap a chunk of user memory.
|
||||
// It can return as user-requested mmap() or another thread stack.
|
||||
// Make it accessible with zero-tagged pointer.
|
||||
TagMemory(p, size, 0);
|
||||
}
|
||||
};
|
||||
|
||||
static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G
|
||||
|
||||
struct AP64 {
|
||||
static const uptr kSpaceBeg = ~0ULL;
|
||||
static const uptr kSpaceSize = 0x2000000000ULL;
|
||||
static const uptr kMetadataSize = sizeof(Metadata);
|
||||
typedef __sanitizer::VeryDenseSizeClassMap SizeClassMap;
|
||||
using AddressSpaceView = LocalAddressSpaceView;
|
||||
typedef HwasanMapUnmapCallback MapUnmapCallback;
|
||||
static const uptr kFlags = 0;
|
||||
};
|
||||
typedef SizeClassAllocator64<AP64> PrimaryAllocator;
|
||||
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
|
||||
typedef LargeMmapAllocator<HwasanMapUnmapCallback> SecondaryAllocator;
|
||||
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
|
||||
SecondaryAllocator> Allocator;
|
||||
|
||||
void AllocatorSwallowThreadLocalCache(AllocatorCache *cache);
|
||||
|
||||
class HwasanChunkView {
|
||||
public:
|
||||
HwasanChunkView() : block_(0), metadata_(nullptr) {}
|
||||
HwasanChunkView(uptr block, Metadata *metadata)
|
||||
: block_(block), metadata_(metadata) {}
|
||||
bool IsValid() const; // Checks if it points to a valid allocated chunk
|
||||
bool IsAllocated() const; // Checks if the memory is currently allocated
|
||||
uptr Beg() const; // First byte of user memory
|
||||
uptr End() const; // Last byte of user memory
|
||||
uptr UsedSize() const; // Size requested by the user
|
||||
uptr ActualSize() const; // Size allocated by the allocator.
|
||||
u32 GetAllocStackId() const;
|
||||
u32 GetFreeStackId() const;
|
||||
bool FromSmallHeap() const;
|
||||
private:
|
||||
uptr block_;
|
||||
Metadata *const metadata_;
|
||||
@ -50,6 +88,21 @@ class HwasanChunkView {
|
||||
|
||||
HwasanChunkView FindHeapChunkByAddress(uptr address);
|
||||
|
||||
// Information about one (de)allocation that happened in the past.
|
||||
// These are recorded in a thread-local ring buffer.
|
||||
// TODO: this is currently 24 bytes (20 bytes + alignment).
|
||||
// Compress it to 16 bytes or extend it to be more useful.
|
||||
struct HeapAllocationRecord {
|
||||
uptr tagged_addr;
|
||||
u32 alloc_context_id;
|
||||
u32 free_context_id;
|
||||
u32 requested_size;
|
||||
};
|
||||
|
||||
typedef RingBuffer<HeapAllocationRecord> HeapAllocationsRingBuffer;
|
||||
|
||||
void GetAllocatorStats(AllocatorStatCounters s);
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
#endif // HWASAN_ALLOCATOR_H
|
||||
|
80
contrib/compiler-rt/lib/hwasan/hwasan_checks.h
Normal file
80
contrib/compiler-rt/lib/hwasan/hwasan_checks.h
Normal file
@ -0,0 +1,80 @@
|
||||
//===-- hwasan_checks.h -----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of HWAddressSanitizer.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef HWASAN_CHECKS_H
|
||||
#define HWASAN_CHECKS_H
|
||||
|
||||
#include "hwasan_mapping.h"
|
||||
|
||||
namespace __hwasan {
|
||||
template <unsigned X>
|
||||
__attribute__((always_inline)) static void SigTrap(uptr p) {
|
||||
#if defined(__aarch64__)
|
||||
(void)p;
|
||||
// 0x900 is added to do not interfere with the kernel use of lower values of
|
||||
// brk immediate.
|
||||
// FIXME: Add a constraint to put the pointer into x0, the same as x86 branch.
|
||||
asm("brk %0\n\t" ::"n"(0x900 + X));
|
||||
#elif defined(__x86_64__)
|
||||
// INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes
|
||||
// total. The pointer is passed via rdi.
|
||||
// 0x40 is added as a safeguard, to help distinguish our trap from others and
|
||||
// to avoid 0 offsets in the command (otherwise it'll be reduced to a
|
||||
// different nop command, the three bytes one).
|
||||
asm volatile(
|
||||
"int3\n"
|
||||
"nopl %c0(%%rax)\n" ::"n"(0x40 + X),
|
||||
"D"(p));
|
||||
#else
|
||||
// FIXME: not always sigill.
|
||||
__builtin_trap();
|
||||
#endif
|
||||
// __builtin_unreachable();
|
||||
}
|
||||
|
||||
enum class ErrorAction { Abort, Recover };
|
||||
enum class AccessType { Load, Store };
|
||||
|
||||
template <ErrorAction EA, AccessType AT, unsigned LogSize>
|
||||
__attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) {
|
||||
tag_t ptr_tag = GetTagFromPointer(p);
|
||||
uptr ptr_raw = p & ~kAddressTagMask;
|
||||
tag_t mem_tag = *(tag_t *)MemToShadow(ptr_raw);
|
||||
if (UNLIKELY(ptr_tag != mem_tag)) {
|
||||
SigTrap<0x20 * (EA == ErrorAction::Recover) +
|
||||
0x10 * (AT == AccessType::Store) + LogSize>(p);
|
||||
if (EA == ErrorAction::Abort)
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <ErrorAction EA, AccessType AT>
|
||||
__attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
|
||||
uptr sz) {
|
||||
if (sz == 0)
|
||||
return;
|
||||
tag_t ptr_tag = GetTagFromPointer(p);
|
||||
uptr ptr_raw = p & ~kAddressTagMask;
|
||||
tag_t *shadow_first = (tag_t *)MemToShadow(ptr_raw);
|
||||
tag_t *shadow_last = (tag_t *)MemToShadow(ptr_raw + sz - 1);
|
||||
for (tag_t *t = shadow_first; t <= shadow_last; ++t)
|
||||
if (UNLIKELY(ptr_tag != *t)) {
|
||||
SigTrap<0x20 * (EA == ErrorAction::Recover) +
|
||||
0x10 * (AT == AccessType::Store) + 0xf>(p);
|
||||
if (EA == ErrorAction::Abort)
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
} // end namespace __hwasan
|
||||
|
||||
#endif // HWASAN_CHECKS_H
|
@ -13,6 +13,7 @@
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "hwasan.h"
|
||||
#include "hwasan_dynamic_shadow.h"
|
||||
#include "hwasan_mapping.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
@ -35,12 +36,16 @@ static void UnmapFromTo(uptr from, uptr to) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an address aligned to 8 pages, such that one page on the left and
|
||||
// shadow_size_bytes bytes on the right of it are mapped r/o.
|
||||
// Returns an address aligned to kShadowBaseAlignment, such that
|
||||
// 2**kShadowBaseAlingment on the left and shadow_size_bytes bytes on the right
|
||||
// of it are mapped no access.
|
||||
static uptr MapDynamicShadow(uptr shadow_size_bytes) {
|
||||
const uptr granularity = GetMmapGranularity();
|
||||
const uptr alignment = granularity * SHADOW_GRANULARITY;
|
||||
const uptr left_padding = granularity;
|
||||
const uptr min_alignment = granularity << kShadowScale;
|
||||
const uptr alignment = 1ULL << kShadowBaseAlignment;
|
||||
CHECK_GE(alignment, min_alignment);
|
||||
|
||||
const uptr left_padding = 1ULL << kShadowBaseAlignment;
|
||||
const uptr shadow_size =
|
||||
RoundUpTo(shadow_size_bytes, granularity);
|
||||
const uptr map_size = shadow_size + left_padding + alignment;
|
||||
@ -58,8 +63,7 @@ static uptr MapDynamicShadow(uptr shadow_size_bytes) {
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
#if HWASAN_PREMAP_SHADOW
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
extern "C" {
|
||||
|
||||
INTERFACE_ATTRIBUTE void __hwasan_shadow();
|
||||
@ -117,16 +121,22 @@ void __hwasan_shadow();
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // HWASAN_PREMAP_SHADOW
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
|
||||
#if HWASAN_PREMAP_SHADOW
|
||||
if (IsPremapShadowAvailable())
|
||||
return FindPremappedShadowStart(shadow_size_bytes);
|
||||
#endif
|
||||
return MapDynamicShadow(shadow_size_bytes);
|
||||
}
|
||||
|
||||
} // namespace __hwasan
|
||||
#else
|
||||
namespace __hwasan {
|
||||
|
||||
uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
|
||||
return MapDynamicShadow(shadow_size_bytes);
|
||||
}
|
||||
|
||||
} // namespace __hwasan
|
||||
#
|
||||
#endif // SANITIZER_ANDROID
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- hwasan_flags.h --------------------------------------------*- C++ -*-===//
|
||||
//===-- hwasan_flags.h ------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
|
@ -17,9 +17,10 @@
|
||||
// HWASAN_FLAG(Type, Name, DefaultValue, Description)
|
||||
// See COMMON_FLAG in sanitizer_flags.inc for more details.
|
||||
|
||||
HWASAN_FLAG(bool, verbose_threads, false,
|
||||
"inform on thread creation/destruction")
|
||||
HWASAN_FLAG(bool, tag_in_malloc, true, "")
|
||||
HWASAN_FLAG(bool, tag_in_free, true, "")
|
||||
HWASAN_FLAG(bool, retag_in_realloc, true, "")
|
||||
HWASAN_FLAG(bool, print_stats, false, "")
|
||||
HWASAN_FLAG(bool, halt_on_error, true, "")
|
||||
HWASAN_FLAG(bool, atexit, false, "")
|
||||
@ -31,3 +32,57 @@ HWASAN_FLAG(bool, disable_allocator_tagging, false, "")
|
||||
// If false, use simple increment of a thread local counter to generate new
|
||||
// tags.
|
||||
HWASAN_FLAG(bool, random_tags, true, "")
|
||||
|
||||
HWASAN_FLAG(
|
||||
int, max_malloc_fill_size, 0x1000, // By default, fill only the first 4K.
|
||||
"HWASan allocator flag. max_malloc_fill_size is the maximal amount of "
|
||||
"bytes that will be filled with malloc_fill_byte on malloc.")
|
||||
|
||||
// Rules for malloc alignment on aarch64:
|
||||
// * If the size is 16-aligned, then malloc should return 16-aligned memory.
|
||||
// * Otherwise, malloc should return 8-alignment memory.
|
||||
// So,
|
||||
// * If the size is 16-aligned, we don't need to do anything.
|
||||
// * Otherwise we don't have to obey 16-alignment, just the 8-alignment.
|
||||
// * We may want to break the 8-alignment rule to catch more buffer overflows
|
||||
// but this will break valid code in some rare cases, like this:
|
||||
// struct Foo {
|
||||
// // accessed via atomic instructions that require 8-alignment.
|
||||
// std::atomic<int64_t> atomic_stuff;
|
||||
// ...
|
||||
// char vla[1]; // the actual size of vla could be anything.
|
||||
// }
|
||||
// Which means that the safe values for malloc_align_right are 0, 8, 9,
|
||||
// and the values 1 and 2 may require changes in otherwise valid code.
|
||||
|
||||
HWASAN_FLAG(
|
||||
int, malloc_align_right, 0, // off by default
|
||||
"HWASan allocator flag. "
|
||||
"0 (default): allocations are always aligned left to 16-byte boundary; "
|
||||
"1: allocations are sometimes aligned right to 1-byte boundary (risky); "
|
||||
"2: allocations are always aligned right to 1-byte boundary (risky); "
|
||||
"8: allocations are sometimes aligned right to 8-byte boundary; "
|
||||
"9: allocations are always aligned right to 8-byte boundary."
|
||||
)
|
||||
HWASAN_FLAG(bool, free_checks_tail_magic, 1,
|
||||
"If set, free() will check the magic values "
|
||||
"to the right of the allocated object "
|
||||
"if the allocation size is not a divident of the granule size")
|
||||
HWASAN_FLAG(
|
||||
int, max_free_fill_size, 0,
|
||||
"HWASan allocator flag. max_free_fill_size is the maximal amount of "
|
||||
"bytes that will be filled with free_fill_byte during free.")
|
||||
HWASAN_FLAG(int, malloc_fill_byte, 0xbe,
|
||||
"Value used to fill the newly allocated memory.")
|
||||
HWASAN_FLAG(int, free_fill_byte, 0x55,
|
||||
"Value used to fill deallocated memory.")
|
||||
HWASAN_FLAG(int, heap_history_size, 1023,
|
||||
"The number of heap (de)allocations remembered per thread. "
|
||||
"Affects the quality of heap-related reports, but not the ability "
|
||||
"to find bugs.")
|
||||
HWASAN_FLAG(bool, export_memory_stats, true,
|
||||
"Export up-to-date memory stats through /proc")
|
||||
HWASAN_FLAG(int, stack_history_size, 1024,
|
||||
"The number of stack frames remembered per thread. "
|
||||
"Affects the quality of stack-related reports, but not the ability "
|
||||
"to find bugs.")
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- hwasan_interceptors.cc ----------------------------------------------===//
|
||||
//===-- hwasan_interceptors.cc --------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "hwasan.h"
|
||||
#include "hwasan_allocator.h"
|
||||
#include "hwasan_mapping.h"
|
||||
#include "hwasan_thread.h"
|
||||
#include "hwasan_poisoning.h"
|
||||
@ -44,24 +45,19 @@ using __sanitizer::atomic_load;
|
||||
using __sanitizer::atomic_store;
|
||||
using __sanitizer::atomic_uintptr_t;
|
||||
|
||||
DECLARE_REAL(SIZE_T, strlen, const char *s)
|
||||
DECLARE_REAL(SIZE_T, strnlen, const char *s, SIZE_T maxlen)
|
||||
DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n)
|
||||
DECLARE_REAL(void *, memset, void *dest, int c, uptr n)
|
||||
|
||||
bool IsInInterceptorScope() {
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
Thread *t = GetCurrentThread();
|
||||
return t && t->InInterceptorScope();
|
||||
}
|
||||
|
||||
struct InterceptorScope {
|
||||
InterceptorScope() {
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
Thread *t = GetCurrentThread();
|
||||
if (t)
|
||||
t->EnterInterceptorScope();
|
||||
}
|
||||
~InterceptorScope() {
|
||||
HwasanThread *t = GetCurrentThread();
|
||||
Thread *t = GetCurrentThread();
|
||||
if (t)
|
||||
t->LeaveInterceptorScope();
|
||||
}
|
||||
@ -92,66 +88,24 @@ static void *AllocateFromLocalPool(uptr size_in_bytes) {
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
#define HWASAN_READ_RANGE(ctx, offset, size) \
|
||||
CHECK_UNPOISONED(offset, size)
|
||||
#define HWASAN_WRITE_RANGE(ctx, offset, size) \
|
||||
CHECK_UNPOISONED(offset, size)
|
||||
|
||||
|
||||
|
||||
// Check that [x, x+n) range is unpoisoned.
|
||||
#define CHECK_UNPOISONED_0(x, n) \
|
||||
do { \
|
||||
sptr __offset = __hwasan_test_shadow(x, n); \
|
||||
if (__hwasan::IsInSymbolizer()) break; \
|
||||
if (__offset >= 0) { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
(void)sp; \
|
||||
ReportInvalidAccessInsideAddressRange(__func__, x, n, __offset); \
|
||||
__hwasan::PrintWarning(pc, bp); \
|
||||
if (__hwasan::flags()->halt_on_error) { \
|
||||
Printf("Exiting\n"); \
|
||||
Die(); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Check that [x, x+n) range is unpoisoned unless we are in a nested
|
||||
// interceptor.
|
||||
#define CHECK_UNPOISONED(x, n) \
|
||||
do { \
|
||||
if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_UNPOISONED_STRING_OF_LEN(x, len, n) \
|
||||
CHECK_UNPOISONED((x), \
|
||||
common_flags()->strict_string_checks ? (len) + 1 : (n) )
|
||||
|
||||
|
||||
INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) {
|
||||
int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
CHECK_NE(memptr, 0);
|
||||
int res = hwasan_posix_memalign(memptr, alignment, size, &stack);
|
||||
return res;
|
||||
}
|
||||
|
||||
#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
|
||||
INTERCEPTOR(void *, memalign, SIZE_T alignment, SIZE_T size) {
|
||||
void * __sanitizer_memalign(uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_memalign(alignment, size, &stack);
|
||||
}
|
||||
#define HWASAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign)
|
||||
#else
|
||||
#define HWASAN_MAYBE_INTERCEPT_MEMALIGN
|
||||
#endif
|
||||
|
||||
INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) {
|
||||
void * __sanitizer_aligned_alloc(uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_aligned_alloc(alignment, size, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) {
|
||||
void * __sanitizer___libc_memalign(uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
void *ptr = hwasan_memalign(alignment, size, &stack);
|
||||
if (ptr)
|
||||
@ -159,80 +113,47 @@ INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, valloc, SIZE_T size) {
|
||||
void * __sanitizer_valloc(uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_valloc(size, &stack);
|
||||
}
|
||||
|
||||
#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
|
||||
INTERCEPTOR(void *, pvalloc, SIZE_T size) {
|
||||
void * __sanitizer_pvalloc(uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_pvalloc(size, &stack);
|
||||
}
|
||||
#define HWASAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc)
|
||||
#else
|
||||
#define HWASAN_MAYBE_INTERCEPT_PVALLOC
|
||||
#endif
|
||||
|
||||
INTERCEPTOR(void, free, void *ptr) {
|
||||
void __sanitizer_free(void *ptr) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
|
||||
HwasanDeallocate(&stack, ptr);
|
||||
hwasan_free(ptr, &stack);
|
||||
}
|
||||
|
||||
#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
|
||||
INTERCEPTOR(void, cfree, void *ptr) {
|
||||
void __sanitizer_cfree(void *ptr) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
|
||||
HwasanDeallocate(&stack, ptr);
|
||||
hwasan_free(ptr, &stack);
|
||||
}
|
||||
#define HWASAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree)
|
||||
#else
|
||||
#define HWASAN_MAYBE_INTERCEPT_CFREE
|
||||
#endif
|
||||
|
||||
INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
|
||||
uptr __sanitizer_malloc_usable_size(const void *ptr) {
|
||||
return __sanitizer_get_allocated_size(ptr);
|
||||
}
|
||||
|
||||
#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
|
||||
// This function actually returns a struct by value, but we can't unpoison a
|
||||
// temporary! The following is equivalent on all supported platforms but
|
||||
// aarch64 (which uses a different register for sret value). We have a test
|
||||
// to confirm that.
|
||||
INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) {
|
||||
#ifdef __aarch64__
|
||||
uptr r8;
|
||||
asm volatile("mov %0,x8" : "=r" (r8));
|
||||
sret = reinterpret_cast<__sanitizer_mallinfo*>(r8);
|
||||
#endif
|
||||
REAL(memset)(sret, 0, sizeof(*sret));
|
||||
struct __sanitizer_struct_mallinfo __sanitizer_mallinfo() {
|
||||
__sanitizer_struct_mallinfo sret;
|
||||
internal_memset(&sret, 0, sizeof(sret));
|
||||
return sret;
|
||||
}
|
||||
#define HWASAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo)
|
||||
#else
|
||||
#define HWASAN_MAYBE_INTERCEPT_MALLINFO
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
|
||||
INTERCEPTOR(int, mallopt, int cmd, int value) {
|
||||
return -1;
|
||||
int __sanitizer_mallopt(int cmd, int value) {
|
||||
return 0;
|
||||
}
|
||||
#define HWASAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt)
|
||||
#else
|
||||
#define HWASAN_MAYBE_INTERCEPT_MALLOPT
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
|
||||
INTERCEPTOR(void, malloc_stats, void) {
|
||||
void __sanitizer_malloc_stats(void) {
|
||||
// FIXME: implement, but don't call REAL(malloc_stats)!
|
||||
}
|
||||
#define HWASAN_MAYBE_INTERCEPT_MALLOC_STATS INTERCEPT_FUNCTION(malloc_stats)
|
||||
#else
|
||||
#define HWASAN_MAYBE_INTERCEPT_MALLOC_STATS
|
||||
#endif
|
||||
|
||||
|
||||
INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) {
|
||||
void * __sanitizer_calloc(uptr nmemb, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (UNLIKELY(!hwasan_inited))
|
||||
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
|
||||
@ -240,7 +161,7 @@ INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) {
|
||||
return hwasan_calloc(nmemb, size, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
|
||||
void * __sanitizer_realloc(void *ptr, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
|
||||
uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
|
||||
@ -258,7 +179,7 @@ INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
|
||||
return hwasan_realloc(ptr, size, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, malloc, SIZE_T size) {
|
||||
void * __sanitizer_malloc(uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (UNLIKELY(!hwasan_init_is_running))
|
||||
ENSURE_HWASAN_INITED();
|
||||
@ -268,48 +189,44 @@ INTERCEPTOR(void *, malloc, SIZE_T size) {
|
||||
return hwasan_malloc(size, &stack);
|
||||
}
|
||||
|
||||
template <class Mmap>
|
||||
static void *mmap_interceptor(Mmap real_mmap, void *addr, SIZE_T sz, int prot,
|
||||
int flags, int fd, OFF64_T off) {
|
||||
if (addr && !MEM_IS_APP(addr)) {
|
||||
if (flags & map_fixed) {
|
||||
errno = errno_EINVAL;
|
||||
return (void *)-1;
|
||||
} else {
|
||||
addr = nullptr;
|
||||
}
|
||||
}
|
||||
return real_mmap(addr, sz, prot, flags, fd, off);
|
||||
}
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
#define INTERCEPTOR_ALIAS(RET, FN, ARGS...) \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE RET WRAP(FN)(ARGS) \
|
||||
ALIAS("__sanitizer_" #FN); \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE RET FN( \
|
||||
ARGS) ALIAS("__sanitizer_" #FN)
|
||||
|
||||
extern "C" int pthread_attr_init(void *attr);
|
||||
extern "C" int pthread_attr_destroy(void *attr);
|
||||
INTERCEPTOR_ALIAS(int, posix_memalign, void **memptr, SIZE_T alignment,
|
||||
SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, aligned_alloc, SIZE_T alignment, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, __libc_memalign, SIZE_T alignment, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, valloc, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void, free, void *ptr);
|
||||
INTERCEPTOR_ALIAS(uptr, malloc_usable_size, const void *ptr);
|
||||
INTERCEPTOR_ALIAS(void *, calloc, SIZE_T nmemb, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, realloc, void *ptr, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, malloc, SIZE_T size);
|
||||
|
||||
static void *HwasanThreadStartFunc(void *arg) {
|
||||
HwasanThread *t = (HwasanThread *)arg;
|
||||
SetCurrentThread(t);
|
||||
return t->ThreadStart();
|
||||
}
|
||||
#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
|
||||
INTERCEPTOR_ALIAS(void *, memalign, SIZE_T alignment, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, pvalloc, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void, cfree, void *ptr);
|
||||
INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo);
|
||||
INTERCEPTOR_ALIAS(int, mallopt, int cmd, int value);
|
||||
INTERCEPTOR_ALIAS(void, malloc_stats, void);
|
||||
#endif
|
||||
#endif // HWASAN_WITH_INTERCEPTORS
|
||||
|
||||
INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
|
||||
void * param) {
|
||||
ENSURE_HWASAN_INITED(); // for GetTlsSize()
|
||||
__sanitizer_pthread_attr_t myattr;
|
||||
if (!attr) {
|
||||
pthread_attr_init(&myattr);
|
||||
attr = &myattr;
|
||||
}
|
||||
|
||||
AdjustStackSize(attr);
|
||||
|
||||
HwasanThread *t = HwasanThread::Create(callback, param);
|
||||
|
||||
int res = REAL(pthread_create)(th, attr, HwasanThreadStartFunc, t);
|
||||
|
||||
if (attr == &myattr)
|
||||
pthread_attr_destroy(&myattr);
|
||||
#if HWASAN_WITH_INTERCEPTORS && !defined(__aarch64__)
|
||||
INTERCEPTOR(int, pthread_create, void *th, void *attr,
|
||||
void *(*callback)(void *), void *param) {
|
||||
ScopedTaggingDisabler disabler;
|
||||
int res = REAL(pthread_create)(UntagPtr(th), UntagPtr(attr),
|
||||
callback, param);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void BeforeFork() {
|
||||
StackDepotLockAll();
|
||||
@ -341,137 +258,21 @@ int OnExit() {
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
// A version of CHECK_UNPOISONED using a saved scope value. Used in common
|
||||
// interceptors.
|
||||
#define CHECK_UNPOISONED_CTX(ctx, x, n) \
|
||||
do { \
|
||||
if (!((HwasanInterceptorContext *)ctx)->in_interceptor_scope) \
|
||||
CHECK_UNPOISONED_0(x, n); \
|
||||
} while (0)
|
||||
|
||||
#define HWASAN_INTERCEPT_FUNC(name) \
|
||||
do { \
|
||||
if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
|
||||
VReport(1, "HWAddressSanitizer: failed to intercept '" #name "'\n"); \
|
||||
} while (0)
|
||||
|
||||
#define HWASAN_INTERCEPT_FUNC_VER(name, ver) \
|
||||
do { \
|
||||
if ((!INTERCEPT_FUNCTION_VER(name, ver) || !REAL(name))) \
|
||||
VReport( \
|
||||
1, "HWAddressSanitizer: failed to intercept '" #name "@@" #ver "'\n"); \
|
||||
} while (0)
|
||||
|
||||
#define COMMON_INTERCEPT_FUNCTION(name) HWASAN_INTERCEPT_FUNC(name)
|
||||
#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
|
||||
HWASAN_INTERCEPT_FUNC_VER(name, ver)
|
||||
#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
|
||||
CHECK_UNPOISONED_CTX(ctx, ptr, size)
|
||||
#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
|
||||
CHECK_UNPOISONED_CTX(ctx, ptr, size)
|
||||
#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \
|
||||
HWASAN_WRITE_RANGE(ctx, ptr, size)
|
||||
#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
|
||||
if (hwasan_init_is_running) return REAL(func)(__VA_ARGS__); \
|
||||
ENSURE_HWASAN_INITED(); \
|
||||
HwasanInterceptorContext hwasan_ctx = {IsInInterceptorScope()}; \
|
||||
ctx = (void *)&hwasan_ctx; \
|
||||
(void)ctx; \
|
||||
InterceptorScope interceptor_scope;
|
||||
#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
|
||||
do { \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
|
||||
do { \
|
||||
} while (false) // FIXME
|
||||
#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
|
||||
do { \
|
||||
} while (false) // FIXME
|
||||
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
|
||||
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
|
||||
|
||||
#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
|
||||
if (HwasanThread *t = GetCurrentThread()) { \
|
||||
*begin = t->tls_begin(); \
|
||||
*end = t->tls_end(); \
|
||||
} else { \
|
||||
*begin = *end = 0; \
|
||||
}
|
||||
|
||||
#define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size) \
|
||||
{ \
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, memset, dst, v, size); \
|
||||
if (common_flags()->intercept_intrin && \
|
||||
MEM_IS_APP(GetAddressFromPointer(dst))) \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \
|
||||
return REAL(memset)(dst, v, size); \
|
||||
}
|
||||
|
||||
#define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, length, prot, flags, fd, \
|
||||
offset) \
|
||||
do { \
|
||||
return mmap_interceptor(REAL(mmap), addr, length, prot, flags, fd, \
|
||||
offset); \
|
||||
} while (false)
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform_interceptors.h"
|
||||
#include "sanitizer_common/sanitizer_common_interceptors.inc"
|
||||
#include "sanitizer_common/sanitizer_signal_interceptors.inc"
|
||||
|
||||
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s)
|
||||
#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \
|
||||
do { \
|
||||
(void)(p); \
|
||||
(void)(s); \
|
||||
} while (false)
|
||||
#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
|
||||
do { \
|
||||
(void)(p); \
|
||||
(void)(s); \
|
||||
} while (false)
|
||||
#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
|
||||
do { \
|
||||
(void)(p); \
|
||||
(void)(s); \
|
||||
} while (false)
|
||||
#include "sanitizer_common/sanitizer_common_syscalls.inc"
|
||||
#include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
|
||||
|
||||
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
void InitializeInterceptors() {
|
||||
static int inited = 0;
|
||||
CHECK_EQ(inited, 0);
|
||||
InitializeCommonInterceptors();
|
||||
InitializeSignalInterceptors();
|
||||
|
||||
INTERCEPT_FUNCTION(posix_memalign);
|
||||
HWASAN_MAYBE_INTERCEPT_MEMALIGN;
|
||||
INTERCEPT_FUNCTION(__libc_memalign);
|
||||
INTERCEPT_FUNCTION(valloc);
|
||||
HWASAN_MAYBE_INTERCEPT_PVALLOC;
|
||||
INTERCEPT_FUNCTION(malloc);
|
||||
INTERCEPT_FUNCTION(calloc);
|
||||
INTERCEPT_FUNCTION(fork);
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
#if !defined(__aarch64__)
|
||||
INTERCEPT_FUNCTION(pthread_create);
|
||||
#endif
|
||||
INTERCEPT_FUNCTION(realloc);
|
||||
INTERCEPT_FUNCTION(free);
|
||||
HWASAN_MAYBE_INTERCEPT_CFREE;
|
||||
INTERCEPT_FUNCTION(malloc_usable_size);
|
||||
HWASAN_MAYBE_INTERCEPT_MALLINFO;
|
||||
HWASAN_MAYBE_INTERCEPT_MALLOPT;
|
||||
HWASAN_MAYBE_INTERCEPT_MALLOC_STATS;
|
||||
INTERCEPT_FUNCTION(pthread_create);
|
||||
INTERCEPT_FUNCTION(fork);
|
||||
#endif
|
||||
|
||||
inited = 1;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- hwasan_interface_internal.h -------------------------------*- C++ -*-===//
|
||||
//===-- hwasan_interface_internal.h -----------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -16,9 +16,13 @@
|
||||
#define HWASAN_INTERFACE_INTERNAL_H
|
||||
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_platform_limits_posix.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __hwasan_shadow_init();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __hwasan_init();
|
||||
|
||||
@ -32,6 +36,9 @@ using __sanitizer::u32;
|
||||
using __sanitizer::u16;
|
||||
using __sanitizer::u8;
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __hwasan_init_frames(uptr, uptr);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
extern uptr __hwasan_shadow_memory_dynamic_address;
|
||||
|
||||
@ -90,6 +97,9 @@ void __hwasan_store16_noabort(uptr);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __hwasan_tag_memory(uptr p, u8 tag, uptr sz);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __hwasan_tag_pointer(uptr p, u8 tag);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
u8 __hwasan_generate_tag();
|
||||
|
||||
@ -104,6 +114,9 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __hwasan_print_shadow(const void *x, uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __hwasan_handle_longjmp(const void *sp_dst);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
u16 __sanitizer_unaligned_load16(const uu16 *p);
|
||||
|
||||
@ -128,6 +141,66 @@ void __hwasan_enable_allocator_tagging();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __hwasan_disable_allocator_tagging();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __hwasan_thread_enter();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __hwasan_thread_exit();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __hwasan_print_memory_usage();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void * __sanitizer_memalign(uptr alignment, uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void * __sanitizer_aligned_alloc(uptr alignment, uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void * __sanitizer___libc_memalign(uptr alignment, uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void * __sanitizer_valloc(uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void * __sanitizer_pvalloc(uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_free(void *ptr);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_cfree(void *ptr);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __sanitizer_malloc_usable_size(const void *ptr);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
__hwasan::__sanitizer_struct_mallinfo __sanitizer_mallinfo();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __sanitizer_mallopt(int cmd, int value);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_malloc_stats(void);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void * __sanitizer_calloc(uptr nmemb, uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void * __sanitizer_realloc(void *ptr, uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void * __sanitizer_malloc(uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__hwasan_memcpy(void *dst, const void *src, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__hwasan_memset(void *s, int c, uptr n);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__hwasan_memmove(void *dest, const void *src, uptr n);
|
||||
} // extern "C"
|
||||
|
||||
#endif // HWASAN_INTERFACE_INTERNAL_H
|
||||
|
@ -22,7 +22,9 @@
|
||||
#include "hwasan_mapping.h"
|
||||
#include "hwasan_report.h"
|
||||
#include "hwasan_thread.h"
|
||||
#include "hwasan_thread_list.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
#include <pthread.h>
|
||||
@ -37,6 +39,11 @@
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_procmaps.h"
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS && !SANITIZER_ANDROID
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
THREADLOCAL uptr __hwasan_tls;
|
||||
#endif
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
static void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
|
||||
@ -51,8 +58,6 @@ static void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
|
||||
size);
|
||||
Abort();
|
||||
}
|
||||
if (common_flags()->no_huge_pages_for_shadow) NoHugePagesInRegion(beg, size);
|
||||
if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(beg, size);
|
||||
}
|
||||
|
||||
static void ProtectGap(uptr addr, uptr size) {
|
||||
@ -103,55 +108,31 @@ static void PrintAddressSpaceLayout() {
|
||||
else
|
||||
CHECK_EQ(kHighShadowEnd + 1, kHighMemStart);
|
||||
PrintRange(kHighShadowStart, kHighShadowEnd, "HighShadow");
|
||||
if (SHADOW_OFFSET) {
|
||||
if (kLowShadowEnd + 1 < kHighShadowStart)
|
||||
PrintRange(kLowShadowEnd + 1, kHighShadowStart - 1, "ShadowGap");
|
||||
else
|
||||
CHECK_EQ(kLowMemEnd + 1, kHighShadowStart);
|
||||
PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow");
|
||||
if (kLowMemEnd + 1 < kLowShadowStart)
|
||||
PrintRange(kLowMemEnd + 1, kLowShadowStart - 1, "ShadowGap");
|
||||
else
|
||||
CHECK_EQ(kLowMemEnd + 1, kLowShadowStart);
|
||||
PrintRange(kLowMemStart, kLowMemEnd, "LowMem");
|
||||
CHECK_EQ(0, kLowMemStart);
|
||||
} else {
|
||||
if (kLowMemEnd + 1 < kHighShadowStart)
|
||||
PrintRange(kLowMemEnd + 1, kHighShadowStart - 1, "ShadowGap");
|
||||
else
|
||||
CHECK_EQ(kLowMemEnd + 1, kHighShadowStart);
|
||||
PrintRange(kLowMemStart, kLowMemEnd, "LowMem");
|
||||
CHECK_EQ(kLowShadowEnd + 1, kLowMemStart);
|
||||
PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow");
|
||||
PrintRange(0, kLowShadowStart - 1, "ShadowGap");
|
||||
}
|
||||
if (kLowShadowEnd + 1 < kHighShadowStart)
|
||||
PrintRange(kLowShadowEnd + 1, kHighShadowStart - 1, "ShadowGap");
|
||||
else
|
||||
CHECK_EQ(kLowMemEnd + 1, kHighShadowStart);
|
||||
PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow");
|
||||
if (kLowMemEnd + 1 < kLowShadowStart)
|
||||
PrintRange(kLowMemEnd + 1, kLowShadowStart - 1, "ShadowGap");
|
||||
else
|
||||
CHECK_EQ(kLowMemEnd + 1, kLowShadowStart);
|
||||
PrintRange(kLowMemStart, kLowMemEnd, "LowMem");
|
||||
CHECK_EQ(0, kLowMemStart);
|
||||
}
|
||||
|
||||
static uptr GetHighMemEnd() {
|
||||
// HighMem covers the upper part of the address space.
|
||||
uptr max_address = GetMaxUserVirtualAddress();
|
||||
if (SHADOW_OFFSET)
|
||||
// Adjust max address to make sure that kHighMemEnd and kHighMemStart are
|
||||
// properly aligned:
|
||||
max_address |= SHADOW_GRANULARITY * GetMmapGranularity() - 1;
|
||||
// Adjust max address to make sure that kHighMemEnd and kHighMemStart are
|
||||
// properly aligned:
|
||||
max_address |= (GetMmapGranularity() << kShadowScale) - 1;
|
||||
return max_address;
|
||||
}
|
||||
|
||||
static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
|
||||
// Set the shadow memory address to uninitialized.
|
||||
__hwasan_shadow_memory_dynamic_address = kDefaultShadowSentinel;
|
||||
uptr shadow_start = SHADOW_OFFSET;
|
||||
// Detect if a dynamic shadow address must be used and find the available
|
||||
// location when necessary. When dynamic address is used, the macro
|
||||
// kLowShadowBeg expands to __hwasan_shadow_memory_dynamic_address which
|
||||
// was just set to kDefaultShadowSentinel.
|
||||
if (shadow_start == kDefaultShadowSentinel) {
|
||||
__hwasan_shadow_memory_dynamic_address = 0;
|
||||
CHECK_EQ(0, SHADOW_OFFSET);
|
||||
shadow_start = FindDynamicShadowStart(shadow_size_bytes);
|
||||
}
|
||||
// Update the shadow memory address (potentially) used by instrumentation.
|
||||
__hwasan_shadow_memory_dynamic_address = shadow_start;
|
||||
__hwasan_shadow_memory_dynamic_address =
|
||||
FindDynamicShadowStart(shadow_size_bytes);
|
||||
}
|
||||
|
||||
bool InitShadow() {
|
||||
@ -159,29 +140,23 @@ bool InitShadow() {
|
||||
kHighMemEnd = GetHighMemEnd();
|
||||
|
||||
// Determine shadow memory base offset.
|
||||
InitializeShadowBaseAddress(MEM_TO_SHADOW_SIZE(kHighMemEnd));
|
||||
InitializeShadowBaseAddress(MemToShadowSize(kHighMemEnd));
|
||||
|
||||
// Place the low memory first.
|
||||
if (SHADOW_OFFSET) {
|
||||
kLowMemEnd = SHADOW_OFFSET - 1;
|
||||
kLowMemStart = 0;
|
||||
} else {
|
||||
// LowMem covers as much of the first 4GB as possible.
|
||||
kLowMemEnd = (1UL << 32) - 1;
|
||||
kLowMemStart = MEM_TO_SHADOW(kLowMemEnd) + 1;
|
||||
}
|
||||
kLowMemEnd = __hwasan_shadow_memory_dynamic_address - 1;
|
||||
kLowMemStart = 0;
|
||||
|
||||
// Define the low shadow based on the already placed low memory.
|
||||
kLowShadowEnd = MEM_TO_SHADOW(kLowMemEnd);
|
||||
kLowShadowStart = SHADOW_OFFSET ? SHADOW_OFFSET : MEM_TO_SHADOW(kLowMemStart);
|
||||
kLowShadowEnd = MemToShadow(kLowMemEnd);
|
||||
kLowShadowStart = __hwasan_shadow_memory_dynamic_address;
|
||||
|
||||
// High shadow takes whatever memory is left up there (making sure it is not
|
||||
// interfering with low memory in the fixed case).
|
||||
kHighShadowEnd = MEM_TO_SHADOW(kHighMemEnd);
|
||||
kHighShadowStart = Max(kLowMemEnd, MEM_TO_SHADOW(kHighShadowEnd)) + 1;
|
||||
kHighShadowEnd = MemToShadow(kHighMemEnd);
|
||||
kHighShadowStart = Max(kLowMemEnd, MemToShadow(kHighShadowEnd)) + 1;
|
||||
|
||||
// High memory starts where allocated shadow allows.
|
||||
kHighMemStart = SHADOW_TO_MEM(kHighShadowStart);
|
||||
kHighMemStart = ShadowToMem(kHighShadowStart);
|
||||
|
||||
// Check the sanity of the defined memory ranges (there might be gaps).
|
||||
CHECK_EQ(kHighMemStart % GetMmapGranularity(), 0);
|
||||
@ -190,10 +165,7 @@ bool InitShadow() {
|
||||
CHECK_GT(kHighShadowStart, kLowMemEnd);
|
||||
CHECK_GT(kLowMemEnd, kLowMemStart);
|
||||
CHECK_GT(kLowShadowEnd, kLowShadowStart);
|
||||
if (SHADOW_OFFSET)
|
||||
CHECK_GT(kLowShadowStart, kLowMemEnd);
|
||||
else
|
||||
CHECK_GT(kLowMemEnd, kLowShadowStart);
|
||||
CHECK_GT(kLowShadowStart, kLowMemEnd);
|
||||
|
||||
if (Verbosity())
|
||||
PrintAddressSpaceLayout();
|
||||
@ -204,21 +176,43 @@ bool InitShadow() {
|
||||
|
||||
// Protect all the gaps.
|
||||
ProtectGap(0, Min(kLowMemStart, kLowShadowStart));
|
||||
if (SHADOW_OFFSET) {
|
||||
if (kLowMemEnd + 1 < kLowShadowStart)
|
||||
ProtectGap(kLowMemEnd + 1, kLowShadowStart - kLowMemEnd - 1);
|
||||
if (kLowShadowEnd + 1 < kHighShadowStart)
|
||||
ProtectGap(kLowShadowEnd + 1, kHighShadowStart - kLowShadowEnd - 1);
|
||||
} else {
|
||||
if (kLowMemEnd + 1 < kHighShadowStart)
|
||||
ProtectGap(kLowMemEnd + 1, kHighShadowStart - kLowMemEnd - 1);
|
||||
}
|
||||
if (kLowMemEnd + 1 < kLowShadowStart)
|
||||
ProtectGap(kLowMemEnd + 1, kLowShadowStart - kLowMemEnd - 1);
|
||||
if (kLowShadowEnd + 1 < kHighShadowStart)
|
||||
ProtectGap(kLowShadowEnd + 1, kHighShadowStart - kLowShadowEnd - 1);
|
||||
if (kHighShadowEnd + 1 < kHighMemStart)
|
||||
ProtectGap(kHighShadowEnd + 1, kHighMemStart - kHighShadowEnd - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitThreads() {
|
||||
CHECK(__hwasan_shadow_memory_dynamic_address);
|
||||
uptr guard_page_size = GetMmapGranularity();
|
||||
uptr thread_space_start =
|
||||
__hwasan_shadow_memory_dynamic_address - (1ULL << kShadowBaseAlignment);
|
||||
uptr thread_space_end =
|
||||
__hwasan_shadow_memory_dynamic_address - guard_page_size;
|
||||
ReserveShadowMemoryRange(thread_space_start, thread_space_end - 1,
|
||||
"hwasan threads");
|
||||
ProtectGap(thread_space_end,
|
||||
__hwasan_shadow_memory_dynamic_address - thread_space_end);
|
||||
InitThreadList(thread_space_start, thread_space_end - thread_space_start);
|
||||
}
|
||||
|
||||
static void MadviseShadowRegion(uptr beg, uptr end) {
|
||||
uptr size = end - beg + 1;
|
||||
if (common_flags()->no_huge_pages_for_shadow)
|
||||
NoHugePagesInRegion(beg, size);
|
||||
if (common_flags()->use_madv_dontdump)
|
||||
DontDumpShadowMemory(beg, size);
|
||||
}
|
||||
|
||||
void MadviseShadow() {
|
||||
MadviseShadowRegion(kLowShadowStart, kLowShadowEnd);
|
||||
MadviseShadowRegion(kHighShadowStart, kHighShadowEnd);
|
||||
}
|
||||
|
||||
bool MemIsApp(uptr p) {
|
||||
CHECK(GetTagFromPointer(p) == 0);
|
||||
return p >= kHighMemStart || (p >= kLowMemStart && p <= kLowMemEnd);
|
||||
@ -240,37 +234,81 @@ void InstallAtExitHandler() {
|
||||
|
||||
// ---------------------- TSD ---------------- {{{1
|
||||
|
||||
extern "C" void __hwasan_thread_enter() {
|
||||
hwasanThreadList().CreateCurrentThread();
|
||||
}
|
||||
|
||||
extern "C" void __hwasan_thread_exit() {
|
||||
Thread *t = GetCurrentThread();
|
||||
// Make sure that signal handler can not see a stale current thread pointer.
|
||||
atomic_signal_fence(memory_order_seq_cst);
|
||||
if (t)
|
||||
hwasanThreadList().ReleaseThread(t);
|
||||
}
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
static pthread_key_t tsd_key;
|
||||
static bool tsd_key_inited = false;
|
||||
|
||||
void HwasanTSDInit(void (*destructor)(void *tsd)) {
|
||||
CHECK(!tsd_key_inited);
|
||||
tsd_key_inited = true;
|
||||
CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
|
||||
}
|
||||
|
||||
HwasanThread *GetCurrentThread() {
|
||||
return (HwasanThread*)pthread_getspecific(tsd_key);
|
||||
}
|
||||
|
||||
void SetCurrentThread(HwasanThread *t) {
|
||||
// Make sure that HwasanTSDDtor gets called at the end.
|
||||
CHECK(tsd_key_inited);
|
||||
// Make sure we do not reset the current HwasanThread.
|
||||
CHECK_EQ(0, pthread_getspecific(tsd_key));
|
||||
pthread_setspecific(tsd_key, (void *)t);
|
||||
void HwasanTSDThreadInit() {
|
||||
if (tsd_key_inited)
|
||||
CHECK_EQ(0, pthread_setspecific(tsd_key,
|
||||
(void *)GetPthreadDestructorIterations()));
|
||||
}
|
||||
|
||||
void HwasanTSDDtor(void *tsd) {
|
||||
HwasanThread *t = (HwasanThread*)tsd;
|
||||
if (t->destructor_iterations_ > 1) {
|
||||
t->destructor_iterations_--;
|
||||
CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
|
||||
uptr iterations = (uptr)tsd;
|
||||
if (iterations > 1) {
|
||||
CHECK_EQ(0, pthread_setspecific(tsd_key, (void *)(iterations - 1)));
|
||||
return;
|
||||
}
|
||||
// Make sure that signal handler can not see a stale current thread pointer.
|
||||
atomic_signal_fence(memory_order_seq_cst);
|
||||
HwasanThread::TSDDtor(tsd);
|
||||
__hwasan_thread_exit();
|
||||
}
|
||||
|
||||
void HwasanTSDInit() {
|
||||
CHECK(!tsd_key_inited);
|
||||
tsd_key_inited = true;
|
||||
CHECK_EQ(0, pthread_key_create(&tsd_key, HwasanTSDDtor));
|
||||
}
|
||||
#else
|
||||
void HwasanTSDInit() {}
|
||||
void HwasanTSDThreadInit() {}
|
||||
#endif
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
uptr *GetCurrentThreadLongPtr() {
|
||||
return (uptr *)get_android_tls_ptr();
|
||||
}
|
||||
#else
|
||||
uptr *GetCurrentThreadLongPtr() {
|
||||
return &__hwasan_tls;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
void AndroidTestTlsSlot() {
|
||||
uptr kMagicValue = 0x010203040A0B0C0D;
|
||||
*(uptr *)get_android_tls_ptr() = kMagicValue;
|
||||
dlerror();
|
||||
if (*(uptr *)get_android_tls_ptr() != kMagicValue) {
|
||||
Printf(
|
||||
"ERROR: Incompatible version of Android: TLS_SLOT_SANITIZER(6) is used "
|
||||
"for dlerror().\n");
|
||||
Die();
|
||||
}
|
||||
}
|
||||
#else
|
||||
void AndroidTestTlsSlot() {}
|
||||
#endif
|
||||
|
||||
Thread *GetCurrentThread() {
|
||||
uptr *ThreadLong = GetCurrentThreadLongPtr();
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
if (!*ThreadLong)
|
||||
__hwasan_thread_enter();
|
||||
#endif
|
||||
auto *R = (StackAllocationsRingBuffer *)ThreadLong;
|
||||
return hwasanThreadList().GetThreadByBufferAddress((uptr)(R->Next()));
|
||||
}
|
||||
|
||||
struct AccessInfo {
|
||||
@ -340,14 +378,13 @@ static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
|
||||
BufferedStackTrace *stack = stack_buffer.data();
|
||||
stack->Reset();
|
||||
SignalContext sig{info, uc};
|
||||
GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, uc,
|
||||
common_flags()->fast_unwind_on_fatal);
|
||||
|
||||
ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store);
|
||||
GetStackTrace(stack, kStackTraceMax, StackTrace::GetNextInstructionPc(sig.pc),
|
||||
sig.bp, uc, common_flags()->fast_unwind_on_fatal);
|
||||
|
||||
++hwasan_report_count;
|
||||
if (flags()->halt_on_error || !ai.recover)
|
||||
Die();
|
||||
|
||||
bool fatal = flags()->halt_on_error || !ai.recover;
|
||||
ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store, fatal);
|
||||
|
||||
#if defined(__aarch64__)
|
||||
uc->uc_mcontext.pc += 4;
|
||||
@ -360,8 +397,8 @@ static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
|
||||
|
||||
static void OnStackUnwind(const SignalContext &sig, const void *,
|
||||
BufferedStackTrace *stack) {
|
||||
GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, sig.context,
|
||||
common_flags()->fast_unwind_on_fatal);
|
||||
GetStackTrace(stack, kStackTraceMax, StackTrace::GetNextInstructionPc(sig.pc),
|
||||
sig.bp, sig.context, common_flags()->fast_unwind_on_fatal);
|
||||
}
|
||||
|
||||
void HwasanOnDeadlySignal(int signo, void *info, void *context) {
|
||||
|
@ -16,68 +16,41 @@
|
||||
#define HWASAN_MAPPING_H
|
||||
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "hwasan_interface_internal.h"
|
||||
|
||||
// Typical mapping on Linux/x86_64 with fixed shadow mapping:
|
||||
// || [0x080000000000, 0x7fffffffffff] || HighMem ||
|
||||
// || [0x008000000000, 0x07ffffffffff] || HighShadow ||
|
||||
// || [0x000100000000, 0x007fffffffff] || ShadowGap ||
|
||||
// || [0x000010000000, 0x0000ffffffff] || LowMem ||
|
||||
// || [0x000001000000, 0x00000fffffff] || LowShadow ||
|
||||
// || [0x000000000000, 0x000000ffffff] || ShadowGap ||
|
||||
//
|
||||
// and with dynamic shadow mapped at [0x770d59f40000, 0x7f0d59f40000]:
|
||||
// Typical mapping on Linux/x86_64:
|
||||
// with dynamic shadow mapped at [0x770d59f40000, 0x7f0d59f40000]:
|
||||
// || [0x7f0d59f40000, 0x7fffffffffff] || HighMem ||
|
||||
// || [0x7efe2f934000, 0x7f0d59f3ffff] || HighShadow ||
|
||||
// || [0x7e7e2f934000, 0x7efe2f933fff] || ShadowGap ||
|
||||
// || [0x770d59f40000, 0x7e7e2f933fff] || LowShadow ||
|
||||
// || [0x000000000000, 0x770d59f3ffff] || LowMem ||
|
||||
|
||||
// Typical mapping on Android/AArch64 (39-bit VMA):
|
||||
// || [0x001000000000, 0x007fffffffff] || HighMem ||
|
||||
// || [0x000800000000, 0x000fffffffff] || ShadowGap ||
|
||||
// || [0x000100000000, 0x0007ffffffff] || HighShadow ||
|
||||
// || [0x000010000000, 0x0000ffffffff] || LowMem ||
|
||||
// || [0x000001000000, 0x00000fffffff] || LowShadow ||
|
||||
// || [0x000000000000, 0x000000ffffff] || ShadowGap ||
|
||||
//
|
||||
// and with dynamic shadow mapped: [0x007477480000, 0x007c77480000]:
|
||||
// Typical mapping on Android/AArch64
|
||||
// with dynamic shadow mapped: [0x007477480000, 0x007c77480000]:
|
||||
// || [0x007c77480000, 0x007fffffffff] || HighMem ||
|
||||
// || [0x007c3ebc8000, 0x007c7747ffff] || HighShadow ||
|
||||
// || [0x007bbebc8000, 0x007c3ebc7fff] || ShadowGap ||
|
||||
// || [0x007477480000, 0x007bbebc7fff] || LowShadow ||
|
||||
// || [0x000000000000, 0x00747747ffff] || LowMem ||
|
||||
|
||||
static constexpr __sanitizer::u64 kDefaultShadowSentinel = ~(__sanitizer::u64)0;
|
||||
|
||||
// Reasonable values are 4 (for 1/16th shadow) and 6 (for 1/64th).
|
||||
constexpr __sanitizer::uptr kShadowScale = 4;
|
||||
constexpr __sanitizer::uptr kShadowAlignment = 1ULL << kShadowScale;
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
# define HWASAN_FIXED_MAPPING 0
|
||||
#else
|
||||
# define HWASAN_FIXED_MAPPING 1
|
||||
#endif
|
||||
|
||||
#if HWASAN_FIXED_MAPPING
|
||||
# define SHADOW_OFFSET (0)
|
||||
# define HWASAN_PREMAP_SHADOW 0
|
||||
#else
|
||||
# define SHADOW_OFFSET (__hwasan_shadow_memory_dynamic_address)
|
||||
# define HWASAN_PREMAP_SHADOW 1
|
||||
#endif
|
||||
|
||||
#define SHADOW_GRANULARITY (1ULL << kShadowScale)
|
||||
|
||||
#define MEM_TO_SHADOW(mem) (((uptr)(mem) >> kShadowScale) + SHADOW_OFFSET)
|
||||
#define SHADOW_TO_MEM(shadow) (((uptr)(shadow) - SHADOW_OFFSET) << kShadowScale)
|
||||
|
||||
#define MEM_TO_SHADOW_SIZE(size) ((uptr)(size) >> kShadowScale)
|
||||
|
||||
#define MEM_IS_APP(mem) MemIsApp((uptr)(mem))
|
||||
constexpr uptr kShadowScale = 4;
|
||||
constexpr uptr kShadowAlignment = 1ULL << kShadowScale;
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
inline uptr MemToShadow(uptr untagged_addr) {
|
||||
return (untagged_addr >> kShadowScale) +
|
||||
__hwasan_shadow_memory_dynamic_address;
|
||||
}
|
||||
inline uptr ShadowToMem(uptr shadow_addr) {
|
||||
return (shadow_addr - __hwasan_shadow_memory_dynamic_address) << kShadowScale;
|
||||
}
|
||||
inline uptr MemToShadowSize(uptr size) {
|
||||
return size >> kShadowScale;
|
||||
}
|
||||
|
||||
bool MemIsApp(uptr p);
|
||||
|
||||
} // namespace __hwasan
|
||||
|
45
contrib/compiler-rt/lib/hwasan/hwasan_memintrinsics.cc
Normal file
45
contrib/compiler-rt/lib/hwasan/hwasan_memintrinsics.cc
Normal file
@ -0,0 +1,45 @@
|
||||
//===-- hwasan_memintrinsics.cc ---------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file is a part of HWAddressSanitizer and contains HWASAN versions of
|
||||
/// memset, memcpy and memmove
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <string.h>
|
||||
#include "hwasan.h"
|
||||
#include "hwasan_checks.h"
|
||||
#include "hwasan_flags.h"
|
||||
#include "hwasan_interface_internal.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
using namespace __hwasan;
|
||||
|
||||
void *__hwasan_memset(void *block, int c, uptr size) {
|
||||
CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
|
||||
reinterpret_cast<uptr>(block), size);
|
||||
return memset(UntagPtr(block), c, size);
|
||||
}
|
||||
|
||||
void *__hwasan_memcpy(void *to, const void *from, uptr size) {
|
||||
CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
|
||||
reinterpret_cast<uptr>(to), size);
|
||||
CheckAddressSized<ErrorAction::Recover, AccessType::Load>(
|
||||
reinterpret_cast<uptr>(from), size);
|
||||
return memcpy(UntagPtr(to), UntagPtr(from), size);
|
||||
}
|
||||
|
||||
void *__hwasan_memmove(void *to, const void *from, uptr size) {
|
||||
CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
|
||||
reinterpret_cast<uptr>(to), size);
|
||||
CheckAddressSized<ErrorAction::Recover, AccessType::Load>(
|
||||
reinterpret_cast<uptr>(from), size);
|
||||
return memmove(UntagPtr(to), UntagPtr(from), size);
|
||||
}
|
@ -51,7 +51,7 @@ void *operator new[](size_t size, std::nothrow_t const&) {
|
||||
|
||||
#define OPERATOR_DELETE_BODY \
|
||||
GET_MALLOC_STACK_TRACE; \
|
||||
if (ptr) HwasanDeallocate(&stack, ptr)
|
||||
if (ptr) hwasan_free(ptr, &stack)
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- hwasan_poisoning.cc ---------------------------------------*- C++ -*-===//
|
||||
//===-- hwasan_poisoning.cc -------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -22,8 +22,8 @@ namespace __hwasan {
|
||||
uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) {
|
||||
CHECK(IsAligned(p, kShadowAlignment));
|
||||
CHECK(IsAligned(size, kShadowAlignment));
|
||||
uptr shadow_start = MEM_TO_SHADOW(p);
|
||||
uptr shadow_size = MEM_TO_SHADOW_SIZE(size);
|
||||
uptr shadow_start = MemToShadow(p);
|
||||
uptr shadow_size = MemToShadowSize(size);
|
||||
internal_memset((void *)shadow_start, tag, shadow_size);
|
||||
return AddTagToPointer(p, tag);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- hwasan_poisoning.h ----------------------------------------*- C++ -*-===//
|
||||
//===-- hwasan_poisoning.h --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
|
@ -15,18 +15,64 @@
|
||||
#include "hwasan.h"
|
||||
#include "hwasan_allocator.h"
|
||||
#include "hwasan_mapping.h"
|
||||
#include "hwasan_thread.h"
|
||||
#include "hwasan_thread_list.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
#include "sanitizer_common/sanitizer_report_decorator.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
#include "sanitizer_common/sanitizer_stacktrace_printer.h"
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
class ScopedReport {
|
||||
public:
|
||||
ScopedReport(bool fatal = false) : error_message_(1), fatal(fatal) {
|
||||
BlockingMutexLock lock(&error_message_lock_);
|
||||
error_message_ptr_ = fatal ? &error_message_ : nullptr;
|
||||
}
|
||||
|
||||
~ScopedReport() {
|
||||
BlockingMutexLock lock(&error_message_lock_);
|
||||
if (fatal) {
|
||||
SetAbortMessage(error_message_.data());
|
||||
Die();
|
||||
}
|
||||
error_message_ptr_ = nullptr;
|
||||
}
|
||||
|
||||
static void MaybeAppendToErrorMessage(const char *msg) {
|
||||
BlockingMutexLock lock(&error_message_lock_);
|
||||
if (!error_message_ptr_)
|
||||
return;
|
||||
uptr len = internal_strlen(msg);
|
||||
uptr old_size = error_message_ptr_->size();
|
||||
error_message_ptr_->resize(old_size + len);
|
||||
// overwrite old trailing '\0', keep new trailing '\0' untouched.
|
||||
internal_memcpy(&(*error_message_ptr_)[old_size - 1], msg, len);
|
||||
}
|
||||
private:
|
||||
ScopedErrorReportLock error_report_lock_;
|
||||
InternalMmapVector<char> error_message_;
|
||||
bool fatal;
|
||||
|
||||
static InternalMmapVector<char> *error_message_ptr_;
|
||||
static BlockingMutex error_message_lock_;
|
||||
};
|
||||
|
||||
InternalMmapVector<char> *ScopedReport::error_message_ptr_;
|
||||
BlockingMutex ScopedReport::error_message_lock_;
|
||||
|
||||
// If there is an active ScopedReport, append to its error message.
|
||||
void AppendToErrorMessageBuffer(const char *buffer) {
|
||||
ScopedReport::MaybeAppendToErrorMessage(buffer);
|
||||
}
|
||||
|
||||
static StackTrace GetStackTraceFromId(u32 id) {
|
||||
CHECK(id);
|
||||
StackTrace res = StackDepotGet(id);
|
||||
@ -34,100 +80,344 @@ static StackTrace GetStackTraceFromId(u32 id) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// A RAII object that holds a copy of the current thread stack ring buffer.
|
||||
// The actual stack buffer may change while we are iterating over it (for
|
||||
// example, Printf may call syslog() which can itself be built with hwasan).
|
||||
class SavedStackAllocations {
|
||||
public:
|
||||
SavedStackAllocations(StackAllocationsRingBuffer *rb) {
|
||||
uptr size = rb->size() * sizeof(uptr);
|
||||
void *storage =
|
||||
MmapAlignedOrDieOnFatalError(size, size * 2, "saved stack allocations");
|
||||
new (&rb_) StackAllocationsRingBuffer(*rb, storage);
|
||||
}
|
||||
|
||||
~SavedStackAllocations() {
|
||||
StackAllocationsRingBuffer *rb = get();
|
||||
UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr));
|
||||
}
|
||||
|
||||
StackAllocationsRingBuffer *get() {
|
||||
return (StackAllocationsRingBuffer *)&rb_;
|
||||
}
|
||||
|
||||
private:
|
||||
uptr rb_;
|
||||
};
|
||||
|
||||
class Decorator: public __sanitizer::SanitizerCommonDecorator {
|
||||
public:
|
||||
Decorator() : SanitizerCommonDecorator() { }
|
||||
const char *Access() { return Blue(); }
|
||||
const char *Allocation() const { return Magenta(); }
|
||||
const char *Origin() const { return Magenta(); }
|
||||
const char *Name() const { return Green(); }
|
||||
const char *Location() { return Green(); }
|
||||
const char *Thread() { return Green(); }
|
||||
};
|
||||
|
||||
struct HeapAddressDescription {
|
||||
uptr addr;
|
||||
u32 alloc_stack_id;
|
||||
u32 free_stack_id;
|
||||
|
||||
void Print() const {
|
||||
Decorator d;
|
||||
if (free_stack_id) {
|
||||
Printf("%sfreed here:%s\n", d.Allocation(), d.Default());
|
||||
GetStackTraceFromId(free_stack_id).Print();
|
||||
Printf("%spreviously allocated here:%s\n", d.Allocation(), d.Default());
|
||||
} else {
|
||||
Printf("%sallocated here:%s\n", d.Allocation(), d.Default());
|
||||
// Returns the index of the rb element that matches tagged_addr (plus one),
|
||||
// or zero if found nothing.
|
||||
uptr FindHeapAllocation(HeapAllocationsRingBuffer *rb,
|
||||
uptr tagged_addr,
|
||||
HeapAllocationRecord *har) {
|
||||
if (!rb) return 0;
|
||||
for (uptr i = 0, size = rb->size(); i < size; i++) {
|
||||
auto h = (*rb)[i];
|
||||
if (h.tagged_addr <= tagged_addr &&
|
||||
h.tagged_addr + h.requested_size > tagged_addr) {
|
||||
*har = h;
|
||||
return i + 1;
|
||||
}
|
||||
GetStackTraceFromId(alloc_stack_id).Print();
|
||||
}
|
||||
};
|
||||
|
||||
bool GetHeapAddressInformation(uptr addr, uptr access_size,
|
||||
HeapAddressDescription *description) {
|
||||
HwasanChunkView chunk = FindHeapChunkByAddress(addr);
|
||||
if (!chunk.IsValid())
|
||||
return false;
|
||||
description->addr = addr;
|
||||
description->alloc_stack_id = chunk.GetAllocStackId();
|
||||
description->free_stack_id = chunk.GetFreeStackId();
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PrintAddressDescription(uptr addr, uptr access_size) {
|
||||
HeapAddressDescription heap_description;
|
||||
if (GetHeapAddressInformation(addr, access_size, &heap_description)) {
|
||||
heap_description.Print();
|
||||
return;
|
||||
}
|
||||
// We exhausted our possibilities. Bail out.
|
||||
Printf("HWAddressSanitizer can not describe address in more detail.\n");
|
||||
}
|
||||
|
||||
void ReportInvalidAccess(StackTrace *stack, u32 origin) {
|
||||
ScopedErrorReportLock l;
|
||||
|
||||
void PrintAddressDescription(
|
||||
uptr tagged_addr, uptr access_size,
|
||||
StackAllocationsRingBuffer *current_stack_allocations) {
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report("WARNING: HWAddressSanitizer: invalid access\n");
|
||||
Printf("%s", d.Default());
|
||||
stack->Print();
|
||||
ReportErrorSummary("invalid-access", stack);
|
||||
int num_descriptions_printed = 0;
|
||||
uptr untagged_addr = UntagAddr(tagged_addr);
|
||||
|
||||
// Print some very basic information about the address, if it's a heap.
|
||||
HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
|
||||
if (uptr beg = chunk.Beg()) {
|
||||
uptr size = chunk.ActualSize();
|
||||
Printf("%s[%p,%p) is a %s %s heap chunk; "
|
||||
"size: %zd offset: %zd\n%s",
|
||||
d.Location(),
|
||||
beg, beg + size,
|
||||
chunk.FromSmallHeap() ? "small" : "large",
|
||||
chunk.IsAllocated() ? "allocated" : "unallocated",
|
||||
size, untagged_addr - beg,
|
||||
d.Default());
|
||||
}
|
||||
|
||||
// Check if this looks like a heap buffer overflow by scanning
|
||||
// the shadow left and right and looking for the first adjacent
|
||||
// object with a different memory tag. If that tag matches addr_tag,
|
||||
// check the allocator if it has a live chunk there.
|
||||
tag_t addr_tag = GetTagFromPointer(tagged_addr);
|
||||
tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
|
||||
if (*tag_ptr != addr_tag) { // should be true usually.
|
||||
tag_t *left = tag_ptr, *right = tag_ptr;
|
||||
// scan left.
|
||||
for (int i = 0; i < 1000 && *left == *tag_ptr; i++, left--){}
|
||||
// scan right.
|
||||
for (int i = 0; i < 1000 && *right == *tag_ptr; i++, right++){}
|
||||
// Chose the object that has addr_tag and that is closer to addr.
|
||||
tag_t *candidate = nullptr;
|
||||
if (*right == addr_tag && *left == addr_tag)
|
||||
candidate = right - tag_ptr < tag_ptr - left ? right : left;
|
||||
else if (*right == addr_tag)
|
||||
candidate = right;
|
||||
else if (*left == addr_tag)
|
||||
candidate = left;
|
||||
|
||||
if (candidate) {
|
||||
uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
|
||||
HwasanChunkView chunk = FindHeapChunkByAddress(mem);
|
||||
if (chunk.IsAllocated()) {
|
||||
Printf("%s", d.Location());
|
||||
Printf(
|
||||
"%p is located %zd bytes to the %s of %zd-byte region [%p,%p)\n",
|
||||
untagged_addr,
|
||||
candidate == left ? untagged_addr - chunk.End()
|
||||
: chunk.Beg() - untagged_addr,
|
||||
candidate == right ? "left" : "right", chunk.UsedSize(),
|
||||
chunk.Beg(), chunk.End());
|
||||
Printf("%s", d.Allocation());
|
||||
Printf("allocated here:\n");
|
||||
Printf("%s", d.Default());
|
||||
GetStackTraceFromId(chunk.GetAllocStackId()).Print();
|
||||
num_descriptions_printed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
|
||||
// Scan all threads' ring buffers to find if it's a heap-use-after-free.
|
||||
HeapAllocationRecord har;
|
||||
if (uptr D = FindHeapAllocation(t->heap_allocations(), tagged_addr, &har)) {
|
||||
Printf("%s", d.Location());
|
||||
Printf("%p is located %zd bytes inside of %zd-byte region [%p,%p)\n",
|
||||
untagged_addr, untagged_addr - UntagAddr(har.tagged_addr),
|
||||
har.requested_size, UntagAddr(har.tagged_addr),
|
||||
UntagAddr(har.tagged_addr) + har.requested_size);
|
||||
Printf("%s", d.Allocation());
|
||||
Printf("freed by thread T%zd here:\n", t->unique_id());
|
||||
Printf("%s", d.Default());
|
||||
GetStackTraceFromId(har.free_context_id).Print();
|
||||
|
||||
Printf("%s", d.Allocation());
|
||||
Printf("previously allocated here:\n", t);
|
||||
Printf("%s", d.Default());
|
||||
GetStackTraceFromId(har.alloc_context_id).Print();
|
||||
|
||||
// Print a developer note: the index of this heap object
|
||||
// in the thread's deallocation ring buffer.
|
||||
Printf("hwasan_dev_note_heap_rb_distance: %zd %zd\n", D,
|
||||
flags()->heap_history_size);
|
||||
|
||||
t->Announce();
|
||||
num_descriptions_printed++;
|
||||
}
|
||||
|
||||
// Very basic check for stack memory.
|
||||
if (t->AddrIsInStack(untagged_addr)) {
|
||||
Printf("%s", d.Location());
|
||||
Printf("Address %p is located in stack of thread T%zd\n", untagged_addr,
|
||||
t->unique_id());
|
||||
Printf("%s", d.Default());
|
||||
t->Announce();
|
||||
|
||||
// Temporary report section, needs to be improved.
|
||||
Printf("Previously allocated frames:\n");
|
||||
auto *sa = (t == GetCurrentThread() && current_stack_allocations)
|
||||
? current_stack_allocations
|
||||
: t->stack_allocations();
|
||||
uptr frames = Min((uptr)flags()->stack_history_size, sa->size());
|
||||
InternalScopedString frame_desc(GetPageSizeCached() * 2);
|
||||
for (uptr i = 0; i < frames; i++) {
|
||||
uptr record = (*sa)[i];
|
||||
if (!record)
|
||||
break;
|
||||
uptr sp = (record >> 48) << 4;
|
||||
uptr pc_mask = (1ULL << 48) - 1;
|
||||
uptr pc = record & pc_mask;
|
||||
if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) {
|
||||
frame_desc.append(" sp: 0x%zx pc: %p ", sp, pc);
|
||||
RenderFrame(&frame_desc, "in %f %s:%l\n", 0, frame->info,
|
||||
common_flags()->symbolize_vs_style,
|
||||
common_flags()->strip_path_prefix);
|
||||
frame->ClearAll();
|
||||
if (auto Descr = GetStackFrameDescr(pc))
|
||||
frame_desc.append(" %s\n", Descr);
|
||||
}
|
||||
Printf("%s", frame_desc.data());
|
||||
frame_desc.clear();
|
||||
}
|
||||
|
||||
num_descriptions_printed++;
|
||||
}
|
||||
});
|
||||
|
||||
// Print the remaining threads, as an extra information, 1 line per thread.
|
||||
hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); });
|
||||
|
||||
if (!num_descriptions_printed)
|
||||
// We exhausted our possibilities. Bail out.
|
||||
Printf("HWAddressSanitizer can not describe address in more detail.\n");
|
||||
}
|
||||
|
||||
void ReportStats() {}
|
||||
|
||||
void ReportInvalidAccessInsideAddressRange(const char *what, const void *start,
|
||||
uptr size, uptr offset) {
|
||||
ScopedErrorReportLock l;
|
||||
static void PrintTagsAroundAddr(tag_t *tag_ptr) {
|
||||
Printf(
|
||||
"Memory tags around the buggy address (one tag corresponds to %zd "
|
||||
"bytes):\n", kShadowAlignment);
|
||||
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Printf("%sTag mismatch in %s%s%s at offset %zu inside [%p, %zu)%s\n",
|
||||
d.Warning(), d.Name(), what, d.Warning(), offset, start, size,
|
||||
d.Default());
|
||||
PrintAddressDescription((uptr)start + offset, 1);
|
||||
// if (__sanitizer::Verbosity())
|
||||
// DescribeMemoryRange(start, size);
|
||||
const uptr row_len = 16; // better be power of two.
|
||||
const uptr num_rows = 17;
|
||||
tag_t *center_row_beg = reinterpret_cast<tag_t *>(
|
||||
RoundDownTo(reinterpret_cast<uptr>(tag_ptr), row_len));
|
||||
tag_t *beg_row = center_row_beg - row_len * (num_rows / 2);
|
||||
tag_t *end_row = center_row_beg + row_len * (num_rows / 2);
|
||||
InternalScopedString s(GetPageSizeCached() * 8);
|
||||
for (tag_t *row = beg_row; row < end_row; row += row_len) {
|
||||
s.append("%s", row == center_row_beg ? "=>" : " ");
|
||||
for (uptr i = 0; i < row_len; i++) {
|
||||
s.append("%s", row + i == tag_ptr ? "[" : " ");
|
||||
s.append("%02x", row[i]);
|
||||
s.append("%s", row + i == tag_ptr ? "]" : " ");
|
||||
}
|
||||
s.append("%s\n", row == center_row_beg ? "<=" : " ");
|
||||
}
|
||||
Printf("%s", s.data());
|
||||
}
|
||||
|
||||
void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
|
||||
bool is_store) {
|
||||
ScopedErrorReportLock l;
|
||||
void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
|
||||
ScopedReport R(flags()->halt_on_error);
|
||||
|
||||
uptr untagged_addr = UntagAddr(tagged_addr);
|
||||
tag_t ptr_tag = GetTagFromPointer(tagged_addr);
|
||||
tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
|
||||
tag_t mem_tag = *tag_ptr;
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
uptr address = GetAddressFromPointer(addr);
|
||||
Printf("%s of size %zu at %p\n", is_store ? "WRITE" : "READ", access_size,
|
||||
address);
|
||||
|
||||
tag_t ptr_tag = GetTagFromPointer(addr);
|
||||
tag_t mem_tag = *(tag_t *)MEM_TO_SHADOW(address);
|
||||
Printf("pointer tag 0x%x\nmemory tag 0x%x\n", ptr_tag, mem_tag);
|
||||
Printf("%s", d.Error());
|
||||
uptr pc = stack->size ? stack->trace[0] : 0;
|
||||
const char *bug_type = "invalid-free";
|
||||
Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
|
||||
untagged_addr, pc);
|
||||
Printf("%s", d.Access());
|
||||
Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag, mem_tag);
|
||||
Printf("%s", d.Default());
|
||||
|
||||
stack->Print();
|
||||
|
||||
PrintAddressDescription(address, access_size);
|
||||
PrintAddressDescription(tagged_addr, 0, nullptr);
|
||||
|
||||
ReportErrorSummary("tag-mismatch", stack);
|
||||
PrintTagsAroundAddr(tag_ptr);
|
||||
|
||||
ReportErrorSummary(bug_type, stack);
|
||||
}
|
||||
|
||||
void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
|
||||
uptr tail_size, const u8 *expected) {
|
||||
ScopedReport R(flags()->halt_on_error);
|
||||
Decorator d;
|
||||
uptr untagged_addr = UntagAddr(tagged_addr);
|
||||
Printf("%s", d.Error());
|
||||
const char *bug_type = "alocation-tail-overwritten";
|
||||
Report("ERROR: %s: %s; heap object [%p,%p) of size %zd\n", SanitizerToolName,
|
||||
bug_type, untagged_addr, untagged_addr + orig_size, orig_size);
|
||||
Printf("\n%s", d.Default());
|
||||
stack->Print();
|
||||
HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
|
||||
if (chunk.Beg()) {
|
||||
Printf("%s", d.Allocation());
|
||||
Printf("allocated here:\n");
|
||||
Printf("%s", d.Default());
|
||||
GetStackTraceFromId(chunk.GetAllocStackId()).Print();
|
||||
}
|
||||
|
||||
InternalScopedString s(GetPageSizeCached() * 8);
|
||||
CHECK_GT(tail_size, 0U);
|
||||
CHECK_LT(tail_size, kShadowAlignment);
|
||||
u8 *tail = reinterpret_cast<u8*>(untagged_addr + orig_size);
|
||||
s.append("Tail contains: ");
|
||||
for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
|
||||
s.append(".. ");
|
||||
for (uptr i = 0; i < tail_size; i++)
|
||||
s.append("%02x ", tail[i]);
|
||||
s.append("\n");
|
||||
s.append("Expected: ");
|
||||
for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
|
||||
s.append(".. ");
|
||||
for (uptr i = 0; i < tail_size; i++)
|
||||
s.append("%02x ", expected[i]);
|
||||
s.append("\n");
|
||||
s.append(" ");
|
||||
for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
|
||||
s.append(" ");
|
||||
for (uptr i = 0; i < tail_size; i++)
|
||||
s.append("%s ", expected[i] != tail[i] ? "^^" : " ");
|
||||
|
||||
s.append("\nThis error occurs when a buffer overflow overwrites memory\n"
|
||||
"to the right of a heap object, but within the %zd-byte granule, e.g.\n"
|
||||
" char *x = new char[20];\n"
|
||||
" x[25] = 42;\n"
|
||||
"By default %s does not detect such bugs at the time of write,\n"
|
||||
"but can detect them at the time of free/delete.\n"
|
||||
"To disable this feature set HWASAN_OPTIONS=free_checks_tail_magic=0;\n"
|
||||
"To enable checking at the time of access, set "
|
||||
"HWASAN_OPTIONS=malloc_align_right to non-zero\n\n",
|
||||
kShadowAlignment, SanitizerToolName);
|
||||
Printf("%s", s.data());
|
||||
GetCurrentThread()->Announce();
|
||||
|
||||
tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
|
||||
PrintTagsAroundAddr(tag_ptr);
|
||||
|
||||
ReportErrorSummary(bug_type, stack);
|
||||
}
|
||||
|
||||
void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
|
||||
bool is_store, bool fatal) {
|
||||
ScopedReport R(fatal);
|
||||
SavedStackAllocations current_stack_allocations(
|
||||
GetCurrentThread()->stack_allocations());
|
||||
|
||||
Decorator d;
|
||||
Printf("%s", d.Error());
|
||||
uptr untagged_addr = UntagAddr(tagged_addr);
|
||||
// TODO: when possible, try to print heap-use-after-free, etc.
|
||||
const char *bug_type = "tag-mismatch";
|
||||
uptr pc = stack->size ? stack->trace[0] : 0;
|
||||
Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
|
||||
untagged_addr, pc);
|
||||
|
||||
Thread *t = GetCurrentThread();
|
||||
|
||||
tag_t ptr_tag = GetTagFromPointer(tagged_addr);
|
||||
tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
|
||||
tag_t mem_tag = *tag_ptr;
|
||||
Printf("%s", d.Access());
|
||||
Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n",
|
||||
is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag,
|
||||
mem_tag, t->unique_id());
|
||||
Printf("%s", d.Default());
|
||||
|
||||
stack->Print();
|
||||
|
||||
PrintAddressDescription(tagged_addr, access_size,
|
||||
current_stack_allocations.get());
|
||||
t->Announce();
|
||||
|
||||
PrintTagsAroundAddr(tag_ptr);
|
||||
|
||||
ReportErrorSummary(bug_type, stack);
|
||||
}
|
||||
|
||||
} // namespace __hwasan
|
||||
|
@ -21,12 +21,12 @@
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
void ReportInvalidAccess(StackTrace *stack, u32 origin);
|
||||
void ReportStats();
|
||||
void ReportInvalidAccessInsideAddressRange(const char *what, const void *start,
|
||||
uptr size, uptr offset);
|
||||
void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
|
||||
bool is_store);
|
||||
bool is_store, bool fatal);
|
||||
void ReportInvalidFree(StackTrace *stack, uptr addr);
|
||||
void ReportTailOverwritten(StackTrace *stack, uptr addr, uptr orig_size,
|
||||
uptr tail_size, const u8 *expected);
|
||||
|
||||
void ReportAtExitStatistics();
|
||||
|
||||
|
@ -5,8 +5,11 @@
|
||||
#include "hwasan_poisoning.h"
|
||||
#include "hwasan_interface_internal.h"
|
||||
|
||||
#include "sanitizer_common/sanitizer_file.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
#include "sanitizer_common/sanitizer_tls_get_addr.h"
|
||||
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
static u32 RandomSeed() {
|
||||
@ -22,69 +25,70 @@ static u32 RandomSeed() {
|
||||
return seed;
|
||||
}
|
||||
|
||||
HwasanThread *HwasanThread::Create(thread_callback_t start_routine,
|
||||
void *arg) {
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
uptr size = RoundUpTo(sizeof(HwasanThread), PageSize);
|
||||
HwasanThread *thread = (HwasanThread*)MmapOrDie(size, __func__);
|
||||
thread->start_routine_ = start_routine;
|
||||
thread->arg_ = arg;
|
||||
thread->destructor_iterations_ = GetPthreadDestructorIterations();
|
||||
thread->random_state_ = flags()->random_tags ? RandomSeed() : 0;
|
||||
void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size) {
|
||||
static u64 unique_id;
|
||||
unique_id_ = unique_id++;
|
||||
random_state_ = flags()->random_tags ? RandomSeed() : unique_id_;
|
||||
if (auto sz = flags()->heap_history_size)
|
||||
heap_allocations_ = HeapAllocationsRingBuffer::New(sz);
|
||||
|
||||
return thread;
|
||||
}
|
||||
HwasanTSDThreadInit(); // Only needed with interceptors.
|
||||
uptr *ThreadLong = GetCurrentThreadLongPtr();
|
||||
// The following implicitly sets (this) as the current thread.
|
||||
stack_allocations_ = new (ThreadLong)
|
||||
StackAllocationsRingBuffer((void *)stack_buffer_start, stack_buffer_size);
|
||||
// Check that it worked.
|
||||
CHECK_EQ(GetCurrentThread(), this);
|
||||
|
||||
void HwasanThread::SetThreadStackAndTls() {
|
||||
uptr tls_size = 0;
|
||||
uptr stack_size = 0;
|
||||
GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size,
|
||||
&tls_begin_, &tls_size);
|
||||
// ScopedTaggingDisable needs GetCurrentThread to be set up.
|
||||
ScopedTaggingDisabler disabler;
|
||||
|
||||
uptr tls_size;
|
||||
uptr stack_size;
|
||||
GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, &tls_begin_,
|
||||
&tls_size);
|
||||
stack_top_ = stack_bottom_ + stack_size;
|
||||
tls_end_ = tls_begin_ + tls_size;
|
||||
|
||||
int local;
|
||||
CHECK(AddrIsInStack((uptr)&local));
|
||||
if (stack_bottom_) {
|
||||
int local;
|
||||
CHECK(AddrIsInStack((uptr)&local));
|
||||
CHECK(MemIsApp(stack_bottom_));
|
||||
CHECK(MemIsApp(stack_top_ - 1));
|
||||
}
|
||||
|
||||
if (flags()->verbose_threads) {
|
||||
if (IsMainThread()) {
|
||||
Printf("sizeof(Thread): %zd sizeof(HeapRB): %zd sizeof(StackRB): %zd\n",
|
||||
sizeof(Thread), heap_allocations_->SizeInBytes(),
|
||||
stack_allocations_->size() * sizeof(uptr));
|
||||
}
|
||||
Print("Creating : ");
|
||||
}
|
||||
}
|
||||
|
||||
void HwasanThread::Init() {
|
||||
SetThreadStackAndTls();
|
||||
CHECK(MEM_IS_APP(stack_bottom_));
|
||||
CHECK(MEM_IS_APP(stack_top_ - 1));
|
||||
}
|
||||
|
||||
void HwasanThread::TSDDtor(void *tsd) {
|
||||
HwasanThread *t = (HwasanThread*)tsd;
|
||||
t->Destroy();
|
||||
}
|
||||
|
||||
void HwasanThread::ClearShadowForThreadStackAndTLS() {
|
||||
TagMemory(stack_bottom_, stack_top_ - stack_bottom_, 0);
|
||||
void Thread::ClearShadowForThreadStackAndTLS() {
|
||||
if (stack_top_ != stack_bottom_)
|
||||
TagMemory(stack_bottom_, stack_top_ - stack_bottom_, 0);
|
||||
if (tls_begin_ != tls_end_)
|
||||
TagMemory(tls_begin_, tls_end_ - tls_begin_, 0);
|
||||
}
|
||||
|
||||
void HwasanThread::Destroy() {
|
||||
malloc_storage().CommitBack();
|
||||
void Thread::Destroy() {
|
||||
if (flags()->verbose_threads)
|
||||
Print("Destroying: ");
|
||||
AllocatorSwallowThreadLocalCache(allocator_cache());
|
||||
ClearShadowForThreadStackAndTLS();
|
||||
uptr size = RoundUpTo(sizeof(HwasanThread), GetPageSizeCached());
|
||||
UnmapOrDie(this, size);
|
||||
if (heap_allocations_)
|
||||
heap_allocations_->Delete();
|
||||
DTLS_Destroy();
|
||||
}
|
||||
|
||||
thread_return_t HwasanThread::ThreadStart() {
|
||||
Init();
|
||||
|
||||
if (!start_routine_) {
|
||||
// start_routine_ == 0 if we're on the main thread or on one of the
|
||||
// OS X libdispatch worker threads. But nobody is supposed to call
|
||||
// ThreadStart() for the worker threads.
|
||||
return 0;
|
||||
}
|
||||
|
||||
thread_return_t res = start_routine_(arg_);
|
||||
|
||||
return res;
|
||||
void Thread::Print(const char *Prefix) {
|
||||
Printf("%sT%zd %p stack: [%p,%p) sz: %zd tls: [%p,%p)\n", Prefix,
|
||||
unique_id_, this, stack_bottom(), stack_top(),
|
||||
stack_top() - stack_bottom(),
|
||||
tls_begin(), tls_end());
|
||||
}
|
||||
|
||||
static u32 xorshift(u32 state) {
|
||||
@ -95,7 +99,8 @@ static u32 xorshift(u32 state) {
|
||||
}
|
||||
|
||||
// Generate a (pseudo-)random non-zero tag.
|
||||
tag_t HwasanThread::GenerateRandomTag() {
|
||||
tag_t Thread::GenerateRandomTag() {
|
||||
if (tagging_disabled_) return 0;
|
||||
tag_t tag;
|
||||
do {
|
||||
if (flags()->random_tags) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user