Merge compiler-rt trunk r351319, and resolve conflicts.

This commit is contained in:
Dimitry Andric 2019-01-22 19:20:24 +00:00
commit 9c710df928
242 changed files with 11913 additions and 4056 deletions

View File

@ -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.

View File

@ -82,7 +82,6 @@ extern "C" {
Currently available with ASan only.
*/
void __sanitizer_purge_allocator(void);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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];

View File

@ -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();

View File

@ -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();
};

View File

@ -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 "

View File

@ -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>.

View File

@ -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;
}

View File

@ -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");

View File

@ -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) \

View File

@ -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 ||

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>.

View File

@ -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.

View File

@ -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();

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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. */

View File

@ -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

View File

@ -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;

View File

@ -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, &quotient, &quotientLo);
// 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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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)

View File

@ -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

View File

@ -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
{

View File

@ -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();
}

View File

@ -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)

View File

@ -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);

View File

@ -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;

View 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;
}

View 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);
}

View File

@ -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.

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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 {

View 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

View 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

View 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

View File

@ -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;

View File

@ -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; }

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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"

View File

@ -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

View 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

View File

@ -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")

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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();
}

View File

@ -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) \

View File

@ -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);
}

View File

@ -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

View 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

View File

@ -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

View File

@ -1,4 +1,4 @@
//===-- hwasan_flags.h --------------------------------------------*- C++ -*-===//
//===-- hwasan_flags.h ------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//

View File

@ -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.")

View File

@ -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;
}

View File

@ -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

View File

@ -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) {

View File

@ -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

View 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);
}

View File

@ -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; }

View File

@ -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);
}

View File

@ -1,4 +1,4 @@
//===-- hwasan_poisoning.h ----------------------------------------*- C++ -*-===//
//===-- hwasan_poisoning.h --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//

View File

@ -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

View File

@ -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();

View File

@ -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