Upgrade our copies of clang, llvm, lldb, compiler-rt and libc++ to 3.7.0
release. Please note that from 3.5.0 onwards, clang, llvm and lldb require C++11 support to build; see UPDATING for more information. Release notes for llvm and clang can be found here: <http://llvm.org/releases/3.7.0/docs/ReleaseNotes.html> <http://llvm.org/releases/3.7.0/tools/clang/docs/ReleaseNotes.html> Thanks to Ed Maste, Andrew Turner and Antoine Brodin for their help. Exp-run: antoine Relnotes: yes
This commit is contained in:
commit
4f4bbad316
@ -38,6 +38,69 @@
|
||||
# xargs -n1 | sort | uniq -d;
|
||||
# done
|
||||
|
||||
# 20151006: new libc++ import
|
||||
OLD_FILES+=usr/include/c++/__tuple_03
|
||||
# 20151006: new clang import which bumps version from 3.6.1 to 3.7.0.
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/__stddef_max_align_t.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/__wmmintrin_aes.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/__wmmintrin_pclmul.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/adxintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/altivec.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/ammintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/arm_acle.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/arm_neon.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/avx2intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/avx512bwintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/avx512erintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/avx512fintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/avx512vlbwintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/avx512vlintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/avxintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/bmi2intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/bmiintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/cpuid.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/emmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/f16cintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/fma4intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/fmaintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/ia32intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/immintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/lzcntintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/mm3dnow.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/mm_malloc.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/mmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/module.modulemap
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/nmmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/pmmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/popcntintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/prfchwintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/rdseedintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/rtmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/shaintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/smmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/tbmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/tmmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/wmmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/x86intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/xmmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/include/xopintrin.h
|
||||
OLD_DIRS+=usr/lib/clang/3.6.1/include
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.asan-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.asan-x86_64.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.asan_cxx-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.asan_cxx-x86_64.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.profile-arm.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.profile-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.profile-x86_64.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.san-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.san-x86_64.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.ubsan-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.ubsan-x86_64.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.ubsan_cxx-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.6.1/lib/freebsd/libclang_rt.ubsan_cxx-x86_64.a
|
||||
OLD_DIRS+=usr/lib/clang/3.6.1/lib/freebsd
|
||||
OLD_DIRS+=usr/lib/clang/3.6.1/lib
|
||||
OLD_DIRS+=usr/lib/clang/3.6.1
|
||||
# 20150928: unused sgsmsg utility is removed
|
||||
OLD_FILES+=usr/bin/sgsmsg
|
||||
# 20150926: remove links to removed/unimplemented mbuf(9) macros
|
||||
|
5
UPDATING
5
UPDATING
@ -31,6 +31,11 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 11.x IS SLOW:
|
||||
disable the most expensive debugging functionality run
|
||||
"ln -s 'abort:false,junk:false' /etc/malloc.conf".)
|
||||
|
||||
20151006:
|
||||
Clang, llvm, lldb, compiler-rt and libc++ have been upgraded to 3.7.0.
|
||||
Please see the 20141231 entry below for information about prerequisites
|
||||
and upgrading, if you are not already using clang 3.5.0 or higher.
|
||||
|
||||
20150924:
|
||||
Kernel debug files have been moved to /usr/lib/debug/boot/kernel/,
|
||||
and renamed from .symbols to .debug. This reduces the size requirements
|
||||
|
@ -14,7 +14,7 @@ Full text of the relevant licenses is included below.
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
|
||||
Copyright (c) 2009-2015 by the contributors listed in CREDITS.TXT
|
||||
|
||||
All rights reserved.
|
||||
|
||||
@ -55,7 +55,7 @@ SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
|
||||
Copyright (c) 2009-2015 by the contributors listed in CREDITS.TXT
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -39,6 +39,23 @@ extern "C" {
|
||||
// Some of the entries in *data will be zero.
|
||||
uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data);
|
||||
|
||||
// The coverage instrumentation may optionally provide imprecise counters.
|
||||
// Rather than exposing the counter values to the user we instead map
|
||||
// the counters to a bitset.
|
||||
// Every counter is associated with 8 bits in the bitset.
|
||||
// We define 8 value ranges: 1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+
|
||||
// The i-th bit is set to 1 if the counter value is in the i-th range.
|
||||
// This counter-based coverage implementation is *not* thread-safe.
|
||||
|
||||
// Returns the number of registered coverage counters.
|
||||
uintptr_t __sanitizer_get_number_of_counters();
|
||||
// Updates the counter 'bitset', clears the counters and returns the number of
|
||||
// new bits in 'bitset'.
|
||||
// If 'bitset' is nullptr, only clears the counters.
|
||||
// Otherwise 'bitset' should be at least
|
||||
// __sanitizer_get_number_of_counters bytes long and 8-aligned.
|
||||
uintptr_t
|
||||
__sanitizer_update_counter_bitset_and_clear_counters(uint8_t *bitset);
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -91,6 +91,16 @@ void dfsan_set_write_callback(dfsan_write_callback_t labeled_write_callback);
|
||||
/// <label> <parent label 1> <parent label 2> <label description if any>
|
||||
void dfsan_dump_labels(int fd);
|
||||
|
||||
/// Whenever a dfsan's custom function is called the corresponding
|
||||
/// hook is called it non-zero. The hooks should be defined by the user.
|
||||
/// The primary use case is taint-guided fuzzing, where the fuzzer
|
||||
/// needs to see the parameters of the function and the labels.
|
||||
/// FIXME: implement more hooks.
|
||||
|
||||
/// memcmp hook.
|
||||
void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2,
|
||||
size_t n, dfsan_label s1_label,
|
||||
dfsan_label s2_label, dfsan_label n_label);
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
|
@ -41,14 +41,25 @@ extern "C" {
|
||||
void __lsan_register_root_region(const void *p, size_t size);
|
||||
void __lsan_unregister_root_region(const void *p, size_t size);
|
||||
|
||||
// Calling this function makes LSan enter the leak checking phase immediately.
|
||||
// Use this if normal end-of-process leak checking happens too late (e.g. if
|
||||
// you have intentional memory leaks in your shutdown code). Calling this
|
||||
// function overrides end-of-process leak checking; it must be called at
|
||||
// most once per process. This function will terminate the process if there
|
||||
// are memory leaks and the exit_code flag is non-zero.
|
||||
// Check for leaks now. This function behaves identically to the default
|
||||
// end-of-process leak check. In particular, it will terminate the process if
|
||||
// leaks are found and the exit_code flag is non-zero.
|
||||
// Subsequent calls to this function will have no effect and end-of-process
|
||||
// leak check will not run. Effectively, end-of-process leak check is moved to
|
||||
// the time of first invocation of this function.
|
||||
// By calling this function early during process shutdown, you can instruct
|
||||
// LSan to ignore shutdown-only leaks which happen later on.
|
||||
void __lsan_do_leak_check();
|
||||
|
||||
// Check for leaks now. Returns zero if no leaks have been found or if leak
|
||||
// detection is disabled, non-zero otherwise.
|
||||
// This function may be called repeatedly, e.g. to periodically check a
|
||||
// long-running process. It prints a leak report if appropriate, but does not
|
||||
// terminate the process. It does not affect the behavior of
|
||||
// __lsan_do_leak_check() or the end-of-process leak check, and is not
|
||||
// affected by them.
|
||||
int __lsan_do_recoverable_leak_check();
|
||||
|
||||
// The user may optionally provide this function to disallow leak checking
|
||||
// for the program it is linked into (if the return value is non-zero). This
|
||||
// function must be defined as returning a constant value; any behavior beyond
|
||||
|
@ -25,6 +25,11 @@ extern "C" {
|
||||
/* Get raw origin for an address. */
|
||||
uint32_t __msan_get_origin(const volatile void *a);
|
||||
|
||||
/* Test that this_id is a descendant of prev_id (or they are simply equal).
|
||||
* "descendant" here means they are part of the same chain, created with
|
||||
* __msan_chain_origin. */
|
||||
int __msan_origin_is_descendant_or_same(uint32_t this_id, uint32_t prev_id);
|
||||
|
||||
/* Returns non-zero if tracking origins. */
|
||||
int __msan_get_track_origins();
|
||||
|
||||
|
@ -223,7 +223,7 @@ void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
|
||||
|
||||
struct Allocator {
|
||||
static const uptr kMaxAllowedMallocSize =
|
||||
FIRST_32_SECOND_64(3UL << 30, 64UL << 30);
|
||||
FIRST_32_SECOND_64(3UL << 30, 1UL << 40);
|
||||
static const uptr kMaxThreadLocalQuarantine =
|
||||
FIRST_32_SECOND_64(1 << 18, 1 << 20);
|
||||
|
||||
@ -354,7 +354,7 @@ struct Allocator {
|
||||
}
|
||||
CHECK(IsAligned(needed_size, min_alignment));
|
||||
if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
|
||||
Report("WARNING: AddressSanitizer failed to allocate %p bytes\n",
|
||||
Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n",
|
||||
(void*)size);
|
||||
return allocator.ReturnNullOrDie();
|
||||
}
|
||||
@ -437,11 +437,10 @@ struct Allocator {
|
||||
thread_stats.mallocs++;
|
||||
thread_stats.malloced += size;
|
||||
thread_stats.malloced_redzones += needed_size - size;
|
||||
uptr class_id =
|
||||
Min(kNumberOfSizeClasses, SizeClassMap::ClassID(needed_size));
|
||||
thread_stats.malloced_by_size[class_id]++;
|
||||
if (needed_size > SizeClassMap::kMaxSize)
|
||||
thread_stats.malloc_large++;
|
||||
else
|
||||
thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++;
|
||||
|
||||
void *res = reinterpret_cast<void *>(user_beg);
|
||||
if (can_fill && fl.max_malloc_fill_size) {
|
||||
|
@ -29,7 +29,6 @@ enum AllocType {
|
||||
FROM_NEW_BR = 3 // Memory block came from operator new [ ]
|
||||
};
|
||||
|
||||
static const uptr kNumberOfSizeClasses = 255;
|
||||
struct AsanChunk;
|
||||
|
||||
struct AllocatorOptions {
|
||||
@ -137,6 +136,7 @@ typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, 16,
|
||||
AsanMapUnmapCallback> PrimaryAllocator;
|
||||
#endif // SANITIZER_CAN_USE_ALLOCATOR64
|
||||
|
||||
static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses;
|
||||
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
|
||||
typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
|
||||
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
|
||||
|
@ -22,6 +22,9 @@ static const u64 kMagic2 = (kMagic1 << 8) | kMagic1;
|
||||
static const u64 kMagic4 = (kMagic2 << 16) | kMagic2;
|
||||
static const u64 kMagic8 = (kMagic4 << 32) | kMagic4;
|
||||
|
||||
static const u64 kAllocaRedzoneSize = 32UL;
|
||||
static const u64 kAllocaRedzoneMask = 31UL;
|
||||
|
||||
// For small size classes inline PoisonShadow for better performance.
|
||||
ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) {
|
||||
CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3.
|
||||
@ -253,4 +256,24 @@ void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
|
||||
if (end) *end = reinterpret_cast<void*>(frame_end);
|
||||
return reinterpret_cast<void*>(frame->real_stack);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_alloca_poison(uptr addr, uptr size) {
|
||||
uptr LeftRedzoneAddr = addr - kAllocaRedzoneSize;
|
||||
uptr PartialRzAddr = addr + size;
|
||||
uptr RightRzAddr = (PartialRzAddr + kAllocaRedzoneMask) & ~kAllocaRedzoneMask;
|
||||
uptr PartialRzAligned = PartialRzAddr & ~(SHADOW_GRANULARITY - 1);
|
||||
FastPoisonShadow(LeftRedzoneAddr, kAllocaRedzoneSize, kAsanAllocaLeftMagic);
|
||||
FastPoisonShadowPartialRightRedzone(
|
||||
PartialRzAligned, PartialRzAddr % SHADOW_GRANULARITY,
|
||||
RightRzAddr - PartialRzAligned, kAsanAllocaRightMagic);
|
||||
FastPoisonShadow(RightRzAddr, kAllocaRedzoneSize, kAsanAllocaRightMagic);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_allocas_unpoison(uptr top, uptr bottom) {
|
||||
if ((!top) || (top > bottom)) return;
|
||||
REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0,
|
||||
(bottom - top) / SHADOW_GRANULARITY);
|
||||
}
|
||||
} // extern "C"
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
#include "sanitizer_common/sanitizer_flag_parser.h"
|
||||
#include "ubsan/ubsan_flags.h"
|
||||
#include "ubsan/ubsan_platform.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
@ -72,8 +74,8 @@ void InitializeFlags() {
|
||||
RegisterAsanFlags(&asan_parser, f);
|
||||
RegisterCommonFlags(&asan_parser);
|
||||
|
||||
// Set the default values and prepare for parsing LSan flags (which can also
|
||||
// overwrite common flags).
|
||||
// Set the default values and prepare for parsing LSan and UBSan flags
|
||||
// (which can also overwrite common flags).
|
||||
#if CAN_SANITIZE_LEAKS
|
||||
__lsan::Flags *lf = __lsan::flags();
|
||||
lf->SetDefaults();
|
||||
@ -83,6 +85,15 @@ void InitializeFlags() {
|
||||
RegisterCommonFlags(&lsan_parser);
|
||||
#endif
|
||||
|
||||
#if CAN_SANITIZE_UB
|
||||
__ubsan::Flags *uf = __ubsan::flags();
|
||||
uf->SetDefaults();
|
||||
|
||||
FlagParser ubsan_parser;
|
||||
__ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
|
||||
RegisterCommonFlags(&ubsan_parser);
|
||||
#endif
|
||||
|
||||
// Override from ASan compile definition.
|
||||
const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition();
|
||||
asan_parser.ParseString(asan_compile_def);
|
||||
@ -90,12 +101,19 @@ void InitializeFlags() {
|
||||
// Override from user-specified string.
|
||||
const char *asan_default_options = MaybeCallAsanDefaultOptions();
|
||||
asan_parser.ParseString(asan_default_options);
|
||||
#if CAN_SANITIZE_UB
|
||||
const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
|
||||
ubsan_parser.ParseString(ubsan_default_options);
|
||||
#endif
|
||||
|
||||
// Override from command line.
|
||||
asan_parser.ParseString(GetEnv("ASAN_OPTIONS"));
|
||||
#if CAN_SANITIZE_LEAKS
|
||||
lsan_parser.ParseString(GetEnv("LSAN_OPTIONS"));
|
||||
#endif
|
||||
#if CAN_SANITIZE_UB
|
||||
ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
|
||||
#endif
|
||||
|
||||
// Let activation flags override current settings. On Android they come
|
||||
// from a system property. On other platforms this is no-op.
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "asan_suppressions.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
@ -73,7 +74,7 @@ ALWAYS_INLINE void PoisonRedZones(const Global &g) {
|
||||
|
||||
const uptr kMinimalDistanceFromAnotherGlobal = 64;
|
||||
|
||||
bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
|
||||
static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
|
||||
if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
|
||||
if (addr >= g.beg + g.size_with_redzone) return false;
|
||||
return true;
|
||||
@ -90,46 +91,8 @@ static void ReportGlobal(const Global &g, const char *prefix) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool DescribeOrGetInfoIfGlobal(uptr addr, uptr size, bool print,
|
||||
Global *output_global) {
|
||||
if (!flags()->report_globals) return false;
|
||||
BlockingMutexLock lock(&mu_for_globals);
|
||||
bool res = false;
|
||||
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
|
||||
const Global &g = *l->g;
|
||||
if (print) {
|
||||
if (flags()->report_globals >= 2)
|
||||
ReportGlobal(g, "Search");
|
||||
res |= DescribeAddressRelativeToGlobal(addr, size, g);
|
||||
} else {
|
||||
if (IsAddressNearGlobal(addr, g)) {
|
||||
CHECK(output_global);
|
||||
*output_global = g;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DescribeAddressIfGlobal(uptr addr, uptr size) {
|
||||
return DescribeOrGetInfoIfGlobal(addr, size, /* print */ true,
|
||||
/* output_global */ nullptr);
|
||||
}
|
||||
|
||||
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
|
||||
Global g = {};
|
||||
if (DescribeOrGetInfoIfGlobal(addr, /* size */ 1, /* print */ false, &g)) {
|
||||
internal_strncpy(descr->name, g.name, descr->name_size);
|
||||
descr->region_address = g.beg;
|
||||
descr->region_size = g.size;
|
||||
descr->region_kind = "global";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 FindRegistrationSite(const Global *g) {
|
||||
static u32 FindRegistrationSite(const Global *g) {
|
||||
mu_for_globals.CheckLocked();
|
||||
CHECK(global_registration_site_vector);
|
||||
for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
|
||||
GlobalRegistrationSite &grs = (*global_registration_site_vector)[i];
|
||||
@ -139,6 +102,38 @@ u32 FindRegistrationSite(const Global *g) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites,
|
||||
int max_globals) {
|
||||
if (!flags()->report_globals) return 0;
|
||||
BlockingMutexLock lock(&mu_for_globals);
|
||||
int res = 0;
|
||||
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
|
||||
const Global &g = *l->g;
|
||||
if (flags()->report_globals >= 2)
|
||||
ReportGlobal(g, "Search");
|
||||
if (IsAddressNearGlobal(addr, g)) {
|
||||
globals[res] = g;
|
||||
if (reg_sites)
|
||||
reg_sites[res] = FindRegistrationSite(&g);
|
||||
res++;
|
||||
if (res == max_globals) break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
|
||||
Global g = {};
|
||||
if (GetGlobalsForAddress(addr, &g, nullptr, 1)) {
|
||||
internal_strncpy(descr->name, g.name, descr->name_size);
|
||||
descr->region_address = g.beg;
|
||||
descr->region_size = g.size;
|
||||
descr->region_kind = "global";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register a global variable.
|
||||
// This function may be called more than once for every global
|
||||
// so we store the globals in a map.
|
||||
@ -158,7 +153,8 @@ static void RegisterGlobal(const Global *g) {
|
||||
// the entire redzone of the second global may be within the first global.
|
||||
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
|
||||
if (g->beg == l->g->beg &&
|
||||
(flags()->detect_odr_violation >= 2 || g->size != l->g->size))
|
||||
(flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
|
||||
!IsODRViolationSuppressed(g->name))
|
||||
ReportODRViolation(g, FindRegistrationSite(g),
|
||||
l->g, FindRegistrationSite(l->g));
|
||||
}
|
||||
@ -210,20 +206,6 @@ void StopInitOrderChecking() {
|
||||
}
|
||||
}
|
||||
|
||||
#if SANITIZER_WINDOWS // Should only be called on Windows.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void UnregisterGlobalsInRange(void *beg, void *end) {
|
||||
if (!flags()->report_globals)
|
||||
return;
|
||||
BlockingMutexLock lock(&mu_for_globals);
|
||||
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
|
||||
void *address = (void *)l->g->beg;
|
||||
if (beg <= address && address < end)
|
||||
UnregisterGlobal(l->g);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
// ---------------------- Interface ---------------- {{{1
|
||||
@ -232,7 +214,7 @@ using namespace __asan; // NOLINT
|
||||
// Register an array of globals.
|
||||
void __asan_register_globals(__asan_global *globals, uptr n) {
|
||||
if (!flags()->report_globals) return;
|
||||
GET_STACK_TRACE_FATAL_HERE;
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
u32 stack_id = StackDepotPut(stack);
|
||||
BlockingMutexLock lock(&mu_for_globals);
|
||||
if (!global_registration_site_vector)
|
||||
|
@ -23,6 +23,10 @@
|
||||
#include "asan_suppressions.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
#if SANITIZER_POSIX
|
||||
#include "sanitizer_common/sanitizer_posix.h"
|
||||
#endif
|
||||
|
||||
namespace __asan {
|
||||
|
||||
// Return true if we can quickly decide that the region is unpoisoned.
|
||||
@ -65,7 +69,7 @@ struct AsanInterceptorContext {
|
||||
} \
|
||||
if (!suppressed) { \
|
||||
GET_CURRENT_PC_BP_SP; \
|
||||
__asan_report_error(pc, bp, sp, __bad, isWrite, __size); \
|
||||
__asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
@ -75,6 +79,13 @@ struct AsanInterceptorContext {
|
||||
#define ASAN_WRITE_RANGE(ctx, offset, size) \
|
||||
ACCESS_MEMORY_RANGE(ctx, offset, size, true)
|
||||
|
||||
#define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \
|
||||
ASAN_READ_RANGE((ctx), (s), \
|
||||
common_flags()->strict_string_checks ? (len) + 1 : (n))
|
||||
|
||||
#define ASAN_READ_STRING(ctx, s, n) \
|
||||
ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
|
||||
|
||||
// Behavior of functions like "memcpy" or "strcpy" is undefined
|
||||
// if memory intervals overlap. We report error in this case.
|
||||
// Macro is used to avoid creation of new frames.
|
||||
@ -120,17 +131,6 @@ using namespace __asan; // NOLINT
|
||||
DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
|
||||
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
|
||||
|
||||
#if !SANITIZER_MAC
|
||||
#define ASAN_INTERCEPT_FUNC(name) \
|
||||
do { \
|
||||
if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
|
||||
VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \
|
||||
} while (0)
|
||||
#else
|
||||
// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
|
||||
#define ASAN_INTERCEPT_FUNC(name)
|
||||
#endif // SANITIZER_MAC
|
||||
|
||||
#define ASAN_INTERCEPTOR_ENTER(ctx, func) \
|
||||
AsanInterceptorContext _ctx = {#func}; \
|
||||
ctx = (void *)&_ctx; \
|
||||
@ -171,11 +171,24 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
|
||||
do { \
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
|
||||
// Strict init-order checking is dlopen-hostile:
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=178
|
||||
#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \
|
||||
if (flags()->strict_init_order) { \
|
||||
StopInitOrderChecking(); \
|
||||
}
|
||||
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
|
||||
#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \
|
||||
CoverageUpdateMapping()
|
||||
#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CoverageUpdateMapping()
|
||||
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited)
|
||||
#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
|
||||
if (AsanThread *t = GetCurrentThread()) { \
|
||||
*begin = t->tls_begin(); \
|
||||
*end = t->tls_end(); \
|
||||
} else { \
|
||||
*begin = *end = 0; \
|
||||
}
|
||||
#include "sanitizer_common/sanitizer_common_interceptors.inc"
|
||||
|
||||
// Syscall interceptors don't have contexts, we don't support suppressions
|
||||
@ -200,12 +213,6 @@ struct ThreadStartParam {
|
||||
};
|
||||
|
||||
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
|
||||
#if SANITIZER_WINDOWS
|
||||
// FIXME: this is a bandaid fix for PR22025.
|
||||
AsanThread *t = (AsanThread*)arg;
|
||||
SetCurrentThread(t);
|
||||
return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
|
||||
#else
|
||||
ThreadStartParam *param = reinterpret_cast<ThreadStartParam *>(arg);
|
||||
AsanThread *t = nullptr;
|
||||
while ((t = reinterpret_cast<AsanThread *>(
|
||||
@ -213,7 +220,6 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
|
||||
internal_sched_yield();
|
||||
SetCurrentThread(t);
|
||||
return t->ThreadStart(GetTid(), ¶m->is_registered);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ASAN_INTERCEPT_PTHREAD_CREATE
|
||||
@ -302,7 +308,7 @@ static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) {
|
||||
ssize += stack - bottom;
|
||||
ssize = RoundUpTo(ssize, PageSize);
|
||||
static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb
|
||||
if (ssize && ssize <= kMaxSaneContextStackSize) {
|
||||
if (AddrIsInMem(bottom) && ssize && ssize <= kMaxSaneContextStackSize) {
|
||||
PoisonShadow(bottom, ssize, 0);
|
||||
}
|
||||
}
|
||||
@ -357,30 +363,6 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SANITIZER_WINDOWS
|
||||
INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
|
||||
CHECK(REAL(RaiseException));
|
||||
__asan_handle_no_return();
|
||||
REAL(RaiseException)(a, b, c, d);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
|
||||
CHECK(REAL(_except_handler3));
|
||||
__asan_handle_no_return();
|
||||
return REAL(_except_handler3)(a, b, c, d);
|
||||
}
|
||||
|
||||
#if ASAN_DYNAMIC
|
||||
// This handler is named differently in -MT and -MD CRTs.
|
||||
#define _except_handler4 _except_handler4_common
|
||||
#endif
|
||||
INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
|
||||
CHECK(REAL(_except_handler4));
|
||||
__asan_handle_no_return();
|
||||
return REAL(_except_handler4)(a, b, c, d);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int CharCmp(unsigned char c1, unsigned char c2) {
|
||||
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
|
||||
}
|
||||
@ -511,8 +493,9 @@ INTERCEPTOR(char*, strchr, const char *str, int c) {
|
||||
ENSURE_ASAN_INITED();
|
||||
char *result = REAL(strchr)(str, c);
|
||||
if (flags()->replace_str) {
|
||||
uptr bytes_read = (result ? result - str : REAL(strlen)(str)) + 1;
|
||||
ASAN_READ_RANGE(ctx, str, bytes_read);
|
||||
uptr len = REAL(strlen)(str);
|
||||
uptr bytes_read = (result ? result - str : len) + 1;
|
||||
ASAN_READ_STRING_OF_LEN(ctx, str, len, bytes_read);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -541,7 +524,7 @@ INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
|
||||
uptr from_length = REAL(strlen)(from);
|
||||
ASAN_READ_RANGE(ctx, from, from_length + 1);
|
||||
uptr to_length = REAL(strlen)(to);
|
||||
ASAN_READ_RANGE(ctx, to, to_length);
|
||||
ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
|
||||
ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
|
||||
// If the copying actually happens, the |from| string should not overlap
|
||||
// with the resulting string starting at |to|, which has a length of
|
||||
@ -563,7 +546,7 @@ INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
|
||||
uptr copy_length = Min(size, from_length + 1);
|
||||
ASAN_READ_RANGE(ctx, from, copy_length);
|
||||
uptr to_length = REAL(strlen)(to);
|
||||
ASAN_READ_RANGE(ctx, to, to_length);
|
||||
ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
|
||||
ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
|
||||
if (from_length > 0) {
|
||||
CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1,
|
||||
@ -665,23 +648,6 @@ INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) {
|
||||
}
|
||||
#endif // ASAN_INTERCEPT_STRNLEN
|
||||
|
||||
static inline bool IsValidStrtolBase(int base) {
|
||||
return (base == 0) || (2 <= base && base <= 36);
|
||||
}
|
||||
|
||||
static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
|
||||
CHECK(endptr);
|
||||
if (nptr == *endptr) {
|
||||
// No digits were found at strtol call, we need to find out the last
|
||||
// symbol accessed by strtoll on our own.
|
||||
// We get this symbol by skipping leading blanks and optional +/- sign.
|
||||
while (IsSpace(*nptr)) nptr++;
|
||||
if (*nptr == '+' || *nptr == '-') nptr++;
|
||||
*endptr = const_cast<char *>(nptr);
|
||||
}
|
||||
CHECK(*endptr >= nptr);
|
||||
}
|
||||
|
||||
INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
|
||||
char **endptr, int base) {
|
||||
void *ctx;
|
||||
@ -692,13 +658,7 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
|
||||
}
|
||||
char *real_endptr;
|
||||
long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT
|
||||
if (endptr != 0) {
|
||||
*endptr = real_endptr;
|
||||
}
|
||||
if (IsValidStrtolBase(base)) {
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
|
||||
}
|
||||
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -719,7 +679,7 @@ INTERCEPTOR(int, atoi, const char *nptr) {
|
||||
// different from int). So, we just imitate this behavior.
|
||||
int result = REAL(strtol)(nptr, &real_endptr, 10);
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
|
||||
ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -736,7 +696,7 @@ INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
|
||||
char *real_endptr;
|
||||
long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
|
||||
ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -751,16 +711,7 @@ INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT
|
||||
}
|
||||
char *real_endptr;
|
||||
long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT
|
||||
if (endptr != 0) {
|
||||
*endptr = real_endptr;
|
||||
}
|
||||
// If base has unsupported value, strtoll can exit with EINVAL
|
||||
// without reading any characters. So do additional checks only
|
||||
// if base is valid.
|
||||
if (IsValidStrtolBase(base)) {
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
|
||||
}
|
||||
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -774,7 +725,7 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT
|
||||
char *real_endptr;
|
||||
long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
|
||||
ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
|
||||
return result;
|
||||
}
|
||||
#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL
|
||||
@ -807,36 +758,6 @@ INTERCEPTOR(int, fork, void) {
|
||||
}
|
||||
#endif // ASAN_INTERCEPT_FORK
|
||||
|
||||
#if SANITIZER_WINDOWS
|
||||
INTERCEPTOR_WINAPI(DWORD, CreateThread,
|
||||
void* security, uptr stack_size,
|
||||
DWORD (__stdcall *start_routine)(void*), void* arg,
|
||||
DWORD thr_flags, void* tid) {
|
||||
// Strict init-order checking is thread-hostile.
|
||||
if (flags()->strict_init_order)
|
||||
StopInitOrderChecking();
|
||||
GET_STACK_TRACE_THREAD;
|
||||
// FIXME: The CreateThread interceptor is not the same as a pthread_create
|
||||
// one. This is a bandaid fix for PR22025.
|
||||
bool detached = false; // FIXME: how can we determine it on Windows?
|
||||
u32 current_tid = GetCurrentTidOrInvalid();
|
||||
AsanThread *t =
|
||||
AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
|
||||
return REAL(CreateThread)(security, stack_size,
|
||||
asan_thread_start, t, thr_flags, tid);
|
||||
}
|
||||
|
||||
namespace __asan {
|
||||
void InitializeWindowsInterceptors() {
|
||||
ASAN_INTERCEPT_FUNC(CreateThread);
|
||||
ASAN_INTERCEPT_FUNC(RaiseException);
|
||||
ASAN_INTERCEPT_FUNC(_except_handler3);
|
||||
ASAN_INTERCEPT_FUNC(_except_handler4);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
#endif
|
||||
|
||||
// ---------------------- InitializeAsanInterceptors ---------------- {{{1
|
||||
namespace __asan {
|
||||
void InitializeAsanInterceptors() {
|
||||
@ -919,10 +840,7 @@ void InitializeAsanInterceptors() {
|
||||
ASAN_INTERCEPT_FUNC(fork);
|
||||
#endif
|
||||
|
||||
// Some Windows-specific interceptors.
|
||||
#if SANITIZER_WINDOWS
|
||||
InitializeWindowsInterceptors();
|
||||
#endif
|
||||
InitializePlatformInterceptors();
|
||||
|
||||
VReport(1, "AddressSanitizer: libc interceptors initialized\n");
|
||||
}
|
||||
|
@ -92,9 +92,21 @@ struct sigaction;
|
||||
DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact)
|
||||
|
||||
#if !SANITIZER_MAC
|
||||
#define ASAN_INTERCEPT_FUNC(name) \
|
||||
do { \
|
||||
if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
|
||||
VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \
|
||||
} while (0)
|
||||
#else
|
||||
// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
|
||||
#define ASAN_INTERCEPT_FUNC(name)
|
||||
#endif // SANITIZER_MAC
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void InitializeAsanInterceptors();
|
||||
void InitializePlatformInterceptors();
|
||||
|
||||
#define ENSURE_ASAN_INITED() do { \
|
||||
CHECK(!asan_init_is_running); \
|
||||
|
@ -128,7 +128,7 @@ extern "C" {
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_report_error(uptr pc, uptr bp, uptr sp,
|
||||
uptr addr, int is_write, uptr access_size);
|
||||
uptr addr, int is_write, uptr access_size, u32 exp);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __asan_set_error_exit_code(int exit_code);
|
||||
@ -165,6 +165,21 @@ extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN(uptr p, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN(uptr p, uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load1(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load2(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load4(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load8(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load16(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store1(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store2(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store4(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store8(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store16(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_loadN(uptr p, uptr size,
|
||||
u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_storeN(uptr p, uptr size,
|
||||
u32 exp);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void* __asan_memcpy(void *dst, const void *src, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
@ -180,6 +195,10 @@ extern "C" {
|
||||
void __asan_poison_intra_object_redzone(uptr p, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_unpoison_intra_object_redzone(uptr p, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_alloca_poison(uptr addr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_allocas_unpoison(uptr top, uptr bottom);
|
||||
} // extern "C"
|
||||
|
||||
#endif // ASAN_INTERFACE_INTERNAL_H
|
||||
|
@ -62,21 +62,6 @@ namespace __asan {
|
||||
class AsanThread;
|
||||
using __sanitizer::StackTrace;
|
||||
|
||||
struct SignalContext {
|
||||
void *context;
|
||||
uptr addr;
|
||||
uptr pc;
|
||||
uptr sp;
|
||||
uptr bp;
|
||||
|
||||
SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) :
|
||||
context(context), addr(addr), pc(pc), sp(sp), bp(bp) {
|
||||
}
|
||||
|
||||
// Creates signal context in a platform-specific manner.
|
||||
static SignalContext Create(void *siginfo, void *context);
|
||||
};
|
||||
|
||||
void AsanInitFromRtl();
|
||||
|
||||
// asan_rtl.cc
|
||||
@ -90,7 +75,6 @@ void *AsanDoesNotSupportStaticLinkage();
|
||||
void AsanCheckDynamicRTPrereqs();
|
||||
void AsanCheckIncompatibleRT();
|
||||
|
||||
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
|
||||
void AsanOnSIGSEGV(int, void *siginfo, void *context);
|
||||
|
||||
void DisableReexec();
|
||||
@ -109,7 +93,7 @@ void AppendToErrorMessageBuffer(const char *buffer);
|
||||
|
||||
void *AsanDlSymNext(const char *sym);
|
||||
|
||||
void ReserveShadowMemoryRange(uptr beg, uptr end);
|
||||
void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);
|
||||
|
||||
// Platform-specific options.
|
||||
#if SANITIZER_MAC
|
||||
|
@ -68,6 +68,8 @@ asan_rt_version_t __asan_rt_version;
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void InitializePlatformInterceptors() {}
|
||||
|
||||
void DisableReexec() {
|
||||
// No need to re-exec on Linux.
|
||||
}
|
||||
@ -111,6 +113,9 @@ static void ReportIncompatibleRT() {
|
||||
}
|
||||
|
||||
void AsanCheckDynamicRTPrereqs() {
|
||||
if (!ASAN_DYNAMIC)
|
||||
return;
|
||||
|
||||
// Ensure that dynamic RT is the first DSO in the list
|
||||
const char *first_dso_name = 0;
|
||||
dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name);
|
||||
@ -152,78 +157,6 @@ void AsanCheckIncompatibleRT() {
|
||||
}
|
||||
#endif // SANITIZER_ANDROID
|
||||
|
||||
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
#if defined(__arm__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.arm_pc;
|
||||
*bp = ucontext->uc_mcontext.arm_fp;
|
||||
*sp = ucontext->uc_mcontext.arm_sp;
|
||||
#elif defined(__aarch64__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.pc;
|
||||
*bp = ucontext->uc_mcontext.regs[29];
|
||||
*sp = ucontext->uc_mcontext.sp;
|
||||
#elif defined(__hppa__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.sc_iaoq[0];
|
||||
/* GCC uses %r3 whenever a frame pointer is needed. */
|
||||
*bp = ucontext->uc_mcontext.sc_gr[3];
|
||||
*sp = ucontext->uc_mcontext.sc_gr[30];
|
||||
#elif defined(__x86_64__)
|
||||
# if SANITIZER_FREEBSD
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.mc_rip;
|
||||
*bp = ucontext->uc_mcontext.mc_rbp;
|
||||
*sp = ucontext->uc_mcontext.mc_rsp;
|
||||
# else
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.gregs[REG_RIP];
|
||||
*bp = ucontext->uc_mcontext.gregs[REG_RBP];
|
||||
*sp = ucontext->uc_mcontext.gregs[REG_RSP];
|
||||
# endif
|
||||
#elif defined(__i386__)
|
||||
# if SANITIZER_FREEBSD
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.mc_eip;
|
||||
*bp = ucontext->uc_mcontext.mc_ebp;
|
||||
*sp = ucontext->uc_mcontext.mc_esp;
|
||||
# else
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.gregs[REG_EIP];
|
||||
*bp = ucontext->uc_mcontext.gregs[REG_EBP];
|
||||
*sp = ucontext->uc_mcontext.gregs[REG_ESP];
|
||||
# endif
|
||||
#elif defined(__powerpc__) || defined(__powerpc64__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.regs->nip;
|
||||
*sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
|
||||
// The powerpc{,64}-linux ABIs do not specify r31 as the frame
|
||||
// pointer, but GCC always uses r31 when we need a frame pointer.
|
||||
*bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
|
||||
#elif defined(__sparc__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
uptr *stk_ptr;
|
||||
# if defined (__arch64__)
|
||||
*pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
|
||||
*sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
|
||||
stk_ptr = (uptr *) (*sp + 2047);
|
||||
*bp = stk_ptr[15];
|
||||
# else
|
||||
*pc = ucontext->uc_mcontext.gregs[REG_PC];
|
||||
*sp = ucontext->uc_mcontext.gregs[REG_O6];
|
||||
stk_ptr = (uptr *) *sp;
|
||||
*bp = stk_ptr[15];
|
||||
# endif
|
||||
#elif defined(__mips__)
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
*pc = ucontext->uc_mcontext.gregs[31];
|
||||
*bp = ucontext->uc_mcontext.gregs[30];
|
||||
*sp = ucontext->uc_mcontext.gregs[29];
|
||||
#else
|
||||
# error "Unsupported arch"
|
||||
#endif
|
||||
}
|
||||
|
||||
void AsanPlatformThreadInit() {
|
||||
// Nothing here for now.
|
||||
}
|
||||
|
@ -24,7 +24,14 @@
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_mac.h"
|
||||
|
||||
#include <crt_externs.h> // for _NSGetArgv
|
||||
#if !SANITIZER_IOS
|
||||
#include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron
|
||||
#else
|
||||
extern "C" {
|
||||
extern char ***_NSGetArgv(void);
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h> // for dladdr()
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/loader.h>
|
||||
@ -40,19 +47,7 @@
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
# if SANITIZER_WORDSIZE == 64
|
||||
*pc = ucontext->uc_mcontext->__ss.__rip;
|
||||
*bp = ucontext->uc_mcontext->__ss.__rbp;
|
||||
*sp = ucontext->uc_mcontext->__ss.__rsp;
|
||||
# else
|
||||
*pc = ucontext->uc_mcontext->__ss.__eip;
|
||||
*bp = ucontext->uc_mcontext->__ss.__ebp;
|
||||
*sp = ucontext->uc_mcontext->__ss.__esp;
|
||||
# endif // SANITIZER_WORDSIZE
|
||||
}
|
||||
|
||||
void InitializePlatformInterceptors() {}
|
||||
|
||||
bool PlatformHasDifferentMemcpyAndMemmove() {
|
||||
// On OS X 10.7 memcpy() and memmove() are both resolved
|
||||
@ -74,30 +69,27 @@ LowLevelAllocator allocator_for_env;
|
||||
// otherwise the corresponding "NAME=value" string is replaced with
|
||||
// |name_value|.
|
||||
void LeakyResetEnv(const char *name, const char *name_value) {
|
||||
char ***env_ptr = _NSGetEnviron();
|
||||
CHECK(env_ptr);
|
||||
char **environ = *env_ptr;
|
||||
CHECK(environ);
|
||||
char **env = GetEnviron();
|
||||
uptr name_len = internal_strlen(name);
|
||||
while (*environ != 0) {
|
||||
uptr len = internal_strlen(*environ);
|
||||
while (*env != 0) {
|
||||
uptr len = internal_strlen(*env);
|
||||
if (len > name_len) {
|
||||
const char *p = *environ;
|
||||
const char *p = *env;
|
||||
if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
|
||||
// Match.
|
||||
if (name_value) {
|
||||
// Replace the old value with the new one.
|
||||
*environ = const_cast<char*>(name_value);
|
||||
*env = const_cast<char*>(name_value);
|
||||
} else {
|
||||
// Shift the subsequent pointers back.
|
||||
char **del = environ;
|
||||
char **del = env;
|
||||
do {
|
||||
del[0] = del[1];
|
||||
} while (*del++);
|
||||
}
|
||||
}
|
||||
}
|
||||
environ++;
|
||||
env++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,6 +99,23 @@ void DisableReexec() {
|
||||
reexec_disabled = true;
|
||||
}
|
||||
|
||||
bool DyldNeedsEnvVariable() {
|
||||
// If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if
|
||||
// DYLD_INSERT_LIBRARIES is not set.
|
||||
|
||||
#if SANITIZER_IOSSIM
|
||||
// GetMacosVersion will not work for the simulator, whose kernel version
|
||||
// is tied to the host. Use a weak linking hack for the simulator.
|
||||
// This API was introduced in the same version of the OS as the dyld
|
||||
// optimization.
|
||||
|
||||
// Check for presence of a symbol that is available on OS X 10.11+, iOS 9.0+.
|
||||
return (dlsym(RTLD_NEXT, "mach_memory_info") == nullptr);
|
||||
#else
|
||||
return (GetMacosVersion() <= MACOS_VERSION_YOSEMITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MaybeReexec() {
|
||||
if (reexec_disabled) return;
|
||||
|
||||
@ -122,8 +131,10 @@ void MaybeReexec() {
|
||||
uptr fname_len = internal_strlen(info.dli_fname);
|
||||
const char *dylib_name = StripModuleName(info.dli_fname);
|
||||
uptr dylib_name_len = internal_strlen(dylib_name);
|
||||
if (!dyld_insert_libraries ||
|
||||
!REAL(strstr)(dyld_insert_libraries, dylib_name)) {
|
||||
|
||||
bool lib_is_in_env =
|
||||
dyld_insert_libraries && REAL(strstr)(dyld_insert_libraries, dylib_name);
|
||||
if (DyldNeedsEnvVariable() && !lib_is_in_env) {
|
||||
// DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
|
||||
// library.
|
||||
char program_name[1024];
|
||||
@ -160,6 +171,9 @@ void MaybeReexec() {
|
||||
CHECK("execv failed" && 0);
|
||||
}
|
||||
|
||||
if (!lib_is_in_env)
|
||||
return;
|
||||
|
||||
// DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
|
||||
// the dylib from the environment variable, because interceptors are installed
|
||||
// and we don't want our children to inherit the variable.
|
||||
|
@ -98,9 +98,12 @@
|
||||
|
||||
static const u64 kDefaultShadowScale = 3;
|
||||
static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000
|
||||
static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000
|
||||
static const u64 kDefaultShadowOffset64 = 1ULL << 44;
|
||||
static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G.
|
||||
static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000
|
||||
static const u64 kIosShadowOffset64 = 0x130000000;
|
||||
static const u64 kIosSimShadowOffset32 = 1ULL << 30;
|
||||
static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64;
|
||||
static const u64 kAArch64_ShadowOffset64 = 1ULL << 36;
|
||||
static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000;
|
||||
static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37;
|
||||
@ -118,10 +121,12 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
|
||||
# define SHADOW_OFFSET kMIPS32_ShadowOffset32
|
||||
# elif SANITIZER_FREEBSD
|
||||
# define SHADOW_OFFSET kFreeBSD_ShadowOffset32
|
||||
# elif SANITIZER_IOS
|
||||
# define SHADOW_OFFSET kIosShadowOffset32
|
||||
# elif SANITIZER_WINDOWS
|
||||
# define SHADOW_OFFSET kWindowsShadowOffset32
|
||||
# elif SANITIZER_IOSSIM
|
||||
# define SHADOW_OFFSET kIosSimShadowOffset32
|
||||
# elif SANITIZER_IOS
|
||||
# define SHADOW_OFFSET kIosShadowOffset32
|
||||
# else
|
||||
# define SHADOW_OFFSET kDefaultShadowOffset32
|
||||
# endif
|
||||
@ -136,6 +141,10 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
|
||||
# define SHADOW_OFFSET kDefaultShadowOffset64
|
||||
# elif defined(__mips64)
|
||||
# define SHADOW_OFFSET kMIPS64_ShadowOffset64
|
||||
# elif SANITIZER_IOSSIM
|
||||
# define SHADOW_OFFSET kIosSimShadowOffset64
|
||||
# elif SANITIZER_IOS
|
||||
# define SHADOW_OFFSET kIosShadowOffset64
|
||||
# else
|
||||
# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
|
||||
# endif
|
||||
|
@ -112,7 +112,7 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) {
|
||||
if (!flags()->allow_user_poisoning || size == 0) return;
|
||||
uptr beg_addr = (uptr)addr;
|
||||
uptr end_addr = beg_addr + size;
|
||||
VPrintf(1, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr,
|
||||
VPrintf(3, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr,
|
||||
(void *)end_addr);
|
||||
ShadowSegmentEndpoint beg(beg_addr);
|
||||
ShadowSegmentEndpoint end(end_addr);
|
||||
@ -152,7 +152,7 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
|
||||
if (!flags()->allow_user_poisoning || size == 0) return;
|
||||
uptr beg_addr = (uptr)addr;
|
||||
uptr end_addr = beg_addr + size;
|
||||
VPrintf(1, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr,
|
||||
VPrintf(3, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr,
|
||||
(void *)end_addr);
|
||||
ShadowSegmentEndpoint beg(beg_addr);
|
||||
ShadowSegmentEndpoint end(end_addr);
|
||||
@ -218,7 +218,7 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) {
|
||||
__asan::AddressIsPoisoned(__p + __size - 1))) { \
|
||||
GET_CURRENT_PC_BP_SP; \
|
||||
uptr __bad = __asan_region_is_poisoned(__p, __size); \
|
||||
__asan_report_error(pc, bp, sp, __bad, isWrite, __size);\
|
||||
__asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\
|
||||
} \
|
||||
} while (false); \
|
||||
|
||||
|
@ -64,7 +64,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
|
||||
if (page_end != shadow_end) {
|
||||
REAL(memset)((void *)page_end, 0, shadow_end - page_end);
|
||||
}
|
||||
ReserveShadowMemoryRange(page_beg, page_end - 1);
|
||||
ReserveShadowMemoryRange(page_beg, page_end - 1, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_posix.h"
|
||||
#include "sanitizer_common/sanitizer_procmaps.h"
|
||||
|
||||
#include <pthread.h>
|
||||
@ -32,13 +33,6 @@
|
||||
|
||||
namespace __asan {
|
||||
|
||||
SignalContext SignalContext::Create(void *siginfo, void *context) {
|
||||
uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
|
||||
uptr pc, sp, bp;
|
||||
GetPcSpBp(context, &pc, &sp, &bp);
|
||||
return SignalContext(context, addr, pc, sp, bp);
|
||||
}
|
||||
|
||||
void AsanOnSIGSEGV(int, void *siginfo, void *context) {
|
||||
ScopedDeadlySignal signal_scope(GetCurrentThread());
|
||||
int code = (int)((siginfo_t*)siginfo)->si_code;
|
||||
|
@ -281,9 +281,8 @@ static void PrintGlobalLocation(InternalScopedString *str,
|
||||
str->append(":%d", g.location->column_no);
|
||||
}
|
||||
|
||||
bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
|
||||
const __asan_global &g) {
|
||||
if (!IsAddressNearGlobal(addr, g)) return false;
|
||||
static void DescribeAddressRelativeToGlobal(uptr addr, uptr size,
|
||||
const __asan_global &g) {
|
||||
InternalScopedString str(4096);
|
||||
Decorator d;
|
||||
str.append("%s", d.Location());
|
||||
@ -306,6 +305,26 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
|
||||
str.append("%s", d.EndLocation());
|
||||
PrintGlobalNameIfASCII(&str, g);
|
||||
Printf("%s", str.data());
|
||||
}
|
||||
|
||||
static bool DescribeAddressIfGlobal(uptr addr, uptr size,
|
||||
const char *bug_type) {
|
||||
// Assume address is close to at most four globals.
|
||||
const int kMaxGlobalsInReport = 4;
|
||||
__asan_global globals[kMaxGlobalsInReport];
|
||||
u32 reg_sites[kMaxGlobalsInReport];
|
||||
int globals_num =
|
||||
GetGlobalsForAddress(addr, globals, reg_sites, ARRAY_SIZE(globals));
|
||||
if (globals_num == 0)
|
||||
return false;
|
||||
for (int i = 0; i < globals_num; i++) {
|
||||
DescribeAddressRelativeToGlobal(addr, size, globals[i]);
|
||||
if (0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
|
||||
reg_sites[i]) {
|
||||
Printf(" registered at:\n");
|
||||
StackDepotGet(reg_sites[i]).Print();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -551,12 +570,12 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
|
||||
DescribeThread(alloc_thread);
|
||||
}
|
||||
|
||||
void DescribeAddress(uptr addr, uptr access_size) {
|
||||
static void DescribeAddress(uptr addr, uptr access_size, const char *bug_type) {
|
||||
// Check if this is shadow or shadow gap.
|
||||
if (DescribeAddressIfShadow(addr))
|
||||
return;
|
||||
CHECK(AddrIsInMem(addr));
|
||||
if (DescribeAddressIfGlobal(addr, access_size))
|
||||
if (DescribeAddressIfGlobal(addr, access_size, bug_type))
|
||||
return;
|
||||
if (DescribeAddressIfStack(addr, access_size))
|
||||
return;
|
||||
@ -578,6 +597,11 @@ void DescribeThread(AsanThreadContext *context) {
|
||||
InternalScopedString str(1024);
|
||||
str.append("Thread T%d%s", context->tid,
|
||||
ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
|
||||
if (context->parent_tid == kInvalidTid) {
|
||||
str.append(" created by unknown thread\n");
|
||||
Printf("%s", str.data());
|
||||
return;
|
||||
}
|
||||
str.append(
|
||||
" created by T%d%s here:\n", context->parent_tid,
|
||||
ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname)));
|
||||
@ -805,8 +829,8 @@ void ReportStringFunctionMemoryRangesOverlap(const char *function,
|
||||
bug_type, offset1, offset1 + length1, offset2, offset2 + length2);
|
||||
Printf("%s", d.EndWarning());
|
||||
stack->Print();
|
||||
DescribeAddress((uptr)offset1, length1);
|
||||
DescribeAddress((uptr)offset2, length2);
|
||||
DescribeAddress((uptr)offset1, length1, bug_type);
|
||||
DescribeAddress((uptr)offset2, length2, bug_type);
|
||||
ReportErrorSummary(bug_type, stack);
|
||||
}
|
||||
|
||||
@ -819,7 +843,7 @@ void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
|
||||
Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size);
|
||||
Printf("%s", d.EndWarning());
|
||||
stack->Print();
|
||||
DescribeAddress(offset, size);
|
||||
DescribeAddress(offset, size, bug_type);
|
||||
ReportErrorSummary(bug_type, stack);
|
||||
}
|
||||
|
||||
@ -834,6 +858,9 @@ void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
|
||||
" old_mid : %p\n"
|
||||
" new_mid : %p\n",
|
||||
beg, end, old_mid, new_mid);
|
||||
uptr granularity = SHADOW_GRANULARITY;
|
||||
if (!IsAligned(beg, granularity))
|
||||
Report("ERROR: beg is not aligned by %d\n", granularity);
|
||||
stack->Print();
|
||||
ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
|
||||
}
|
||||
@ -871,15 +898,16 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
|
||||
static NOINLINE void
|
||||
ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, uptr a1, uptr a2) {
|
||||
ScopedInErrorReport in_report;
|
||||
const char *bug_type = "invalid-pointer-pair";
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n", a1, a2);
|
||||
Printf("%s", d.EndWarning());
|
||||
GET_STACK_TRACE_FATAL(pc, bp);
|
||||
stack.Print();
|
||||
DescribeAddress(a1, 1);
|
||||
DescribeAddress(a2, 1);
|
||||
ReportErrorSummary("invalid-pointer-pair", &stack);
|
||||
DescribeAddress(a1, 1, bug_type);
|
||||
DescribeAddress(a2, 1, bug_type);
|
||||
ReportErrorSummary(bug_type, &stack);
|
||||
}
|
||||
|
||||
static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
|
||||
@ -936,9 +964,18 @@ void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
||||
uptr access_size) {
|
||||
uptr access_size, u32 exp) {
|
||||
ENABLE_FRAME_POINTER;
|
||||
|
||||
// Optimization experiments.
|
||||
// The experiments can be used to evaluate potential optimizations that remove
|
||||
// instrumentation (assess false negatives). Instead of completely removing
|
||||
// some instrumentation, compiler can emit special calls into runtime
|
||||
// (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass
|
||||
// mask of experiments (exp).
|
||||
// The reaction to a non-zero value of exp is to be defined.
|
||||
(void)exp;
|
||||
|
||||
// Determine the error type.
|
||||
const char *bug_descr = "unknown-crash";
|
||||
if (AddrIsInMem(addr)) {
|
||||
@ -1017,7 +1054,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
||||
GET_STACK_TRACE_FATAL(pc, bp);
|
||||
stack.Print();
|
||||
|
||||
DescribeAddress(addr, access_size);
|
||||
DescribeAddress(addr, access_size, bug_descr);
|
||||
ReportErrorSummary(bug_descr, &stack);
|
||||
PrintShadowMemoryForAddress(addr);
|
||||
}
|
||||
@ -1035,7 +1072,7 @@ void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
|
||||
void __asan_describe_address(uptr addr) {
|
||||
// Thread registry must be locked while we're describing an address.
|
||||
asanThreadRegistry().Lock();
|
||||
DescribeAddress(addr, 1);
|
||||
DescribeAddress(addr, 1, "");
|
||||
asanThreadRegistry().Unlock();
|
||||
}
|
||||
|
||||
|
@ -33,22 +33,19 @@ struct AddressDescription {
|
||||
const char *region_kind;
|
||||
};
|
||||
|
||||
// Returns the number of globals close to the provided address and copies
|
||||
// them to "globals" array.
|
||||
int GetGlobalsForAddress(uptr addr, __asan_global *globals, u32 *reg_sites,
|
||||
int max_globals);
|
||||
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
|
||||
// The following functions prints address description depending
|
||||
// on the memory type (shadow/heap/stack/global).
|
||||
void DescribeHeapAddress(uptr addr, uptr access_size);
|
||||
bool DescribeAddressIfGlobal(uptr addr, uptr access_size);
|
||||
bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
|
||||
const __asan_global &g);
|
||||
bool IsAddressNearGlobal(uptr addr, const __asan_global &g);
|
||||
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
|
||||
bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr,
|
||||
bool print = true);
|
||||
bool ParseFrameDescription(const char *frame_descr,
|
||||
InternalMmapVector<StackVarDescr> *vars);
|
||||
bool DescribeAddressIfStack(uptr addr, uptr access_size);
|
||||
// Determines memory type on its own.
|
||||
void DescribeAddress(uptr addr, uptr access_size);
|
||||
|
||||
void DescribeThread(AsanThreadContext *context);
|
||||
|
||||
// Different kinds of error reports.
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
#include "lsan/lsan_common.h"
|
||||
#include "ubsan/ubsan_init.h"
|
||||
#include "ubsan/ubsan_platform.h"
|
||||
|
||||
int __asan_option_detect_stack_use_after_return; // Global interface symbol.
|
||||
uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan.
|
||||
@ -87,12 +89,12 @@ void ShowStatsAndAbort() {
|
||||
// ---------------------- mmap -------------------- {{{1
|
||||
// Reserve memory range [beg, end].
|
||||
// We need to use inclusive range because end+1 may not be representable.
|
||||
void ReserveShadowMemoryRange(uptr beg, uptr end) {
|
||||
void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
|
||||
CHECK_EQ((beg % GetPageSizeCached()), 0);
|
||||
CHECK_EQ(((end + 1) % GetPageSizeCached()), 0);
|
||||
uptr size = end - beg + 1;
|
||||
DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb.
|
||||
void *res = MmapFixedNoReserve(beg, size);
|
||||
void *res = MmapFixedNoReserve(beg, size, name);
|
||||
if (res != (void*)beg) {
|
||||
Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
|
||||
"Perhaps you're using ulimit -v\n", size);
|
||||
@ -112,11 +114,15 @@ static void OnLowLevelAllocate(uptr ptr, uptr size) {
|
||||
// -------------------------- Run-time entry ------------------- {{{1
|
||||
// exported functions
|
||||
#define ASAN_REPORT_ERROR(type, is_write, size) \
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
|
||||
void __asan_report_ ## type ## size(uptr addr); \
|
||||
void __asan_report_ ## type ## size(uptr addr) { \
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
|
||||
void __asan_report_ ## type ## size(uptr addr) { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size); \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size, 0); \
|
||||
} \
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
|
||||
void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size, exp); \
|
||||
}
|
||||
|
||||
ASAN_REPORT_ERROR(load, false, 1)
|
||||
@ -132,18 +138,20 @@ ASAN_REPORT_ERROR(store, true, 16)
|
||||
|
||||
#define ASAN_REPORT_ERROR_N(type, is_write) \
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
|
||||
void __asan_report_ ## type ## _n(uptr addr, uptr size); \
|
||||
void __asan_report_ ## type ## _n(uptr addr, uptr size) { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size); \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size, 0); \
|
||||
} \
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
|
||||
void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size, exp); \
|
||||
}
|
||||
|
||||
ASAN_REPORT_ERROR_N(load, false)
|
||||
ASAN_REPORT_ERROR_N(store, true)
|
||||
|
||||
#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE void __asan_##type##size(uptr addr); \
|
||||
void __asan_##type##size(uptr addr) { \
|
||||
#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg) \
|
||||
uptr sp = MEM_TO_SHADOW(addr); \
|
||||
uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \
|
||||
: *reinterpret_cast<u16 *>(sp); \
|
||||
@ -155,10 +163,19 @@ ASAN_REPORT_ERROR_N(store, true)
|
||||
*__asan_test_only_reported_buggy_pointer = addr; \
|
||||
} else { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size); \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size, exp_arg); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
|
||||
void __asan_##type##size(uptr addr) { \
|
||||
ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0) \
|
||||
} \
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
|
||||
void __asan_exp_##type##size(uptr addr, u32 exp) { \
|
||||
ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp) \
|
||||
}
|
||||
|
||||
ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1)
|
||||
@ -173,18 +190,38 @@ ASAN_MEMORY_ACCESS_CALLBACK(store, true, 8)
|
||||
ASAN_MEMORY_ACCESS_CALLBACK(store, true, 16)
|
||||
|
||||
extern "C"
|
||||
NOINLINE INTERFACE_ATTRIBUTE void __asan_loadN(uptr addr, uptr size) {
|
||||
NOINLINE INTERFACE_ATTRIBUTE
|
||||
void __asan_loadN(uptr addr, uptr size) {
|
||||
if (__asan_region_is_poisoned(addr, size)) {
|
||||
GET_CALLER_PC_BP_SP;
|
||||
__asan_report_error(pc, bp, sp, addr, false, size);
|
||||
__asan_report_error(pc, bp, sp, addr, false, size, 0);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
NOINLINE INTERFACE_ATTRIBUTE void __asan_storeN(uptr addr, uptr size) {
|
||||
NOINLINE INTERFACE_ATTRIBUTE
|
||||
void __asan_exp_loadN(uptr addr, uptr size, u32 exp) {
|
||||
if (__asan_region_is_poisoned(addr, size)) {
|
||||
GET_CALLER_PC_BP_SP;
|
||||
__asan_report_error(pc, bp, sp, addr, true, size);
|
||||
__asan_report_error(pc, bp, sp, addr, false, size, exp);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
NOINLINE INTERFACE_ATTRIBUTE
|
||||
void __asan_storeN(uptr addr, uptr size) {
|
||||
if (__asan_region_is_poisoned(addr, size)) {
|
||||
GET_CALLER_PC_BP_SP;
|
||||
__asan_report_error(pc, bp, sp, addr, true, size, 0);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
NOINLINE INTERFACE_ATTRIBUTE
|
||||
void __asan_exp_storeN(uptr addr, uptr size, u32 exp) {
|
||||
if (__asan_region_is_poisoned(addr, size)) {
|
||||
GET_CALLER_PC_BP_SP;
|
||||
__asan_report_error(pc, bp, sp, addr, true, size, exp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,26 +240,40 @@ static NOINLINE void force_interface_symbols() {
|
||||
case 3: __asan_report_load4(0); break;
|
||||
case 4: __asan_report_load8(0); break;
|
||||
case 5: __asan_report_load16(0); break;
|
||||
case 6: __asan_report_store1(0); break;
|
||||
case 7: __asan_report_store2(0); break;
|
||||
case 8: __asan_report_store4(0); break;
|
||||
case 9: __asan_report_store8(0); break;
|
||||
case 10: __asan_report_store16(0); break;
|
||||
case 12: __asan_register_globals(0, 0); break;
|
||||
case 13: __asan_unregister_globals(0, 0); break;
|
||||
case 14: __asan_set_death_callback(0); break;
|
||||
case 15: __asan_set_error_report_callback(0); break;
|
||||
case 16: __asan_handle_no_return(); break;
|
||||
case 17: __asan_address_is_poisoned(0); break;
|
||||
case 25: __asan_poison_memory_region(0, 0); break;
|
||||
case 26: __asan_unpoison_memory_region(0, 0); break;
|
||||
case 27: __asan_set_error_exit_code(0); break;
|
||||
case 30: __asan_before_dynamic_init(0); break;
|
||||
case 31: __asan_after_dynamic_init(); break;
|
||||
case 32: __asan_poison_stack_memory(0, 0); break;
|
||||
case 33: __asan_unpoison_stack_memory(0, 0); break;
|
||||
case 34: __asan_region_is_poisoned(0, 0); break;
|
||||
case 35: __asan_describe_address(0); break;
|
||||
case 6: __asan_report_load_n(0, 0); break;
|
||||
case 7: __asan_report_store1(0); break;
|
||||
case 8: __asan_report_store2(0); break;
|
||||
case 9: __asan_report_store4(0); break;
|
||||
case 10: __asan_report_store8(0); break;
|
||||
case 11: __asan_report_store16(0); break;
|
||||
case 12: __asan_report_store_n(0, 0); break;
|
||||
case 13: __asan_report_exp_load1(0, 0); break;
|
||||
case 14: __asan_report_exp_load2(0, 0); break;
|
||||
case 15: __asan_report_exp_load4(0, 0); break;
|
||||
case 16: __asan_report_exp_load8(0, 0); break;
|
||||
case 17: __asan_report_exp_load16(0, 0); break;
|
||||
case 18: __asan_report_exp_load_n(0, 0, 0); break;
|
||||
case 19: __asan_report_exp_store1(0, 0); break;
|
||||
case 20: __asan_report_exp_store2(0, 0); break;
|
||||
case 21: __asan_report_exp_store4(0, 0); break;
|
||||
case 22: __asan_report_exp_store8(0, 0); break;
|
||||
case 23: __asan_report_exp_store16(0, 0); break;
|
||||
case 24: __asan_report_exp_store_n(0, 0, 0); break;
|
||||
case 25: __asan_register_globals(0, 0); break;
|
||||
case 26: __asan_unregister_globals(0, 0); break;
|
||||
case 27: __asan_set_death_callback(0); break;
|
||||
case 28: __asan_set_error_report_callback(0); break;
|
||||
case 29: __asan_handle_no_return(); break;
|
||||
case 30: __asan_address_is_poisoned(0); break;
|
||||
case 31: __asan_poison_memory_region(0, 0); break;
|
||||
case 32: __asan_unpoison_memory_region(0, 0); break;
|
||||
case 33: __asan_set_error_exit_code(0); break;
|
||||
case 34: __asan_before_dynamic_init(0); break;
|
||||
case 35: __asan_after_dynamic_init(); break;
|
||||
case 36: __asan_poison_stack_memory(0, 0); break;
|
||||
case 37: __asan_unpoison_stack_memory(0, 0); break;
|
||||
case 38: __asan_region_is_poisoned(0, 0); break;
|
||||
case 39: __asan_describe_address(0); break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,9 +297,9 @@ static void InitializeHighMemEnd() {
|
||||
CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0);
|
||||
}
|
||||
|
||||
static void ProtectGap(uptr a, uptr size) {
|
||||
void *res = Mprotect(a, size);
|
||||
if (a == (uptr)res)
|
||||
static void ProtectGap(uptr addr, uptr size) {
|
||||
void *res = MmapNoAccess(addr, size, "shadow gap");
|
||||
if (addr == (uptr)res)
|
||||
return;
|
||||
Report("ERROR: Failed to protect the shadow gap. "
|
||||
"ASan cannot proceed correctly. ABORTING.\n");
|
||||
@ -296,9 +347,9 @@ static void PrintAddressSpaceLayout() {
|
||||
Printf("malloc_context_size=%zu\n",
|
||||
(uptr)common_flags()->malloc_context_size);
|
||||
|
||||
Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE);
|
||||
Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY);
|
||||
Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET);
|
||||
Printf("SHADOW_SCALE: %d\n", (int)SHADOW_SCALE);
|
||||
Printf("SHADOW_GRANULARITY: %d\n", (int)SHADOW_GRANULARITY);
|
||||
Printf("SHADOW_OFFSET: 0x%zx\n", (uptr)SHADOW_OFFSET);
|
||||
CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7);
|
||||
if (kMidMemBeg)
|
||||
CHECK(kMidShadowBeg > kLowShadowEnd &&
|
||||
@ -316,6 +367,11 @@ static void AsanInitInternal() {
|
||||
// initialization steps look at flags().
|
||||
InitializeFlags();
|
||||
|
||||
CacheBinaryName();
|
||||
|
||||
AsanCheckIncompatibleRT();
|
||||
AsanCheckDynamicRTPrereqs();
|
||||
|
||||
SetCanPoisonMemory(flags()->poison_heap);
|
||||
SetMallocContextSize(common_flags()->malloc_context_size);
|
||||
|
||||
@ -371,9 +427,9 @@ static void AsanInitInternal() {
|
||||
if (full_shadow_is_available) {
|
||||
// mmap the low shadow plus at least one page at the left.
|
||||
if (kLowShadowBeg)
|
||||
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
|
||||
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
|
||||
// mmap the high shadow.
|
||||
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
|
||||
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
|
||||
// protect the gap.
|
||||
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
|
||||
CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
|
||||
@ -382,11 +438,11 @@ static void AsanInitInternal() {
|
||||
MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
|
||||
CHECK(kLowShadowBeg != kLowShadowEnd);
|
||||
// mmap the low shadow plus at least one page at the left.
|
||||
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
|
||||
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
|
||||
// mmap the mid shadow.
|
||||
ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd);
|
||||
ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow");
|
||||
// mmap the high shadow.
|
||||
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
|
||||
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
|
||||
// protect the gaps.
|
||||
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
|
||||
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
|
||||
@ -446,6 +502,10 @@ static void AsanInitInternal() {
|
||||
}
|
||||
#endif // CAN_SANITIZE_LEAKS
|
||||
|
||||
#if CAN_SANITIZE_UB
|
||||
__ubsan::InitAsPlugin();
|
||||
#endif
|
||||
|
||||
InitializeSuppressions();
|
||||
|
||||
VReport(1, "AddressSanitizer Init done\n");
|
||||
@ -459,13 +519,11 @@ void AsanInitFromRtl() {
|
||||
|
||||
#if ASAN_DYNAMIC
|
||||
// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable
|
||||
// (and thus normal initializer from .preinit_array haven't run).
|
||||
// (and thus normal initializers from .preinit_array or modules haven't run).
|
||||
|
||||
class AsanInitializer {
|
||||
public: // NOLINT
|
||||
AsanInitializer() {
|
||||
AsanCheckIncompatibleRT();
|
||||
AsanCheckDynamicRTPrereqs();
|
||||
AsanInitFromRtl();
|
||||
}
|
||||
};
|
||||
@ -517,7 +575,6 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
|
||||
// Initialize as requested from instrumented application code.
|
||||
// We use this call as a trigger to wake up ASan from deactivated state.
|
||||
void __asan_init() {
|
||||
AsanCheckIncompatibleRT();
|
||||
AsanActivate();
|
||||
AsanInitInternal();
|
||||
}
|
||||
|
@ -51,12 +51,8 @@ void AsanStats::Print() {
|
||||
(mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
|
||||
mmaps, munmaps);
|
||||
|
||||
PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size);
|
||||
PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
|
||||
PrintMallocStatsArray(" frees by size class: ", freed_by_size);
|
||||
PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size);
|
||||
Printf("Stats: malloc large: %zu small slow: %zu\n",
|
||||
malloc_large, malloc_small_slow);
|
||||
Printf("Stats: malloc large: %zu\n", malloc_large);
|
||||
}
|
||||
|
||||
void AsanStats::MergeFrom(const AsanStats *stats) {
|
||||
@ -161,8 +157,7 @@ uptr __sanitizer_get_free_bytes() {
|
||||
GetAccumulatedStats(&stats);
|
||||
uptr total_free = stats.mmaped
|
||||
- stats.munmaped
|
||||
+ stats.really_freed
|
||||
+ stats.really_freed_redzones;
|
||||
+ stats.really_freed;
|
||||
uptr total_used = stats.malloced
|
||||
+ stats.malloced_redzones;
|
||||
// Return sane value if total_free < total_used due to racy
|
||||
|
@ -32,20 +32,14 @@ struct AsanStats {
|
||||
uptr freed;
|
||||
uptr real_frees;
|
||||
uptr really_freed;
|
||||
uptr really_freed_redzones;
|
||||
uptr reallocs;
|
||||
uptr realloced;
|
||||
uptr mmaps;
|
||||
uptr mmaped;
|
||||
uptr munmaps;
|
||||
uptr munmaped;
|
||||
uptr mmaped_by_size[kNumberOfSizeClasses];
|
||||
uptr malloced_by_size[kNumberOfSizeClasses];
|
||||
uptr freed_by_size[kNumberOfSizeClasses];
|
||||
uptr really_freed_by_size[kNumberOfSizeClasses];
|
||||
|
||||
uptr malloc_large;
|
||||
uptr malloc_small_slow;
|
||||
uptr malloced_by_size[kNumberOfSizeClasses];
|
||||
|
||||
// Ctor for global AsanStats (accumulated stats for dead threads).
|
||||
explicit AsanStats(LinkerInitialized) { }
|
||||
|
@ -26,14 +26,28 @@ static SuppressionContext *suppression_ctx = nullptr;
|
||||
static const char kInterceptorName[] = "interceptor_name";
|
||||
static const char kInterceptorViaFunction[] = "interceptor_via_fun";
|
||||
static const char kInterceptorViaLibrary[] = "interceptor_via_lib";
|
||||
static const char kODRViolation[] = "odr_violation";
|
||||
static const char *kSuppressionTypes[] = {
|
||||
kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary};
|
||||
kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary,
|
||||
kODRViolation};
|
||||
|
||||
extern "C" {
|
||||
#if SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
const char *__asan_default_suppressions();
|
||||
#else
|
||||
// No week hooks, provide empty implementation.
|
||||
const char *__asan_default_suppressions() { return ""; }
|
||||
#endif // SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
} // extern "C"
|
||||
|
||||
void InitializeSuppressions() {
|
||||
CHECK_EQ(nullptr, suppression_ctx);
|
||||
suppression_ctx = new (suppression_placeholder) // NOLINT
|
||||
SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
|
||||
suppression_ctx->ParseFromFile(flags()->suppressions);
|
||||
if (&__asan_default_suppressions)
|
||||
suppression_ctx->Parse(__asan_default_suppressions());
|
||||
}
|
||||
|
||||
bool IsInterceptorSuppressed(const char *interceptor_name) {
|
||||
@ -49,6 +63,13 @@ bool HaveStackTraceBasedSuppressions() {
|
||||
suppression_ctx->HasSuppressionType(kInterceptorViaLibrary);
|
||||
}
|
||||
|
||||
bool IsODRViolationSuppressed(const char *global_var_name) {
|
||||
CHECK(suppression_ctx);
|
||||
Suppression *s;
|
||||
// Match "odr_violation" suppressions.
|
||||
return suppression_ctx->Match(global_var_name, kODRViolation, &s);
|
||||
}
|
||||
|
||||
bool IsStackTraceSuppressed(const StackTrace *stack) {
|
||||
if (!HaveStackTraceBasedSuppressions())
|
||||
return false;
|
||||
@ -60,14 +81,10 @@ bool IsStackTraceSuppressed(const StackTrace *stack) {
|
||||
uptr addr = stack->trace[i];
|
||||
|
||||
if (suppression_ctx->HasSuppressionType(kInterceptorViaLibrary)) {
|
||||
const char *module_name;
|
||||
uptr module_offset;
|
||||
// Match "interceptor_via_lib" suppressions.
|
||||
if (symbolizer->GetModuleNameAndOffsetForPC(addr, &module_name,
|
||||
&module_offset) &&
|
||||
suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s)) {
|
||||
return true;
|
||||
}
|
||||
if (const char *module_name = symbolizer->GetModuleNameForPc(addr))
|
||||
if (suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) {
|
||||
|
@ -23,6 +23,7 @@ void InitializeSuppressions();
|
||||
bool IsInterceptorSuppressed(const char *interceptor_name);
|
||||
bool HaveStackTraceBasedSuppressions();
|
||||
bool IsStackTraceSuppressed(const StackTrace *stack);
|
||||
bool IsODRViolationSuppressed(const char *global_var_name);
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
|
@ -34,19 +34,16 @@ class AsanThread;
|
||||
class AsanThreadContext : public ThreadContextBase {
|
||||
public:
|
||||
explicit AsanThreadContext(int tid)
|
||||
: ThreadContextBase(tid),
|
||||
announced(false),
|
||||
destructor_iterations(kPthreadDestructorIterations),
|
||||
stack_id(0),
|
||||
thread(0) {
|
||||
}
|
||||
: ThreadContextBase(tid), announced(false),
|
||||
destructor_iterations(GetPthreadDestructorIterations()), stack_id(0),
|
||||
thread(0) {}
|
||||
bool announced;
|
||||
u8 destructor_iterations;
|
||||
u32 stack_id;
|
||||
AsanThread *thread;
|
||||
|
||||
void OnCreated(void *arg);
|
||||
void OnFinished();
|
||||
void OnCreated(void *arg) override;
|
||||
void OnFinished() override;
|
||||
};
|
||||
|
||||
// AsanThreadContext objects are never freed, so we need many of them.
|
||||
|
@ -22,21 +22,134 @@
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __asan_should_detect_stack_use_after_return() {
|
||||
__asan_init();
|
||||
return __asan_option_detect_stack_use_after_return;
|
||||
}
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __asan_should_detect_stack_use_after_return() {
|
||||
__asan_init();
|
||||
return __asan_option_detect_stack_use_after_return;
|
||||
}
|
||||
|
||||
// -------------------- A workaround for the abscence of weak symbols ----- {{{
|
||||
// We don't have a direct equivalent of weak symbols when using MSVC, but we can
|
||||
// use the /alternatename directive to tell the linker to default a specific
|
||||
// symbol to a specific value, which works nicely for allocator hooks and
|
||||
// __asan_default_options().
|
||||
void __sanitizer_default_malloc_hook(void *ptr, uptr size) { }
|
||||
void __sanitizer_default_free_hook(void *ptr) { }
|
||||
const char* __asan_default_default_options() { return ""; }
|
||||
const char* __asan_default_default_suppressions() { return ""; }
|
||||
void __asan_default_on_error() {}
|
||||
#pragma comment(linker, "/alternatename:___sanitizer_malloc_hook=___sanitizer_default_malloc_hook") // NOLINT
|
||||
#pragma comment(linker, "/alternatename:___sanitizer_free_hook=___sanitizer_default_free_hook") // NOLINT
|
||||
#pragma comment(linker, "/alternatename:___asan_default_options=___asan_default_default_options") // NOLINT
|
||||
#pragma comment(linker, "/alternatename:___asan_default_suppressions=___asan_default_default_suppressions") // NOLINT
|
||||
#pragma comment(linker, "/alternatename:___asan_on_error=___asan_default_on_error") // NOLINT
|
||||
// }}}
|
||||
} // extern "C"
|
||||
|
||||
// ---------------------- Windows-specific inteceptors ---------------- {{{
|
||||
INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
|
||||
CHECK(REAL(RaiseException));
|
||||
__asan_handle_no_return();
|
||||
REAL(RaiseException)(a, b, c, d);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
|
||||
CHECK(REAL(_except_handler3));
|
||||
__asan_handle_no_return();
|
||||
return REAL(_except_handler3)(a, b, c, d);
|
||||
}
|
||||
|
||||
#if ASAN_DYNAMIC
|
||||
// This handler is named differently in -MT and -MD CRTs.
|
||||
#define _except_handler4 _except_handler4_common
|
||||
#endif
|
||||
INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
|
||||
CHECK(REAL(_except_handler4));
|
||||
__asan_handle_no_return();
|
||||
return REAL(_except_handler4)(a, b, c, d);
|
||||
}
|
||||
|
||||
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
|
||||
AsanThread *t = (AsanThread*)arg;
|
||||
SetCurrentThread(t);
|
||||
return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
|
||||
}
|
||||
|
||||
INTERCEPTOR_WINAPI(DWORD, CreateThread,
|
||||
void* security, uptr stack_size,
|
||||
DWORD (__stdcall *start_routine)(void*), void* arg,
|
||||
DWORD thr_flags, void* tid) {
|
||||
// Strict init-order checking is thread-hostile.
|
||||
if (flags()->strict_init_order)
|
||||
StopInitOrderChecking();
|
||||
GET_STACK_TRACE_THREAD;
|
||||
// FIXME: The CreateThread interceptor is not the same as a pthread_create
|
||||
// one. This is a bandaid fix for PR22025.
|
||||
bool detached = false; // FIXME: how can we determine it on Windows?
|
||||
u32 current_tid = GetCurrentTidOrInvalid();
|
||||
AsanThread *t =
|
||||
AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
|
||||
return REAL(CreateThread)(security, stack_size,
|
||||
asan_thread_start, t, thr_flags, tid);
|
||||
}
|
||||
|
||||
namespace {
|
||||
BlockingMutex mu_for_thread_tracking(LINKER_INITIALIZED);
|
||||
|
||||
void EnsureWorkerThreadRegistered() {
|
||||
// FIXME: GetCurrentThread relies on TSD, which might not play well with
|
||||
// system thread pools. We might want to use something like reference
|
||||
// counting to zero out GetCurrentThread() underlying storage when the last
|
||||
// work item finishes? Or can we disable reclaiming of threads in the pool?
|
||||
BlockingMutexLock l(&mu_for_thread_tracking);
|
||||
if (__asan::GetCurrentThread())
|
||||
return;
|
||||
|
||||
AsanThread *t = AsanThread::Create(
|
||||
/* start_routine */ nullptr, /* arg */ nullptr,
|
||||
/* parent_tid */ -1, /* stack */ nullptr, /* detached */ true);
|
||||
t->Init();
|
||||
asanThreadRegistry().StartThread(t->tid(), 0, 0);
|
||||
SetCurrentThread(t);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
INTERCEPTOR_WINAPI(DWORD, NtWaitForWorkViaWorkerFactory, DWORD a, DWORD b) {
|
||||
// NtWaitForWorkViaWorkerFactory is called from system worker pool threads to
|
||||
// query work scheduled by BindIoCompletionCallback, QueueUserWorkItem, etc.
|
||||
// System worker pool threads are created at arbitraty point in time and
|
||||
// without using CreateThread, so we wrap NtWaitForWorkViaWorkerFactory
|
||||
// instead and don't register a specific parent_tid/stack.
|
||||
EnsureWorkerThreadRegistered();
|
||||
return REAL(NtWaitForWorkViaWorkerFactory)(a, b);
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
namespace __asan {
|
||||
|
||||
// ---------------------- TSD ---------------- {{{1
|
||||
void InitializePlatformInterceptors() {
|
||||
ASAN_INTERCEPT_FUNC(CreateThread);
|
||||
ASAN_INTERCEPT_FUNC(RaiseException);
|
||||
ASAN_INTERCEPT_FUNC(_except_handler3);
|
||||
ASAN_INTERCEPT_FUNC(_except_handler4);
|
||||
|
||||
// NtWaitForWorkViaWorkerFactory is always linked dynamically.
|
||||
CHECK(::__interception::OverrideFunction(
|
||||
"NtWaitForWorkViaWorkerFactory",
|
||||
(uptr)WRAP(NtWaitForWorkViaWorkerFactory),
|
||||
(uptr *)&REAL(NtWaitForWorkViaWorkerFactory)));
|
||||
}
|
||||
|
||||
// ---------------------- TSD ---------------- {{{
|
||||
static bool tsd_key_inited = false;
|
||||
|
||||
static __declspec(thread) void *fake_tsd = 0;
|
||||
@ -59,7 +172,9 @@ void AsanTSDSet(void *tsd) {
|
||||
void PlatformTSDDtor(void *tsd) {
|
||||
AsanThread::TSDDtor(tsd);
|
||||
}
|
||||
// ---------------------- Various stuff ---------------- {{{1
|
||||
// }}}
|
||||
|
||||
// ---------------------- Various stuff ---------------- {{{
|
||||
void DisableReexec() {
|
||||
// No need to re-exec on Windows.
|
||||
}
|
||||
@ -93,23 +208,6 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) {
|
||||
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
|
||||
|
||||
SignalContext SignalContext::Create(void *siginfo, void *context) {
|
||||
EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo;
|
||||
CONTEXT *context_record = (CONTEXT*)context;
|
||||
|
||||
uptr pc = (uptr)exception_record->ExceptionAddress;
|
||||
#ifdef _WIN64
|
||||
uptr bp = (uptr)context_record->Rbp;
|
||||
uptr sp = (uptr)context_record->Rsp;
|
||||
#else
|
||||
uptr bp = (uptr)context_record->Ebp;
|
||||
uptr sp = (uptr)context_record->Esp;
|
||||
#endif
|
||||
uptr access_addr = exception_record->ExceptionInformation[1];
|
||||
|
||||
return SignalContext(context, access_addr, pc, sp, bp);
|
||||
}
|
||||
|
||||
static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
|
||||
EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
|
||||
CONTEXT *context = info->ContextRecord;
|
||||
@ -162,7 +260,7 @@ int __asan_set_seh_filter() {
|
||||
static __declspec(allocate(".CRT$XIZ"))
|
||||
int (*__intercept_seh)() = __asan_set_seh_filter;
|
||||
#endif
|
||||
|
||||
// }}}
|
||||
} // namespace __asan
|
||||
|
||||
#endif // _WIN32
|
||||
|
@ -303,8 +303,8 @@ INTERFACE_FUNCTION(__sanitizer_cov_init)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_module_init)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_cmp)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_with_check)
|
||||
INTERFACE_FUNCTION(__sanitizer_free_hook)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_allocated_size)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_coverage_guards)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes)
|
||||
@ -314,13 +314,14 @@ INTERFACE_FUNCTION(__sanitizer_get_heap_size)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_ownership)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes)
|
||||
INTERFACE_FUNCTION(__sanitizer_malloc_hook)
|
||||
INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file)
|
||||
INTERFACE_FUNCTION(__sanitizer_print_stack_trace)
|
||||
INTERFACE_FUNCTION(__sanitizer_ptr_cmp)
|
||||
INTERFACE_FUNCTION(__sanitizer_ptr_sub)
|
||||
INTERFACE_FUNCTION(__sanitizer_report_error_summary)
|
||||
INTERFACE_FUNCTION(__sanitizer_reset_coverage)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_number_of_counters)
|
||||
INTERFACE_FUNCTION(__sanitizer_update_counter_bitset_and_clear_counters)
|
||||
INTERFACE_FUNCTION(__sanitizer_sandbox_on_notify)
|
||||
INTERFACE_FUNCTION(__sanitizer_set_death_callback)
|
||||
INTERFACE_FUNCTION(__sanitizer_set_report_path)
|
||||
@ -380,11 +381,15 @@ INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT
|
||||
INTERCEPT_LIBRARY_FUNCTION(strchr);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strcmp);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT
|
||||
INTERCEPT_LIBRARY_FUNCTION(strcspn);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strlen);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strncat);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strncmp);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strncpy);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strnlen);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strpbrk);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strspn);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strstr);
|
||||
INTERCEPT_LIBRARY_FUNCTION(strtol);
|
||||
INTERCEPT_LIBRARY_FUNCTION(wcslen);
|
||||
|
||||
|
@ -15,7 +15,8 @@
|
||||
//
|
||||
// This includes:
|
||||
// - forwarding the detect_stack_use_after_return runtime option
|
||||
// - installing a custom SEH handler
|
||||
// - working around deficiencies of the MD runtime
|
||||
// - installing a custom SEH handlerx
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -24,9 +25,13 @@
|
||||
// simplifies the build procedure.
|
||||
#ifdef ASAN_DYNAMIC_RUNTIME_THUNK
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
|
||||
extern "C" {
|
||||
// First, declare CRT sections we'll be using in this file
|
||||
#pragma section(".CRT$XID", long, read) // NOLINT
|
||||
#pragma section(".CRT$XIZ", long, read) // NOLINT
|
||||
#pragma section(".CRT$XTW", long, read) // NOLINT
|
||||
#pragma section(".CRT$XTY", long, read) // NOLINT
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Define a copy of __asan_option_detect_stack_use_after_return that should be
|
||||
// used when linking an MD runtime with a set of object files on Windows.
|
||||
@ -38,82 +43,55 @@ extern "C" {
|
||||
// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
|
||||
// just to work around this issue, let's clone the a variable that is
|
||||
// constant after initialization anyways.
|
||||
extern "C" {
|
||||
__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
|
||||
int __asan_option_detect_stack_use_after_return =
|
||||
__asan_should_detect_stack_use_after_return();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// For some reason, the MD CRT doesn't call the C/C++ terminators as MT does.
|
||||
// To work around this, for each DLL we schedule a call to
|
||||
// UnregisterGlobalsInRange atexit() specifying the address range of the DLL
|
||||
// image to unregister globals in that range. We don't do the same
|
||||
// for the main module (.exe) as the asan_globals.cc allocator is destroyed
|
||||
// by the time UnregisterGlobalsInRange is executed.
|
||||
// See PR22545 for the details.
|
||||
namespace __asan {
|
||||
__declspec(dllimport)
|
||||
void UnregisterGlobalsInRange(void *beg, void *end);
|
||||
}
|
||||
// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL
|
||||
// unload or on exit. ASan relies on LLVM global_dtors to call
|
||||
// __asan_unregister_globals on these events, which unfortunately doesn't work
|
||||
// with the MD runtime, see PR22545 for the details.
|
||||
// To work around this, for each DLL we schedule a call to UnregisterGlobals
|
||||
// using atexit() that calls a small subset of C terminators
|
||||
// where LLVM global_dtors is placed. Fingers crossed, no other C terminators
|
||||
// are there.
|
||||
extern "C" void __cdecl _initterm(void *a, void *b);
|
||||
|
||||
namespace {
|
||||
void *this_module_base, *this_module_end;
|
||||
__declspec(allocate(".CRT$XTW")) void* before_global_dtors = 0;
|
||||
__declspec(allocate(".CRT$XTY")) void* after_global_dtors = 0;
|
||||
|
||||
void UnregisterGlobals() {
|
||||
__asan::UnregisterGlobalsInRange(this_module_base, this_module_end);
|
||||
_initterm(&before_global_dtors, &after_global_dtors);
|
||||
}
|
||||
|
||||
int ScheduleUnregisterGlobals() {
|
||||
HMODULE this_module = 0;
|
||||
// Increments the reference counter of the DLL module, so need to call
|
||||
// FreeLibrary later.
|
||||
if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
||||
(LPCTSTR)&UnregisterGlobals, &this_module))
|
||||
return 1;
|
||||
|
||||
// Skip the main module.
|
||||
if (this_module == GetModuleHandle(0))
|
||||
return 0;
|
||||
|
||||
MODULEINFO mi;
|
||||
bool success =
|
||||
GetModuleInformation(GetCurrentProcess(), this_module, &mi, sizeof(mi));
|
||||
if (!FreeLibrary(this_module))
|
||||
return 2;
|
||||
if (!success)
|
||||
return 3;
|
||||
|
||||
this_module_base = mi.lpBaseOfDll;
|
||||
this_module_end = (char*)mi.lpBaseOfDll + mi.SizeOfImage;
|
||||
|
||||
return atexit(UnregisterGlobals);
|
||||
}
|
||||
|
||||
// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after
|
||||
// atexit() is initialized (.CRT$XIC). As this is executed before C++
|
||||
// initializers (think ctors for globals), UnregisterGlobals gets executed after
|
||||
// dtors for C++ globals.
|
||||
__declspec(allocate(".CRT$XID"))
|
||||
int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
|
||||
|
||||
} // namespace
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ASan SEH handling.
|
||||
extern "C" __declspec(dllimport) int __asan_set_seh_filter();
|
||||
static int SetSEHFilter() { return __asan_set_seh_filter(); }
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// We schedule some work at start-up by placing callbacks to our code to the
|
||||
// list of CRT C initializers.
|
||||
//
|
||||
// First, declare sections we'll be using:
|
||||
#pragma section(".CRT$XID", long, read) // NOLINT
|
||||
#pragma section(".CRT$XIZ", long, read) // NOLINT
|
||||
|
||||
// We need to call 'atexit(UnregisterGlobals);' after atexit() is initialized
|
||||
// (.CRT$XIC) but before the C++ constructors (.CRT$XCA).
|
||||
__declspec(allocate(".CRT$XID"))
|
||||
static int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
|
||||
|
||||
// We need to set the ASan-specific SEH handler at the end of CRT initialization
|
||||
// of each module (see also asan_win.cc).
|
||||
//
|
||||
extern "C" {
|
||||
__declspec(dllimport) int __asan_set_seh_filter();
|
||||
static int SetSEHFilter() { return __asan_set_seh_filter(); }
|
||||
|
||||
// Unfortunately, putting a pointer to __asan_set_seh_filter into
|
||||
// __asan_intercept_seh gets optimized out, so we have to use an extra function.
|
||||
extern "C" __declspec(allocate(".CRT$XIZ"))
|
||||
int (*__asan_seh_interceptor)() = SetSEHFilter;
|
||||
__declspec(allocate(".CRT$XIZ")) int (*__asan_seh_interceptor)() = SetSEHFilter;
|
||||
}
|
||||
|
||||
#endif // ASAN_DYNAMIC_RUNTIME_THUNK
|
||||
|
@ -23,6 +23,7 @@ sysroot_path = None
|
||||
binary_name_filter = None
|
||||
fix_filename_patterns = None
|
||||
logfile = sys.stdin
|
||||
allow_system_symbolizer = True
|
||||
|
||||
# FIXME: merge the code that calls fix_filename().
|
||||
def fix_filename(file_name):
|
||||
@ -392,6 +393,8 @@ class SymbolizationLoop(object):
|
||||
[BreakpadSymbolizerFactory(binary), self.llvm_symbolizers[binary]])
|
||||
result = symbolizers[binary].symbolize(addr, binary, offset)
|
||||
if result is None:
|
||||
if not allow_system_symbolizer:
|
||||
raise Exception('Failed to launch or use llvm-symbolizer.')
|
||||
# Initialize system symbolizer only if other symbolizers failed.
|
||||
symbolizers[binary].append_symbolizer(
|
||||
SystemSymbolizerFactory(self.system, addr, binary))
|
||||
|
@ -232,7 +232,7 @@ TEST(AddressSanitizer, asm_flags) {
|
||||
long magic = 0x1234;
|
||||
long r = 0x0;
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#if defined(__x86_64__) && !defined(__ILP32__)
|
||||
__asm__("xorq %%rax, %%rax \n\t"
|
||||
"movq (%[p]), %%rax \n\t"
|
||||
"sete %%al \n\t"
|
||||
@ -248,7 +248,7 @@ TEST(AddressSanitizer, asm_flags) {
|
||||
: [r] "=r"(r)
|
||||
: [p] "r"(&magic)
|
||||
: "eax", "memory");
|
||||
#endif // defined(__x86_64__)
|
||||
#endif // defined(__x86_64__) && !defined(__ILP32__)
|
||||
|
||||
ASSERT_EQ(0x1, r);
|
||||
}
|
||||
|
@ -290,9 +290,6 @@ void RunStrCmpTest(PointerToStrCmp StrCmp) {
|
||||
Ident(StrCmp(s1, s2));
|
||||
Ident(StrCmp(s1, s2 + size - 1));
|
||||
Ident(StrCmp(s1 + size - 1, s2 + size - 1));
|
||||
s1[size - 1] = 'z';
|
||||
s2[size - 1] = 'x';
|
||||
Ident(StrCmp(s1, s2));
|
||||
// One of arguments points to not allocated memory.
|
||||
EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1));
|
||||
@ -371,17 +368,14 @@ TEST(AddressSanitizer, StrCatOOBTest) {
|
||||
// One of arguments points to not allocated memory.
|
||||
EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1));
|
||||
EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(strcat(to + to_size, from), RightOOBWriteMessage(0));
|
||||
EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0));
|
||||
|
||||
// "from" is not zero-terminated.
|
||||
from[from_size - 1] = 'z';
|
||||
EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0));
|
||||
from[from_size - 1] = '\0';
|
||||
// "to" is not zero-terminated.
|
||||
memset(to, 'z', to_size);
|
||||
EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
|
||||
// "to" is too short to fit "from".
|
||||
memset(to, 'z', to_size);
|
||||
to[to_size - from_size + 1] = '\0';
|
||||
EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
|
||||
// length of "to" is just enough.
|
||||
@ -409,7 +403,6 @@ TEST(AddressSanitizer, StrNCatOOBTest) {
|
||||
// One of arguments points to not allocated memory.
|
||||
EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1));
|
||||
EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBWriteMessage(0));
|
||||
EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0));
|
||||
|
||||
memset(from, 'z', from_size);
|
||||
@ -417,8 +410,6 @@ TEST(AddressSanitizer, StrNCatOOBTest) {
|
||||
to[0] = '\0';
|
||||
// "from" is too short.
|
||||
EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0));
|
||||
// "to" is not zero-terminated.
|
||||
EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBWriteMessage(0));
|
||||
// "to" is too short to fit "from".
|
||||
to[0] = 'z';
|
||||
to[to_size - from_size + 1] = '\0';
|
||||
@ -508,20 +499,15 @@ void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
|
||||
EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1));
|
||||
// Die if a buffer doesn't have terminating NULL.
|
||||
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
|
||||
// Make last symbol a terminating NULL or other non-digit.
|
||||
// Make last symbol a terminating NULL
|
||||
array[9] = '\0';
|
||||
Atoi(array);
|
||||
array[9] = 'a';
|
||||
Atoi(array);
|
||||
Atoi(array + 9);
|
||||
// Sometimes we need to detect overflow if no digits are found.
|
||||
memset(array, ' ', 10);
|
||||
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
|
||||
array[9] = '-';
|
||||
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0));
|
||||
array[8] = '-';
|
||||
Atoi(array);
|
||||
free(array);
|
||||
}
|
||||
|
||||
@ -546,7 +532,6 @@ typedef void(*PointerToCallStrtol)(const char*, char**, int);
|
||||
|
||||
void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
|
||||
char *array = MallocAndMemsetString(3);
|
||||
char *endptr = NULL;
|
||||
array[0] = '1';
|
||||
array[1] = '2';
|
||||
array[2] = '3';
|
||||
@ -554,19 +539,12 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
|
||||
EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1));
|
||||
// Buffer overflow if there is no terminating null (depends on base).
|
||||
Strtol(array, &endptr, 3);
|
||||
EXPECT_EQ(array + 2, endptr);
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
|
||||
array[2] = 'z';
|
||||
Strtol(array, &endptr, 35);
|
||||
EXPECT_EQ(array + 2, endptr);
|
||||
EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0));
|
||||
// Add terminating zero to get rid of overflow.
|
||||
array[2] = '\0';
|
||||
Strtol(array, NULL, 36);
|
||||
// Don't check for overflow if base is invalid.
|
||||
Strtol(array - 1, NULL, -1);
|
||||
Strtol(array + 3, NULL, 1);
|
||||
// Sometimes we need to detect overflow if no digits are found.
|
||||
array[0] = array[1] = array[2] = ' ';
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
|
||||
@ -574,13 +552,6 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
|
||||
array[2] = '-';
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
|
||||
array[1] = '+';
|
||||
Strtol(array, NULL, 0);
|
||||
array[1] = array[2] = 'z';
|
||||
Strtol(array, &endptr, 0);
|
||||
EXPECT_EQ(array, endptr);
|
||||
Strtol(array + 2, NULL, 0);
|
||||
EXPECT_EQ(array, endptr);
|
||||
free(array);
|
||||
}
|
||||
|
||||
|
@ -31,13 +31,13 @@ NOINLINE void free_aaa(void *p) { free_bbb(p); break_optimization(0);}
|
||||
|
||||
template<typename T>
|
||||
NOINLINE void uaf_test(int size, int off) {
|
||||
char *p = (char *)malloc_aaa(size);
|
||||
void *p = malloc_aaa(size);
|
||||
free_aaa(p);
|
||||
for (int i = 1; i < 100; i++)
|
||||
free_aaa(malloc_aaa(i));
|
||||
fprintf(stderr, "writing %ld byte(s) at %p with offset %d\n",
|
||||
(long)sizeof(T), p, off);
|
||||
asan_write((T*)(p + off));
|
||||
asan_write((T *)((char *)p + off));
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, HasFeatureAddressSanitizerTest) {
|
||||
@ -436,10 +436,10 @@ TEST(AddressSanitizer, WrongFreeTest) {
|
||||
|
||||
void DoubleFree() {
|
||||
int *x = (int*)malloc(100 * sizeof(int));
|
||||
fprintf(stderr, "DoubleFree: x=%p\n", x);
|
||||
fprintf(stderr, "DoubleFree: x=%p\n", (void *)x);
|
||||
free(x);
|
||||
free(x);
|
||||
fprintf(stderr, "should have failed in the second free(%p)\n", x);
|
||||
fprintf(stderr, "should have failed in the second free(%p)\n", (void *)x);
|
||||
abort();
|
||||
}
|
||||
|
||||
@ -569,17 +569,6 @@ TEST(AddressSanitizer, LongJmpTest) {
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) // Only basic longjmp is available on Windows.
|
||||
NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) {
|
||||
// create three red zones for these two stack objects.
|
||||
int a;
|
||||
int b;
|
||||
|
||||
int *A = Ident(&a);
|
||||
int *B = Ident(&b);
|
||||
*A = *B;
|
||||
__builtin_longjmp((void**)buf, 1);
|
||||
}
|
||||
|
||||
NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) {
|
||||
// create three red zones for these two stack objects.
|
||||
int a;
|
||||
@ -604,7 +593,19 @@ NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) {
|
||||
|
||||
#if !defined(__ANDROID__) && !defined(__arm__) && \
|
||||
!defined(__powerpc64__) && !defined(__powerpc__) && \
|
||||
!defined(__aarch64__)
|
||||
!defined(__aarch64__) && !defined(__mips__) && \
|
||||
!defined(__mips64)
|
||||
NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) {
|
||||
// create three red zones for these two stack objects.
|
||||
int a;
|
||||
int b;
|
||||
|
||||
int *A = Ident(&a);
|
||||
int *B = Ident(&b);
|
||||
*A = *B;
|
||||
__builtin_longjmp((void**)buf, 1);
|
||||
}
|
||||
|
||||
// Does not work on Power and ARM:
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=185
|
||||
TEST(AddressSanitizer, BuiltinLongJmpTest) {
|
||||
@ -616,7 +617,8 @@ TEST(AddressSanitizer, BuiltinLongJmpTest) {
|
||||
}
|
||||
}
|
||||
#endif // !defined(__ANDROID__) && !defined(__powerpc64__) &&
|
||||
// !defined(__powerpc__) && !defined(__arm__)
|
||||
// !defined(__powerpc__) && !defined(__arm__) &&
|
||||
// !defined(__mips__) && !defined(__mips64)
|
||||
|
||||
TEST(AddressSanitizer, UnderscopeLongJmpTest) {
|
||||
static jmp_buf buf;
|
||||
@ -1243,7 +1245,7 @@ TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) {
|
||||
const size_t kAllocSize = (1 << 28) - 1024;
|
||||
size_t total_size = 0;
|
||||
while (true) {
|
||||
char *x = (char*)malloc(kAllocSize);
|
||||
void *x = malloc(kAllocSize);
|
||||
memset(x, 0, kAllocSize);
|
||||
total_size += kAllocSize;
|
||||
fprintf(stderr, "total: %ldM %p\n", (long)total_size >> 20, x);
|
||||
|
19
contrib/compiler-rt/lib/builtins/atomic_flag_clear.c
Normal file
19
contrib/compiler-rt/lib/builtins/atomic_flag_clear.c
Normal file
@ -0,0 +1,19 @@
|
||||
/*===-- atomic_flag_clear.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 atomic_flag_clear from C11's stdatomic.h.
|
||||
*
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_flag_clear
|
||||
void atomic_flag_clear(volatile atomic_flag *object) {
|
||||
return __c11_atomic_store(&(object)->_Value, 0, __ATOMIC_SEQ_CST);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*===-- atomic_flag_clear_explicit.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 atomic_flag_clear_explicit from C11's stdatomic.h.
|
||||
*
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_flag_clear_explicit
|
||||
void atomic_flag_clear_explicit(volatile atomic_flag *object,
|
||||
memory_order order) {
|
||||
return __c11_atomic_store(&(object)->_Value, 0, order);
|
||||
}
|
19
contrib/compiler-rt/lib/builtins/atomic_flag_test_and_set.c
Normal file
19
contrib/compiler-rt/lib/builtins/atomic_flag_test_and_set.c
Normal file
@ -0,0 +1,19 @@
|
||||
/*===-- atomic_flag_test_and_set.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 atomic_flag_test_and_set from C11's stdatomic.h.
|
||||
*
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_flag_test_and_set
|
||||
_Bool atomic_flag_test_and_set(volatile atomic_flag *object) {
|
||||
return __c11_atomic_exchange(&(object)->_Value, 1, __ATOMIC_SEQ_CST);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*===-- atomic_flag_test_and_set_explicit.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 atomic_flag_test_and_set_explicit from C11's stdatomic.h
|
||||
*
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_flag_test_and_set_explicit
|
||||
_Bool atomic_flag_test_and_set_explicit(volatile atomic_flag *object,
|
||||
memory_order order) {
|
||||
return __c11_atomic_exchange(&(object)->_Value, 1, order);
|
||||
}
|
19
contrib/compiler-rt/lib/builtins/atomic_signal_fence.c
Normal file
19
contrib/compiler-rt/lib/builtins/atomic_signal_fence.c
Normal file
@ -0,0 +1,19 @@
|
||||
/*===-- atomic_signal_fence.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 atomic_signal_fence from C11's stdatomic.h.
|
||||
*
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_signal_fence
|
||||
void atomic_signal_fence(memory_order order) {
|
||||
__c11_atomic_signal_fence(order);
|
||||
}
|
19
contrib/compiler-rt/lib/builtins/atomic_thread_fence.c
Normal file
19
contrib/compiler-rt/lib/builtins/atomic_thread_fence.c
Normal file
@ -0,0 +1,19 @@
|
||||
/*===-- atomic_thread_fence.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 atomic_thread_fence from C11's stdatomic.h.
|
||||
*
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_thread_fence
|
||||
void atomic_thread_fence(memory_order order) {
|
||||
__c11_atomic_thread_fence(order);
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
#if __APPLE__
|
||||
#include <libkern/OSCacheControl.h>
|
||||
#endif
|
||||
#if defined(__FreeBSD__) && defined(__arm__)
|
||||
#if (defined(__FreeBSD__) || defined(__Bitrig__)) && defined(__arm__)
|
||||
#include <sys/types.h>
|
||||
#include <machine/sysarch.h>
|
||||
#endif
|
||||
@ -26,6 +26,7 @@
|
||||
#if defined(__mips__) && !defined(__FreeBSD__)
|
||||
#include <sys/cachectl.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#if defined(__ANDROID__) && defined(__LP64__)
|
||||
/*
|
||||
* clear_mips_cache - Invalidates instruction cache for Mips.
|
||||
@ -90,7 +91,7 @@ void __clear_cache(void *start, void *end) {
|
||||
* so there is nothing to do
|
||||
*/
|
||||
#elif defined(__arm__) && !defined(__APPLE__)
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__Bitrig__)
|
||||
struct arm_sync_icache_args arg;
|
||||
|
||||
arg.addr = (uintptr_t)start;
|
||||
@ -127,6 +128,7 @@ void __clear_cache(void *start, void *end) {
|
||||
#elif defined(__aarch64__) && !defined(__APPLE__)
|
||||
uint64_t xstart = (uint64_t)(uintptr_t) start;
|
||||
uint64_t xend = (uint64_t)(uintptr_t) end;
|
||||
uint64_t addr;
|
||||
|
||||
// Get Cache Type Info
|
||||
uint64_t ctr_el0;
|
||||
@ -137,12 +139,12 @@ void __clear_cache(void *start, void *end) {
|
||||
* uintptr_t in case this runs in an IPL32 environment.
|
||||
*/
|
||||
const size_t dcache_line_size = 4 << ((ctr_el0 >> 16) & 15);
|
||||
for (uint64_t addr = xstart; addr < xend; addr += dcache_line_size)
|
||||
for (addr = xstart; addr < xend; addr += dcache_line_size)
|
||||
__asm __volatile("dc cvau, %0" :: "r"(addr));
|
||||
__asm __volatile("dsb ish");
|
||||
|
||||
const size_t icache_line_size = 4 << ((ctr_el0 >> 0) & 15);
|
||||
for (uint64_t addr = xstart; addr < xend; addr += icache_line_size)
|
||||
for (addr = xstart; addr < xend; addr += icache_line_size)
|
||||
__asm __volatile("ic ivau, %0" :: "r"(addr));
|
||||
__asm __volatile("isb sy");
|
||||
#else
|
||||
|
@ -10,7 +10,9 @@
|
||||
|
||||
#include "int_lib.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
/* #include "config.h"
|
||||
* FIXME: CMake - include when cmake system is ready.
|
||||
@ -18,9 +20,14 @@
|
||||
*/
|
||||
#define HAVE_SYSCONF 1
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
#else
|
||||
#ifndef __APPLE__
|
||||
#include <unistd.h>
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#if __LP64__
|
||||
#define TRAMPOLINE_SIZE 48
|
||||
@ -40,6 +47,12 @@ COMPILER_RT_ABI void
|
||||
__enable_execute_stack(void* addr)
|
||||
{
|
||||
|
||||
#if _WIN32
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
if (!VirtualQuery (addr, &mbi, sizeof(mbi)))
|
||||
return; /* We should probably assert here because there is no return value */
|
||||
VirtualProtect (mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
|
||||
#else
|
||||
#if __APPLE__
|
||||
/* On Darwin, pagesize is always 4096 bytes */
|
||||
const uintptr_t pageSize = 4096;
|
||||
@ -55,4 +68,5 @@ __enable_execute_stack(void* addr)
|
||||
unsigned char* endPage = (unsigned char*)((p+TRAMPOLINE_SIZE+pageSize) & pageAlignMask);
|
||||
size_t length = endPage - startPage;
|
||||
(void) mprotect((void *)startPage, length, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
#endif
|
||||
}
|
||||
|
23
contrib/compiler-rt/lib/builtins/extendhfsf2.c
Normal file
23
contrib/compiler-rt/lib/builtins/extendhfsf2.c
Normal file
@ -0,0 +1,23 @@
|
||||
//===-- lib/extendhfsf2.c - half -> single conversion -------------*- 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
|
||||
#define SRC_HALF
|
||||
#define DST_SINGLE
|
||||
#include "fp_extend_impl.inc"
|
||||
|
||||
// Use a forwarding definition and noinline to implement a poor man's alias,
|
||||
// as there isn't a good cross-platform way of defining one.
|
||||
COMPILER_RT_ABI __attribute__((noinline)) float __extendhfsf2(uint16_t a) {
|
||||
return __extendXfYf2__(a);
|
||||
}
|
||||
|
||||
COMPILER_RT_ABI float __gnu_h2f_ieee(uint16_t a) {
|
||||
return __extendhfsf2(a);
|
||||
}
|
@ -12,6 +12,28 @@
|
||||
#include "fp_lib.h"
|
||||
ARM_EABI_FNALIAS(d2lz, fixdfdi)
|
||||
|
||||
#ifndef __SOFT_FP__
|
||||
/* Support for systems that have hardware floating-point; can set the invalid
|
||||
* flag as a side-effect of computation.
|
||||
*/
|
||||
|
||||
COMPILER_RT_ABI du_int __fixunsdfdi(double a);
|
||||
|
||||
COMPILER_RT_ABI di_int
|
||||
__fixdfdi(double a)
|
||||
{
|
||||
if (a < 0.0) {
|
||||
return -__fixunsdfdi(-a);
|
||||
}
|
||||
return __fixunsdfdi(a);
|
||||
}
|
||||
|
||||
#else
|
||||
/* Support for systems that don't have hardware floating-point; there are no
|
||||
* flags to set, and we don't want to code-gen to an unknown soft-float
|
||||
* implementation.
|
||||
*/
|
||||
|
||||
typedef di_int fixint_t;
|
||||
typedef du_int fixuint_t;
|
||||
#include "fp_fixint_impl.inc"
|
||||
@ -20,3 +42,5 @@ COMPILER_RT_ABI di_int
|
||||
__fixdfdi(fp_t a) {
|
||||
return __fixint(a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -13,6 +13,28 @@
|
||||
|
||||
ARM_EABI_FNALIAS(f2lz, fixsfdi)
|
||||
|
||||
#ifndef __SOFT_FP__
|
||||
/* Support for systems that have hardware floating-point; can set the invalid
|
||||
* flag as a side-effect of computation.
|
||||
*/
|
||||
|
||||
COMPILER_RT_ABI du_int __fixunssfdi(float a);
|
||||
|
||||
COMPILER_RT_ABI di_int
|
||||
__fixsfdi(float a)
|
||||
{
|
||||
if (a < 0.0f) {
|
||||
return -__fixunssfdi(-a);
|
||||
}
|
||||
return __fixunssfdi(a);
|
||||
}
|
||||
|
||||
#else
|
||||
/* Support for systems that don't have hardware floating-point; there are no
|
||||
* flags to set, and we don't want to code-gen to an unknown soft-float
|
||||
* implementation.
|
||||
*/
|
||||
|
||||
typedef di_int fixint_t;
|
||||
typedef du_int fixuint_t;
|
||||
#include "fp_fixint_impl.inc"
|
||||
@ -21,3 +43,5 @@ COMPILER_RT_ABI di_int
|
||||
__fixsfdi(fp_t a) {
|
||||
return __fixint(a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -10,12 +10,35 @@
|
||||
|
||||
#define DOUBLE_PRECISION
|
||||
#include "fp_lib.h"
|
||||
typedef du_int fixuint_t;
|
||||
#include "fp_fixuint_impl.inc"
|
||||
|
||||
ARM_EABI_FNALIAS(d2ulz, fixunsdfdi)
|
||||
|
||||
#ifndef __SOFT_FP__
|
||||
/* Support for systems that have hardware floating-point; can set the invalid
|
||||
* flag as a side-effect of computation.
|
||||
*/
|
||||
|
||||
COMPILER_RT_ABI du_int
|
||||
__fixunsdfdi(double a)
|
||||
{
|
||||
if (a <= 0.0) return 0;
|
||||
su_int high = a/0x1p32f;
|
||||
su_int low = a - (double)high*0x1p32f;
|
||||
return ((du_int)high << 32) | low;
|
||||
}
|
||||
|
||||
#else
|
||||
/* Support for systems that don't have hardware floating-point; there are no
|
||||
* flags to set, and we don't want to code-gen to an unknown soft-float
|
||||
* implementation.
|
||||
*/
|
||||
|
||||
typedef du_int fixuint_t;
|
||||
#include "fp_fixuint_impl.inc"
|
||||
|
||||
COMPILER_RT_ABI du_int
|
||||
__fixunsdfdi(fp_t a) {
|
||||
return __fixuint(a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -17,7 +17,7 @@ typedef tu_int fixuint_t;
|
||||
#include "fp_fixuint_impl.inc"
|
||||
|
||||
COMPILER_RT_ABI tu_int
|
||||
__fixunsdftti(fp_t a) {
|
||||
__fixunsdfti(fp_t a) {
|
||||
return __fixuint(a);
|
||||
}
|
||||
#endif /* CRT_HAS_128BIT */
|
||||
|
@ -10,12 +10,36 @@
|
||||
|
||||
#define SINGLE_PRECISION
|
||||
#include "fp_lib.h"
|
||||
typedef du_int fixuint_t;
|
||||
#include "fp_fixuint_impl.inc"
|
||||
|
||||
ARM_EABI_FNALIAS(f2ulz, fixunssfdi)
|
||||
|
||||
#ifndef __SOFT_FP__
|
||||
/* Support for systems that have hardware floating-point; can set the invalid
|
||||
* flag as a side-effect of computation.
|
||||
*/
|
||||
|
||||
COMPILER_RT_ABI du_int
|
||||
__fixunssfdi(float a)
|
||||
{
|
||||
if (a <= 0.0f) return 0;
|
||||
double da = a;
|
||||
su_int high = da/0x1p32f;
|
||||
su_int low = da - (double)high*0x1p32f;
|
||||
return ((du_int)high << 32) | low;
|
||||
}
|
||||
|
||||
#else
|
||||
/* Support for systems that don't have hardware floating-point; there are no
|
||||
* flags to set, and we don't want to code-gen to an unknown soft-float
|
||||
* implementation.
|
||||
*/
|
||||
|
||||
typedef du_int fixuint_t;
|
||||
#include "fp_fixuint_impl.inc"
|
||||
|
||||
COMPILER_RT_ABI du_int
|
||||
__fixunssfdi(fp_t a) {
|
||||
return __fixuint(a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -39,11 +39,24 @@ static inline int src_rep_t_clz(src_rep_t a) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#elif defined SRC_HALF
|
||||
typedef uint16_t src_t;
|
||||
typedef uint16_t src_rep_t;
|
||||
#define SRC_REP_C UINT16_C
|
||||
static const int srcSigBits = 10;
|
||||
#define src_rep_t_clz __builtin_clz
|
||||
|
||||
#else
|
||||
#error Source should be single precision or double precision!
|
||||
#error Source should be half, single, or double precision!
|
||||
#endif //end source precision
|
||||
|
||||
#if defined DST_DOUBLE
|
||||
#if defined DST_SINGLE
|
||||
typedef float dst_t;
|
||||
typedef uint32_t dst_rep_t;
|
||||
#define DST_REP_C UINT32_C
|
||||
static const int dstSigBits = 23;
|
||||
|
||||
#elif defined DST_DOUBLE
|
||||
typedef double dst_t;
|
||||
typedef uint64_t dst_rep_t;
|
||||
#define DST_REP_C UINT64_C
|
||||
@ -56,7 +69,7 @@ typedef __uint128_t dst_rep_t;
|
||||
static const int dstSigBits = 112;
|
||||
|
||||
#else
|
||||
#error Destination should be double precision or quad precision!
|
||||
#error Destination should be single, double, or quad precision!
|
||||
#endif //end destination precision
|
||||
|
||||
// End of specialization parameters. Two helper routines for conversion to and
|
||||
|
@ -66,7 +66,9 @@ static inline dst_t __extendXfYf2__(src_t a) {
|
||||
const src_rep_t sign = aRep & srcSignMask;
|
||||
dst_rep_t absResult;
|
||||
|
||||
if (aAbs - srcMinNormal < srcInfinity - srcMinNormal) {
|
||||
// If sizeof(src_rep_t) < sizeof(int), the subtraction result is promoted
|
||||
// to (signed) int. To avoid that, explicitly cast to src_rep_t.
|
||||
if ((src_rep_t)(aAbs - srcMinNormal) < srcInfinity - srcMinNormal) {
|
||||
// a is a normal number.
|
||||
// Extend to the destination type by shifting the significand and
|
||||
// exponent into the proper position and rebiasing the exponent.
|
||||
|
@ -16,7 +16,13 @@
|
||||
|
||||
#include "int_lib.h"
|
||||
|
||||
#if defined SRC_DOUBLE
|
||||
#if defined SRC_SINGLE
|
||||
typedef float src_t;
|
||||
typedef uint32_t src_rep_t;
|
||||
#define SRC_REP_C UINT32_C
|
||||
static const int srcSigBits = 23;
|
||||
|
||||
#elif defined SRC_DOUBLE
|
||||
typedef double src_t;
|
||||
typedef uint64_t src_rep_t;
|
||||
#define SRC_REP_C UINT64_C
|
||||
@ -44,6 +50,12 @@ typedef uint32_t dst_rep_t;
|
||||
#define DST_REP_C UINT32_C
|
||||
static const int dstSigBits = 23;
|
||||
|
||||
#elif defined DST_HALF
|
||||
typedef uint16_t dst_t;
|
||||
typedef uint16_t dst_rep_t;
|
||||
#define DST_REP_C UINT16_C
|
||||
static const int dstSigBits = 10;
|
||||
|
||||
#else
|
||||
#error Destination should be single precision or double precision!
|
||||
#endif //end destination precision
|
||||
|
@ -99,7 +99,7 @@ static inline dst_t __truncXfYf2__(src_t a) {
|
||||
absResult |= dstQNaN;
|
||||
absResult |= ((aAbs & srcNaNCode) >> (srcSigBits - dstSigBits)) & dstNaNCode;
|
||||
}
|
||||
else if (aAbs > overflow) {
|
||||
else if (aAbs >= overflow) {
|
||||
// a overflows to infinity.
|
||||
absResult = (dst_rep_t)dstInfExp << dstSigBits;
|
||||
}
|
||||
|
34
contrib/compiler-rt/lib/builtins/i386/chkstk.S
Normal file
34
contrib/compiler-rt/lib/builtins/i386/chkstk.S
Normal file
@ -0,0 +1,34 @@
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
// _chkstk routine
|
||||
// This routine is windows specific
|
||||
// http://msdn.microsoft.com/en-us/library/ms648426.aspx
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
.text
|
||||
.balign 4
|
||||
DEFINE_COMPILERRT_FUNCTION(__chkstk_ms)
|
||||
push %ecx
|
||||
push %eax
|
||||
cmp $0x1000,%eax
|
||||
lea 12(%esp),%ecx
|
||||
jb 1f
|
||||
2:
|
||||
sub $0x1000,%ecx
|
||||
orl $0,(%ecx)
|
||||
sub $0x1000,%eax
|
||||
cmp $0x1000,%eax
|
||||
ja 2b
|
||||
1:
|
||||
sub %eax,%ecx
|
||||
orl $0,(%ecx)
|
||||
pop %eax
|
||||
pop %ecx
|
||||
ret
|
||||
END_COMPILERRT_FUNCTION(__chkstk_ms)
|
||||
|
||||
#endif // __i386__
|
@ -16,6 +16,20 @@
|
||||
#ifndef INT_ENDIANNESS_H
|
||||
#define INT_ENDIANNESS_H
|
||||
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
|
||||
defined(__ORDER_LITTLE_ENDIAN__)
|
||||
|
||||
/* Clang and GCC provide built-in endianness definitions. */
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#define _YUGA_LITTLE_ENDIAN 0
|
||||
#define _YUGA_BIG_ENDIAN 1
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define _YUGA_LITTLE_ENDIAN 1
|
||||
#define _YUGA_BIG_ENDIAN 0
|
||||
#endif /* __BYTE_ORDER__ */
|
||||
|
||||
#else /* Compilers other than Clang or GCC. */
|
||||
|
||||
#if defined(__SVR4) && defined(__sun)
|
||||
#include <sys/byteorder.h>
|
||||
|
||||
@ -84,18 +98,6 @@
|
||||
|
||||
/* .. */
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#define _YUGA_LITTLE_ENDIAN 0
|
||||
#define _YUGA_BIG_ENDIAN 1
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define _YUGA_LITTLE_ENDIAN 1
|
||||
#define _YUGA_BIG_ENDIAN 0
|
||||
#endif /* __BYTE_ORDER__ */
|
||||
|
||||
#endif /* GNU/Linux */
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#define _YUGA_LITTLE_ENDIAN 1
|
||||
@ -103,6 +105,8 @@
|
||||
|
||||
#endif /* Windows */
|
||||
|
||||
#endif /* Clang or GCC. */
|
||||
|
||||
/* . */
|
||||
|
||||
#if !defined(_YUGA_LITTLE_ENDIAN) || !defined(_YUGA_BIG_ENDIAN)
|
||||
|
@ -36,7 +36,11 @@
|
||||
|
||||
#else
|
||||
# define ARM_EABI_FNALIAS(aeabi_name, name)
|
||||
# define COMPILER_RT_ABI
|
||||
# if defined(__arm__) && defined(_WIN32)
|
||||
# define COMPILER_RT_ABI __attribute__((pcs("aapcs")))
|
||||
# else
|
||||
# define COMPILER_RT_ABI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__) && (defined(_KERNEL) || defined(_STANDALONE))
|
||||
|
16
contrib/compiler-rt/lib/builtins/truncdfhf2.c
Normal file
16
contrib/compiler-rt/lib/builtins/truncdfhf2.c
Normal file
@ -0,0 +1,16 @@
|
||||
//===-- lib/truncdfhf2.c - double -> half conversion --------------*- 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define SRC_DOUBLE
|
||||
#define DST_HALF
|
||||
#include "fp_trunc_impl.inc"
|
||||
|
||||
COMPILER_RT_ABI uint16_t __truncdfhf2(double a) {
|
||||
return __truncXfYf2__(a);
|
||||
}
|
22
contrib/compiler-rt/lib/builtins/truncsfhf2.c
Normal file
22
contrib/compiler-rt/lib/builtins/truncsfhf2.c
Normal file
@ -0,0 +1,22 @@
|
||||
//===-- lib/truncsfhf2.c - single -> half conversion --------------*- 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define SRC_SINGLE
|
||||
#define DST_HALF
|
||||
#include "fp_trunc_impl.inc"
|
||||
|
||||
// Use a forwarding definition and noinline to implement a poor man's alias,
|
||||
// as there isn't a good cross-platform way of defining one.
|
||||
COMPILER_RT_ABI __attribute__((noinline)) uint16_t __truncsfhf2(float a) {
|
||||
return __truncXfYf2__(a);
|
||||
}
|
||||
|
||||
COMPILER_RT_ABI uint16_t __gnu_f2h_ieee(float a) {
|
||||
return __truncsfhf2(a);
|
||||
}
|
39
contrib/compiler-rt/lib/builtins/x86_64/chkstk.S
Normal file
39
contrib/compiler-rt/lib/builtins/x86_64/chkstk.S
Normal file
@ -0,0 +1,39 @@
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
// _chkstk routine
|
||||
// This routine is windows specific
|
||||
// http://msdn.microsoft.com/en-us/library/ms648426.aspx
|
||||
|
||||
// Notes from r227519
|
||||
// MSVC x64s __chkstk and cygmings ___chkstk_ms do not adjust %rsp
|
||||
// themselves. It also does not clobber %rax so we can reuse it when
|
||||
// adjusting %rsp.
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
.text
|
||||
.balign 4
|
||||
DEFINE_COMPILERRT_FUNCTION(___chkstk_ms)
|
||||
push %rcx
|
||||
push %rax
|
||||
cmp $0x1000,%rax
|
||||
lea 24(%rsp),%rcx
|
||||
jb 1f
|
||||
2:
|
||||
sub $0x1000,%rcx
|
||||
orl $0,(%rcx)
|
||||
sub $0x1000,%rax
|
||||
cmp $0x1000,%rax
|
||||
ja 2b
|
||||
1:
|
||||
sub %rax,%rcx
|
||||
orl $0,(%rcx)
|
||||
pop %rax
|
||||
pop %rcx
|
||||
ret
|
||||
END_COMPILERRT_FUNCTION(___chkstk_ms)
|
||||
|
||||
#endif // __x86_64__
|
@ -302,12 +302,12 @@ dfsan_dump_labels(int fd) {
|
||||
char buf[64];
|
||||
internal_snprintf(buf, sizeof(buf), "%u %u %u ", l,
|
||||
__dfsan_label_info[l].l1, __dfsan_label_info[l].l2);
|
||||
internal_write(fd, buf, internal_strlen(buf));
|
||||
WriteToFile(fd, buf, internal_strlen(buf));
|
||||
if (__dfsan_label_info[l].l1 == 0 && __dfsan_label_info[l].desc) {
|
||||
internal_write(fd, __dfsan_label_info[l].desc,
|
||||
internal_strlen(__dfsan_label_info[l].desc));
|
||||
WriteToFile(fd, __dfsan_label_info[l].desc,
|
||||
internal_strlen(__dfsan_label_info[l].desc));
|
||||
}
|
||||
internal_write(fd, "\n", 1);
|
||||
WriteToFile(fd, "\n", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,7 +333,7 @@ static void InitializeFlags() {
|
||||
|
||||
static void dfsan_fini() {
|
||||
if (internal_strcmp(flags().dump_labels_at_exit, "") != 0) {
|
||||
fd_t fd = OpenFile(flags().dump_labels_at_exit, true /* write */);
|
||||
fd_t fd = OpenFile(flags().dump_labels_at_exit, WrOnly);
|
||||
if (fd == kInvalidFd) {
|
||||
Report("WARNING: DataFlowSanitizer: unable to open output file %s\n",
|
||||
flags().dump_labels_at_exit);
|
||||
@ -343,7 +343,7 @@ static void dfsan_fini() {
|
||||
Report("INFO: DataFlowSanitizer: dumping labels to %s\n",
|
||||
flags().dump_labels_at_exit);
|
||||
dfsan_dump_labels(fd);
|
||||
internal_close(fd);
|
||||
CloseFile(fd);
|
||||
}
|
||||
}
|
||||
|
||||
@ -361,7 +361,7 @@ static void dfsan_init(int argc, char **argv, char **envp) {
|
||||
// case by disabling memory protection when ASLR is disabled.
|
||||
uptr init_addr = (uptr)&dfsan_init;
|
||||
if (!(init_addr >= kUnusedAddr && init_addr < kAppAddr))
|
||||
Mprotect(kUnusedAddr, kAppAddr - kUnusedAddr);
|
||||
MmapNoAccess(kUnusedAddr, kAppAddr - kUnusedAddr);
|
||||
|
||||
InitializeFlags();
|
||||
InitializeInterceptors();
|
||||
|
@ -82,11 +82,20 @@ SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strchr(const char *s, int c,
|
||||
}
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void
|
||||
dfsan_weak_hook_memcmp(uptr caller_pc, const void *s1, const void *s2, size_t n,
|
||||
dfsan_label s1_label, dfsan_label s2_label,
|
||||
dfsan_label n_label);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_memcmp(const void *s1, const void *s2,
|
||||
size_t n, dfsan_label s1_label,
|
||||
dfsan_label s2_label,
|
||||
dfsan_label n_label,
|
||||
dfsan_label *ret_label) {
|
||||
if (dfsan_weak_hook_memcmp)
|
||||
dfsan_weak_hook_memcmp(GET_CALLER_PC(), s1, s2, n, s1_label, s2_label,
|
||||
n_label);
|
||||
const char *cs1 = (const char *) s1, *cs2 = (const char *) s2;
|
||||
for (size_t i = 0; i != n; ++i) {
|
||||
if (cs1[i] != cs2[i]) {
|
||||
@ -847,266 +856,247 @@ __dfsw_write(int fd, const void *buf, size_t count,
|
||||
*ret_label = 0;
|
||||
return write(fd, buf, count);
|
||||
}
|
||||
}
|
||||
|
||||
// Type used to extract a dfsan_label with va_arg()
|
||||
typedef int dfsan_label_va;
|
||||
|
||||
// A chunk of data representing the output of formatting either a constant
|
||||
// string or a single format directive.
|
||||
struct Chunk {
|
||||
// Address of the beginning of the formatted string
|
||||
const char *ptr;
|
||||
// Size of the formatted string
|
||||
// Formats a chunk either a constant string or a single format directive (e.g.,
|
||||
// '%.3f').
|
||||
struct Formatter {
|
||||
Formatter(char *str_, const char *fmt_, size_t size_)
|
||||
: str(str_), str_off(0), size(size_), fmt_start(fmt_), fmt_cur(fmt_),
|
||||
width(-1) {}
|
||||
|
||||
int format() {
|
||||
char *tmp_fmt = build_format_string();
|
||||
int retval =
|
||||
snprintf(str + str_off, str_off < size ? size - str_off : 0, tmp_fmt,
|
||||
0 /* used only to avoid warnings */);
|
||||
free(tmp_fmt);
|
||||
return retval;
|
||||
}
|
||||
|
||||
template <typename T> int format(T arg) {
|
||||
char *tmp_fmt = build_format_string();
|
||||
int retval;
|
||||
if (width >= 0) {
|
||||
retval = snprintf(str + str_off, str_off < size ? size - str_off : 0,
|
||||
tmp_fmt, width, arg);
|
||||
} else {
|
||||
retval = snprintf(str + str_off, str_off < size ? size - str_off : 0,
|
||||
tmp_fmt, arg);
|
||||
}
|
||||
free(tmp_fmt);
|
||||
return retval;
|
||||
}
|
||||
|
||||
char *build_format_string() {
|
||||
size_t fmt_size = fmt_cur - fmt_start + 1;
|
||||
char *new_fmt = (char *)malloc(fmt_size + 1);
|
||||
assert(new_fmt);
|
||||
internal_memcpy(new_fmt, fmt_start, fmt_size);
|
||||
new_fmt[fmt_size] = '\0';
|
||||
return new_fmt;
|
||||
}
|
||||
|
||||
char *str_cur() { return str + str_off; }
|
||||
|
||||
size_t num_written_bytes(int retval) {
|
||||
if (retval < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t num_avail = str_off < size ? size - str_off : 0;
|
||||
if (num_avail == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t num_written = retval;
|
||||
// A return value of {v,}snprintf of size or more means that the output was
|
||||
// truncated.
|
||||
if (num_written >= num_avail) {
|
||||
num_written -= num_avail;
|
||||
}
|
||||
|
||||
return num_written;
|
||||
}
|
||||
|
||||
char *str;
|
||||
size_t str_off;
|
||||
size_t size;
|
||||
|
||||
// Type of DFSan label (depends on the format directive)
|
||||
enum {
|
||||
// Constant string, no argument and thus no label
|
||||
NONE = 0,
|
||||
// Label for an argument of '%n'
|
||||
IGNORED,
|
||||
// Label for a '%s' argument
|
||||
STRING,
|
||||
// Label for any other type of argument
|
||||
NUMERIC,
|
||||
} label_type;
|
||||
|
||||
// Value of the argument (if label_type == STRING)
|
||||
const char *arg;
|
||||
const char *fmt_start;
|
||||
const char *fmt_cur;
|
||||
int width;
|
||||
};
|
||||
|
||||
// Formats the input. The output is stored in 'str' starting from offset
|
||||
// 'off'. The format directive is represented by the first 'format_size' bytes
|
||||
// of 'format'. If 'has_size' is true, 'size' bounds the number of output
|
||||
// bytes. Returns the return value of the vsnprintf call used to format the
|
||||
// input.
|
||||
static int format_chunk(char *str, size_t off, bool has_size, size_t size,
|
||||
const char *format, size_t format_size, ...) {
|
||||
char *chunk_format = (char *) malloc(format_size + 1);
|
||||
assert(chunk_format);
|
||||
internal_memcpy(chunk_format, format, format_size);
|
||||
chunk_format[format_size] = '\0';
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format_size);
|
||||
int r = 0;
|
||||
if (has_size) {
|
||||
r = vsnprintf(str + off, off < size ? size - off : 0, chunk_format, ap);
|
||||
} else {
|
||||
r = vsprintf(str + off, chunk_format, ap);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
free(chunk_format);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Formats the input and propagates the input labels to the output. The output
|
||||
// is stored in 'str'. If 'has_size' is true, 'size' bounds the number of
|
||||
// output bytes. 'format' and 'ap' are the format string and the list of
|
||||
// arguments for formatting. Returns the return value vsnprintf would return.
|
||||
// is stored in 'str'. 'size' bounds the number of output bytes. 'format' and
|
||||
// 'ap' are the format string and the list of arguments for formatting. Returns
|
||||
// the return value vsnprintf would return.
|
||||
//
|
||||
// The function tokenizes the format string in chunks representing either a
|
||||
// constant string or a single format directive (e.g., '%.3f') and formats each
|
||||
// chunk independently into the output string. This approach allows to figure
|
||||
// out which bytes of the output string depends on which argument and thus to
|
||||
// propagate labels more precisely.
|
||||
static int format_buffer(char *str, bool has_size, size_t size,
|
||||
const char *format, dfsan_label *va_labels,
|
||||
dfsan_label *ret_label, va_list ap) {
|
||||
InternalMmapVector<Chunk> chunks(8);
|
||||
size_t off = 0;
|
||||
//
|
||||
// WARNING: This implementation does not support conversion specifiers with
|
||||
// positional arguments.
|
||||
static int format_buffer(char *str, size_t size, const char *fmt,
|
||||
dfsan_label *va_labels, dfsan_label *ret_label,
|
||||
va_list ap) {
|
||||
Formatter formatter(str, fmt, size);
|
||||
|
||||
while (*format) {
|
||||
chunks.push_back(Chunk());
|
||||
Chunk& chunk = chunks.back();
|
||||
chunk.ptr = str + off;
|
||||
chunk.arg = nullptr;
|
||||
while (*formatter.fmt_cur) {
|
||||
formatter.fmt_start = formatter.fmt_cur;
|
||||
formatter.width = -1;
|
||||
int retval = 0;
|
||||
|
||||
int status = 0;
|
||||
|
||||
if (*format != '%') {
|
||||
if (*formatter.fmt_cur != '%') {
|
||||
// Ordinary character. Consume all the characters until a '%' or the end
|
||||
// of the string.
|
||||
size_t format_size = 0;
|
||||
for (; *format && *format != '%'; ++format, ++format_size) {}
|
||||
status = format_chunk(str, off, has_size, size, format - format_size,
|
||||
format_size);
|
||||
chunk.label_type = Chunk::NONE;
|
||||
for (; *(formatter.fmt_cur + 1) && *(formatter.fmt_cur + 1) != '%';
|
||||
++formatter.fmt_cur) {}
|
||||
retval = formatter.format();
|
||||
dfsan_set_label(0, formatter.str_cur(),
|
||||
formatter.num_written_bytes(retval));
|
||||
} else {
|
||||
// Conversion directive. Consume all the characters until a conversion
|
||||
// specifier or the end of the string.
|
||||
bool end_format = false;
|
||||
#define FORMAT_CHUNK(t) \
|
||||
format_chunk(str, off, has_size, size, format - format_size, \
|
||||
format_size + 1, va_arg(ap, t))
|
||||
|
||||
for (size_t format_size = 1; *++format && !end_format; ++format_size) {
|
||||
switch (*format) {
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'o':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
switch (*(format - 1)) {
|
||||
case 'h':
|
||||
// Also covers the 'hh' case (since the size of the arg is still
|
||||
// an int).
|
||||
status = FORMAT_CHUNK(int);
|
||||
break;
|
||||
case 'l':
|
||||
if (format_size >= 2 && *(format - 2) == 'l') {
|
||||
status = FORMAT_CHUNK(long long int);
|
||||
} else {
|
||||
status = FORMAT_CHUNK(long int);
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
status = FORMAT_CHUNK(long long int);
|
||||
break;
|
||||
case 'j':
|
||||
status = FORMAT_CHUNK(intmax_t);
|
||||
break;
|
||||
case 'z':
|
||||
status = FORMAT_CHUNK(size_t);
|
||||
break;
|
||||
case 't':
|
||||
status = FORMAT_CHUNK(size_t);
|
||||
break;
|
||||
default:
|
||||
status = FORMAT_CHUNK(int);
|
||||
}
|
||||
chunk.label_type = Chunk::NUMERIC;
|
||||
end_format = true;
|
||||
bool end_fmt = false;
|
||||
for (; *formatter.fmt_cur && !end_fmt; ) {
|
||||
switch (*++formatter.fmt_cur) {
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'o':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
switch (*(formatter.fmt_cur - 1)) {
|
||||
case 'h':
|
||||
// Also covers the 'hh' case (since the size of the arg is still
|
||||
// an int).
|
||||
retval = formatter.format(va_arg(ap, int));
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'g':
|
||||
case 'G':
|
||||
if (*(format - 1) == 'L') {
|
||||
status = FORMAT_CHUNK(long double);
|
||||
case 'l':
|
||||
if (formatter.fmt_cur - formatter.fmt_start >= 2 &&
|
||||
*(formatter.fmt_cur - 2) == 'l') {
|
||||
retval = formatter.format(va_arg(ap, long long int));
|
||||
} else {
|
||||
status = FORMAT_CHUNK(double);
|
||||
retval = formatter.format(va_arg(ap, long int));
|
||||
}
|
||||
chunk.label_type = Chunk::NUMERIC;
|
||||
end_format = true;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
status = FORMAT_CHUNK(int);
|
||||
chunk.label_type = Chunk::NUMERIC;
|
||||
end_format = true;
|
||||
case 'q':
|
||||
retval = formatter.format(va_arg(ap, long long int));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
chunk.arg = va_arg(ap, char *);
|
||||
status =
|
||||
format_chunk(str, off, has_size, size,
|
||||
format - format_size, format_size + 1,
|
||||
chunk.arg);
|
||||
chunk.label_type = Chunk::STRING;
|
||||
end_format = true;
|
||||
case 'j':
|
||||
retval = formatter.format(va_arg(ap, intmax_t));
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
status = FORMAT_CHUNK(void *);
|
||||
chunk.label_type = Chunk::NUMERIC;
|
||||
end_format = true;
|
||||
case 'z':
|
||||
case 't':
|
||||
retval = formatter.format(va_arg(ap, size_t));
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
*(va_arg(ap, int *)) = (int)off;
|
||||
chunk.label_type = Chunk::IGNORED;
|
||||
end_format = true;
|
||||
break;
|
||||
|
||||
case '%':
|
||||
status = format_chunk(str, off, has_size, size,
|
||||
format - format_size, format_size + 1);
|
||||
chunk.label_type = Chunk::NONE;
|
||||
end_format = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
retval = formatter.format(va_arg(ap, int));
|
||||
}
|
||||
dfsan_set_label(*va_labels++, formatter.str_cur(),
|
||||
formatter.num_written_bytes(retval));
|
||||
end_fmt = true;
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'g':
|
||||
case 'G':
|
||||
if (*(formatter.fmt_cur - 1) == 'L') {
|
||||
retval = formatter.format(va_arg(ap, long double));
|
||||
} else {
|
||||
retval = formatter.format(va_arg(ap, double));
|
||||
}
|
||||
dfsan_set_label(*va_labels++, formatter.str_cur(),
|
||||
formatter.num_written_bytes(retval));
|
||||
end_fmt = true;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
retval = formatter.format(va_arg(ap, int));
|
||||
dfsan_set_label(*va_labels++, formatter.str_cur(),
|
||||
formatter.num_written_bytes(retval));
|
||||
end_fmt = true;
|
||||
break;
|
||||
|
||||
case 's': {
|
||||
char *arg = va_arg(ap, char *);
|
||||
retval = formatter.format(arg);
|
||||
va_labels++;
|
||||
internal_memcpy(shadow_for(formatter.str_cur()), shadow_for(arg),
|
||||
sizeof(dfsan_label) *
|
||||
formatter.num_written_bytes(retval));
|
||||
end_fmt = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'p':
|
||||
retval = formatter.format(va_arg(ap, void *));
|
||||
dfsan_set_label(*va_labels++, formatter.str_cur(),
|
||||
formatter.num_written_bytes(retval));
|
||||
end_fmt = true;
|
||||
break;
|
||||
|
||||
case 'n': {
|
||||
int *ptr = va_arg(ap, int *);
|
||||
*ptr = (int)formatter.str_off;
|
||||
va_labels++;
|
||||
dfsan_set_label(0, ptr, sizeof(ptr));
|
||||
end_fmt = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case '%':
|
||||
retval = formatter.format();
|
||||
dfsan_set_label(0, formatter.str_cur(),
|
||||
formatter.num_written_bytes(retval));
|
||||
end_fmt = true;
|
||||
break;
|
||||
|
||||
case '*':
|
||||
formatter.width = va_arg(ap, int);
|
||||
va_labels++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#undef FORMAT_CHUNK
|
||||
}
|
||||
|
||||
if (status < 0) {
|
||||
return status;
|
||||
if (retval < 0) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
// A return value of {v,}snprintf of size or more means that the output was
|
||||
// truncated.
|
||||
if (has_size) {
|
||||
if (off < size) {
|
||||
size_t ustatus = (size_t) status;
|
||||
chunk.size = ustatus >= (size - off) ?
|
||||
ustatus - (size - off) : ustatus;
|
||||
} else {
|
||||
chunk.size = 0;
|
||||
}
|
||||
} else {
|
||||
chunk.size = status;
|
||||
}
|
||||
off += status;
|
||||
}
|
||||
|
||||
// TODO(martignlo): Decide how to combine labels (e.g., whether to ignore or
|
||||
// not the label of the format string).
|
||||
|
||||
// Label each output chunk according to the label supplied as argument to the
|
||||
// function. We need to go through all the chunks and arguments even if the
|
||||
// string was only partially printed ({v,}snprintf case).
|
||||
for (size_t i = 0; i < chunks.size(); ++i) {
|
||||
const Chunk& chunk = chunks[i];
|
||||
void *chunk_ptr = const_cast<char *>(chunk.ptr);
|
||||
|
||||
switch (chunk.label_type) {
|
||||
case Chunk::NONE:
|
||||
dfsan_set_label(0, chunk_ptr, chunk.size);
|
||||
break;
|
||||
case Chunk::IGNORED:
|
||||
va_labels++;
|
||||
dfsan_set_label(0, chunk_ptr, chunk.size);
|
||||
break;
|
||||
case Chunk::NUMERIC: {
|
||||
dfsan_label label = *va_labels++;
|
||||
dfsan_set_label(label, chunk_ptr, chunk.size);
|
||||
break;
|
||||
}
|
||||
case Chunk::STRING: {
|
||||
// Consume the label of the pointer to the string
|
||||
va_labels++;
|
||||
internal_memcpy(shadow_for(chunk_ptr),
|
||||
shadow_for(chunk.arg),
|
||||
sizeof(dfsan_label) * (strlen(chunk.arg)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
formatter.fmt_cur++;
|
||||
formatter.str_off += retval;
|
||||
}
|
||||
|
||||
*ret_label = 0;
|
||||
|
||||
// Number of bytes written in total.
|
||||
return off;
|
||||
return formatter.str_off;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __dfsw_sprintf(char *str, const char *format, dfsan_label str_label,
|
||||
dfsan_label format_label, dfsan_label *va_labels,
|
||||
dfsan_label *ret_label, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, ret_label);
|
||||
int ret = format_buffer(str, false, 0, format, va_labels, ret_label, ap);
|
||||
int ret = format_buffer(str, ~0ul, format, va_labels, ret_label, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
@ -1118,7 +1108,7 @@ int __dfsw_snprintf(char *str, size_t size, const char *format,
|
||||
dfsan_label *ret_label, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, ret_label);
|
||||
int ret = format_buffer(str, true, size, format, va_labels, ret_label, ap);
|
||||
int ret = format_buffer(str, size, format, va_labels, ret_label, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
@ -258,3 +258,18 @@ fun:reflect.MakeFuncStubGo=uninstrumented
|
||||
fun:reflect.MakeFuncStubGo=discard
|
||||
fun:reflect.makeFuncStub=uninstrumented
|
||||
fun:reflect.makeFuncStub=discard
|
||||
|
||||
|
||||
###############################################################################
|
||||
# lib/Fuzzer
|
||||
###############################################################################
|
||||
# Replaces __sanitizer_cov_trace_cmp with __dfsw___sanitizer_cov_trace_cmp
|
||||
fun:__sanitizer_cov_trace_cmp=custom
|
||||
fun:__sanitizer_cov_trace_cmp=uninstrumented
|
||||
|
||||
# Ignores all other __sanitizer callbacks.
|
||||
fun:__sanitizer_*=uninstrumented
|
||||
fun:__sanitizer_*=discard
|
||||
|
||||
# Don't add extra parameters to the Fuzzer callback.
|
||||
fun:LLVMFuzzerTestOneInput=uninstrumented
|
||||
|
@ -17,8 +17,10 @@ on_exit() {
|
||||
rm -f ${DIFF_B} 2> /dev/null
|
||||
}
|
||||
|
||||
# Ignore __sanitizer_cov_trace* because they are implemented elsewhere.
|
||||
trap on_exit EXIT
|
||||
grep -E "^fun:.*=custom" ${DFSAN_ABI_LIST} | grep -v "dfsan_get_label" \
|
||||
grep -E "^fun:.*=custom" ${DFSAN_ABI_LIST} \
|
||||
| grep -v "dfsan_get_label\|__sanitizer_cov_trace" \
|
||||
| sed "s/^fun:\(.*\)=custom.*/\1/" | sort > $DIFF_A
|
||||
grep -E "__dfsw.*\(" ${DFSAN_CUSTOM_WRAPPERS} \
|
||||
| sed "s/.*__dfsw_\(.*\)(.*/\1/" | sort > $DIFF_B
|
||||
@ -32,7 +34,7 @@ fi
|
||||
|
||||
grep -E __dfsw_ ${DFSAN_CUSTOM_WRAPPERS} \
|
||||
| sed "s/.*__dfsw_\([^(]*\).*/\1/" | sort > $DIFF_A
|
||||
grep -E "^\\s*test_.*\(\);" ${DFSAN_CUSTOM_TESTS} \
|
||||
grep -E "^[[:space:]]*test_.*\(\);" ${DFSAN_CUSTOM_TESTS} \
|
||||
| sed "s/.*test_\(.*\)();/\1/" | sort > $DIFF_B
|
||||
diff -u $DIFF_A $DIFF_B > ${DIFFOUT}
|
||||
if [ $? -ne 0 ]
|
||||
|
@ -219,7 +219,6 @@ const interpose_substitution substitution_##func_name[] \
|
||||
namespace __interception { \
|
||||
FUNC_TYPE(func) PTR_TO_REAL(func); \
|
||||
} \
|
||||
DECLARE_WRAPPER_WINAPI(ret_type, func, __VA_ARGS__) \
|
||||
extern "C" \
|
||||
INTERCEPTOR_ATTRIBUTE \
|
||||
ret_type __stdcall WRAP(func)(__VA_ARGS__)
|
||||
|
@ -84,6 +84,7 @@ static size_t RoundUpToInstrBoundary(size_t size, char *code) {
|
||||
cursor += 2;
|
||||
continue;
|
||||
case '\xE9': // E9 XX YY ZZ WW = jmp WWZZYYXX
|
||||
case '\xB8': // B8 XX YY ZZ WW = mov eax, WWZZYYXX
|
||||
cursor += 5;
|
||||
continue;
|
||||
}
|
||||
@ -182,10 +183,14 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
|
||||
}
|
||||
|
||||
static const void **InterestingDLLsAvailable() {
|
||||
const char *InterestingDLLs[] = {"kernel32.dll",
|
||||
"msvcr110.dll", // VS2012
|
||||
"msvcr120.dll", // VS2013
|
||||
NULL};
|
||||
const char *InterestingDLLs[] = {
|
||||
"kernel32.dll",
|
||||
"msvcr110.dll", // VS2012
|
||||
"msvcr120.dll", // VS2013
|
||||
// NTDLL should go last as it exports some functions that we should override
|
||||
// in the CRT [presumably only used internally].
|
||||
"ntdll.dll", NULL
|
||||
};
|
||||
static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
|
||||
if (!result[0]) {
|
||||
for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "sanitizer_common/sanitizer_procmaps.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||
#include "sanitizer_common/sanitizer_stoptheworld.h"
|
||||
#include "sanitizer_common/sanitizer_suppressions.h"
|
||||
#include "sanitizer_common/sanitizer_report_decorator.h"
|
||||
|
||||
@ -127,13 +126,14 @@ static inline bool CanBeAHeapPointer(uptr p) {
|
||||
|
||||
// Scans the memory range, looking for byte patterns that point into allocator
|
||||
// chunks. Marks those chunks with |tag| and adds them to |frontier|.
|
||||
// There are two usage modes for this function: finding reachable or ignored
|
||||
// chunks (|tag| = kReachable or kIgnored) and finding indirectly leaked chunks
|
||||
// There are two usage modes for this function: finding reachable chunks
|
||||
// (|tag| = kReachable) and finding indirectly leaked chunks
|
||||
// (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill,
|
||||
// so |frontier| = 0.
|
||||
void ScanRangeForPointers(uptr begin, uptr end,
|
||||
Frontier *frontier,
|
||||
const char *region_type, ChunkTag tag) {
|
||||
CHECK(tag == kReachable || tag == kIndirectlyLeaked);
|
||||
const uptr alignment = flags()->pointer_alignment();
|
||||
LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end);
|
||||
uptr pp = begin;
|
||||
@ -147,9 +147,7 @@ void ScanRangeForPointers(uptr begin, uptr end,
|
||||
// Pointers to self don't count. This matters when tag == kIndirectlyLeaked.
|
||||
if (chunk == begin) continue;
|
||||
LsanMetadata m(chunk);
|
||||
// Reachable beats ignored beats leaked.
|
||||
if (m.tag() == kReachable) continue;
|
||||
if (m.tag() == kIgnored && tag != kReachable) continue;
|
||||
if (m.tag() == kReachable || m.tag() == kIgnored) continue;
|
||||
|
||||
// Do this check relatively late so we can log only the interesting cases.
|
||||
if (!flags()->use_poisoned && WordIsPoisoned(pp)) {
|
||||
@ -288,7 +286,7 @@ static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) {
|
||||
LsanMetadata m(chunk);
|
||||
if (m.allocated() && m.tag() != kReachable) {
|
||||
ScanRangeForPointers(chunk, chunk + m.requested_size(),
|
||||
/* frontier */ 0, "HEAP", kIndirectlyLeaked);
|
||||
/* frontier */ nullptr, "HEAP", kIndirectlyLeaked);
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,8 +296,11 @@ static void CollectIgnoredCb(uptr chunk, void *arg) {
|
||||
CHECK(arg);
|
||||
chunk = GetUserBegin(chunk);
|
||||
LsanMetadata m(chunk);
|
||||
if (m.allocated() && m.tag() == kIgnored)
|
||||
if (m.allocated() && m.tag() == kIgnored) {
|
||||
LOG_POINTERS("Ignored: chunk %p-%p of size %zu.\n",
|
||||
chunk, chunk + m.requested_size(), m.requested_size());
|
||||
reinterpret_cast<Frontier *>(arg)->push_back(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the appropriate tag on each chunk.
|
||||
@ -307,26 +308,33 @@ static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
|
||||
// Holds the flood fill frontier.
|
||||
Frontier frontier(1);
|
||||
|
||||
ForEachChunk(CollectIgnoredCb, &frontier);
|
||||
ProcessGlobalRegions(&frontier);
|
||||
ProcessThreads(suspended_threads, &frontier);
|
||||
ProcessRootRegions(&frontier);
|
||||
FloodFillTag(&frontier, kReachable);
|
||||
|
||||
// The check here is relatively expensive, so we do this in a separate flood
|
||||
// fill. That way we can skip the check for chunks that are reachable
|
||||
// otherwise.
|
||||
LOG_POINTERS("Processing platform-specific allocations.\n");
|
||||
CHECK_EQ(0, frontier.size());
|
||||
ProcessPlatformSpecificAllocations(&frontier);
|
||||
FloodFillTag(&frontier, kReachable);
|
||||
|
||||
LOG_POINTERS("Scanning ignored chunks.\n");
|
||||
CHECK_EQ(0, frontier.size());
|
||||
ForEachChunk(CollectIgnoredCb, &frontier);
|
||||
FloodFillTag(&frontier, kIgnored);
|
||||
|
||||
// Iterate over leaked chunks and mark those that are reachable from other
|
||||
// leaked chunks.
|
||||
LOG_POINTERS("Scanning leaked chunks.\n");
|
||||
ForEachChunk(MarkIndirectlyLeakedCb, 0 /* arg */);
|
||||
ForEachChunk(MarkIndirectlyLeakedCb, nullptr);
|
||||
}
|
||||
|
||||
// ForEachChunk callback. Resets the tags to pre-leak-check state.
|
||||
static void ResetTagsCb(uptr chunk, void *arg) {
|
||||
(void)arg;
|
||||
chunk = GetUserBegin(chunk);
|
||||
LsanMetadata m(chunk);
|
||||
if (m.allocated() && m.tag() != kIgnored)
|
||||
m.set_tag(kDirectlyLeaked);
|
||||
}
|
||||
|
||||
static void PrintStackTraceById(u32 stack_trace_id) {
|
||||
@ -372,35 +380,33 @@ static void PrintMatchedSuppressions() {
|
||||
Printf("%s\n\n", line);
|
||||
}
|
||||
|
||||
struct DoLeakCheckParam {
|
||||
struct CheckForLeaksParam {
|
||||
bool success;
|
||||
LeakReport leak_report;
|
||||
};
|
||||
|
||||
static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads,
|
||||
void *arg) {
|
||||
DoLeakCheckParam *param = reinterpret_cast<DoLeakCheckParam *>(arg);
|
||||
static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
|
||||
void *arg) {
|
||||
CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg);
|
||||
CHECK(param);
|
||||
CHECK(!param->success);
|
||||
ClassifyAllChunks(suspended_threads);
|
||||
ForEachChunk(CollectLeaksCb, ¶m->leak_report);
|
||||
// Clean up for subsequent leak checks. This assumes we did not overwrite any
|
||||
// kIgnored tags.
|
||||
ForEachChunk(ResetTagsCb, nullptr);
|
||||
param->success = true;
|
||||
}
|
||||
|
||||
void DoLeakCheck() {
|
||||
EnsureMainThreadIDIsCorrect();
|
||||
BlockingMutexLock l(&global_mutex);
|
||||
static bool already_done;
|
||||
if (already_done) return;
|
||||
already_done = true;
|
||||
static bool CheckForLeaks() {
|
||||
if (&__lsan_is_turned_off && __lsan_is_turned_off())
|
||||
return;
|
||||
|
||||
DoLeakCheckParam param;
|
||||
return false;
|
||||
EnsureMainThreadIDIsCorrect();
|
||||
CheckForLeaksParam param;
|
||||
param.success = false;
|
||||
LockThreadRegistry();
|
||||
LockAllocator();
|
||||
StopTheWorld(DoLeakCheckCallback, ¶m);
|
||||
DoStopTheWorld(CheckForLeaksCallback, ¶m);
|
||||
UnlockAllocator();
|
||||
UnlockThreadRegistry();
|
||||
|
||||
@ -424,25 +430,42 @@ void DoLeakCheck() {
|
||||
PrintMatchedSuppressions();
|
||||
if (unsuppressed_count > 0) {
|
||||
param.leak_report.PrintSummary();
|
||||
if (flags()->exitcode) {
|
||||
if (common_flags()->coverage)
|
||||
__sanitizer_cov_dump();
|
||||
internal__exit(flags()->exitcode);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DoLeakCheck() {
|
||||
BlockingMutexLock l(&global_mutex);
|
||||
static bool already_done;
|
||||
if (already_done) return;
|
||||
already_done = true;
|
||||
bool have_leaks = CheckForLeaks();
|
||||
if (!have_leaks) {
|
||||
return;
|
||||
}
|
||||
if (flags()->exitcode) {
|
||||
if (common_flags()->coverage)
|
||||
__sanitizer_cov_dump();
|
||||
internal__exit(flags()->exitcode);
|
||||
}
|
||||
}
|
||||
|
||||
static int DoRecoverableLeakCheck() {
|
||||
BlockingMutexLock l(&global_mutex);
|
||||
bool have_leaks = CheckForLeaks();
|
||||
return have_leaks ? 1 : 0;
|
||||
}
|
||||
|
||||
static Suppression *GetSuppressionForAddr(uptr addr) {
|
||||
Suppression *s = nullptr;
|
||||
|
||||
// Suppress by module name.
|
||||
const char *module_name;
|
||||
uptr module_offset;
|
||||
SuppressionContext *suppressions = GetSuppressionContext();
|
||||
if (Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(addr, &module_name,
|
||||
&module_offset) &&
|
||||
suppressions->Match(module_name, kSuppressionLeak, &s))
|
||||
return s;
|
||||
if (const char *module_name =
|
||||
Symbolizer::GetOrInit()->GetModuleNameForPc(addr))
|
||||
if (suppressions->Match(module_name, kSuppressionLeak, &s))
|
||||
return s;
|
||||
|
||||
// Suppress by file or function name.
|
||||
SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
|
||||
@ -679,6 +702,15 @@ void __lsan_do_leak_check() {
|
||||
#endif // CAN_SANITIZE_LEAKS
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __lsan_do_recoverable_leak_check() {
|
||||
#if CAN_SANITIZE_LEAKS
|
||||
if (common_flags()->detect_leaks)
|
||||
return __lsan::DoRecoverableLeakCheck();
|
||||
#endif // CAN_SANITIZE_LEAKS
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
int __lsan_is_turned_off() {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#include "sanitizer_common/sanitizer_stoptheworld.h"
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
|
||||
#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips64)) \
|
||||
@ -99,6 +100,8 @@ typedef InternalMmapVector<uptr> Frontier;
|
||||
void InitializePlatformSpecificModules();
|
||||
void ProcessGlobalRegions(Frontier *frontier);
|
||||
void ProcessPlatformSpecificAllocations(Frontier *frontier);
|
||||
// Run stoptheworld while holding any platform-specific locks.
|
||||
void DoStopTheWorld(StopTheWorldCallback callback, void* argument);
|
||||
|
||||
void ScanRangeForPointers(uptr begin, uptr end,
|
||||
Frontier *frontier,
|
||||
|
@ -85,10 +85,6 @@ static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
|
||||
// Scans global variables for heap pointers.
|
||||
void ProcessGlobalRegions(Frontier *frontier) {
|
||||
if (!flags()->use_globals) return;
|
||||
// FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of
|
||||
// deadlocking by running this under StopTheWorld. However, the lock is
|
||||
// reentrant, so we should be able to fix this by acquiring the lock before
|
||||
// suspending threads.
|
||||
dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
|
||||
}
|
||||
|
||||
@ -114,7 +110,7 @@ static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) {
|
||||
reinterpret_cast<ProcessPlatformAllocParam *>(arg);
|
||||
chunk = GetUserBegin(chunk);
|
||||
LsanMetadata m(chunk);
|
||||
if (m.allocated() && m.tag() != kReachable) {
|
||||
if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) {
|
||||
u32 stack_id = m.stack_trace_id();
|
||||
uptr caller_pc = 0;
|
||||
if (stack_id > 0)
|
||||
@ -153,5 +149,30 @@ void ProcessPlatformSpecificAllocations(Frontier *frontier) {
|
||||
ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg);
|
||||
}
|
||||
|
||||
struct DoStopTheWorldParam {
|
||||
StopTheWorldCallback callback;
|
||||
void *argument;
|
||||
};
|
||||
|
||||
static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size,
|
||||
void *data) {
|
||||
DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);
|
||||
StopTheWorld(param->callback, param->argument);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one
|
||||
// of the threads is frozen while holding the libdl lock, the tracer will hang
|
||||
// in dl_iterate_phdr() forever.
|
||||
// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the
|
||||
// tracer task and the thread that spawned it. Thus, if we run the tracer task
|
||||
// while holding the libdl lock in the parent thread, we can safely reenter it
|
||||
// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()
|
||||
// callback in the parent thread.
|
||||
void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
|
||||
DoStopTheWorldParam param = {callback, argument};
|
||||
dl_iterate_phdr(DoStopTheWorldCallback, ¶m);
|
||||
}
|
||||
|
||||
} // namespace __lsan
|
||||
#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX
|
||||
|
@ -208,7 +208,7 @@ extern "C" void *__lsan_thread_start_func(void *arg) {
|
||||
// Wait until the last iteration to maximize the chance that we are the last
|
||||
// destructor to run.
|
||||
if (pthread_setspecific(g_thread_finalize_key,
|
||||
(void*)kPthreadDestructorIterations)) {
|
||||
(void*)GetPthreadDestructorIterations())) {
|
||||
Report("LeakSanitizer: failed to set thread key.\n");
|
||||
Die();
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ namespace __lsan {
|
||||
class ThreadContext : public ThreadContextBase {
|
||||
public:
|
||||
explicit ThreadContext(int tid);
|
||||
void OnStarted(void *arg);
|
||||
void OnFinished();
|
||||
void OnStarted(void *arg) override;
|
||||
void OnFinished() override;
|
||||
uptr stack_begin() { return stack_begin_; }
|
||||
uptr stack_end() { return stack_end_; }
|
||||
uptr tls_begin() { return tls_begin_; }
|
||||
|
@ -26,6 +26,8 @@
|
||||
#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"
|
||||
|
||||
// ACHTUNG! No system header includes in this file.
|
||||
|
||||
@ -133,11 +135,6 @@ static void RegisterMsanFlags(FlagParser *parser, Flags *f) {
|
||||
}
|
||||
|
||||
static void InitializeFlags() {
|
||||
Flags *f = flags();
|
||||
FlagParser parser;
|
||||
RegisterMsanFlags(&parser, f);
|
||||
RegisterCommonFlags(&parser);
|
||||
|
||||
SetCommonFlagsDefaults();
|
||||
{
|
||||
CommonFlags cf;
|
||||
@ -151,14 +148,35 @@ static void InitializeFlags() {
|
||||
OverrideCommonFlags(cf);
|
||||
}
|
||||
|
||||
Flags *f = flags();
|
||||
f->SetDefaults();
|
||||
|
||||
FlagParser parser;
|
||||
RegisterMsanFlags(&parser, f);
|
||||
RegisterCommonFlags(&parser);
|
||||
|
||||
#if MSAN_CONTAINS_UBSAN
|
||||
__ubsan::Flags *uf = __ubsan::flags();
|
||||
uf->SetDefaults();
|
||||
|
||||
FlagParser ubsan_parser;
|
||||
__ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
|
||||
RegisterCommonFlags(&ubsan_parser);
|
||||
#endif
|
||||
|
||||
// Override from user-specified string.
|
||||
if (__msan_default_options)
|
||||
parser.ParseString(__msan_default_options());
|
||||
#if MSAN_CONTAINS_UBSAN
|
||||
const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
|
||||
ubsan_parser.ParseString(ubsan_default_options);
|
||||
#endif
|
||||
|
||||
const char *msan_options = GetEnv("MSAN_OPTIONS");
|
||||
parser.ParseString(msan_options);
|
||||
#if MSAN_CONTAINS_UBSAN
|
||||
ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
|
||||
#endif
|
||||
VPrintf(1, "MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>");
|
||||
|
||||
SetVerbosity(common_flags()->verbosity);
|
||||
@ -355,13 +373,12 @@ void __msan_init() {
|
||||
InitTlsSize();
|
||||
|
||||
InitializeFlags();
|
||||
CacheBinaryName();
|
||||
__sanitizer_set_report_path(common_flags()->log_path);
|
||||
|
||||
InitializeInterceptors();
|
||||
InstallAtExitHandler(); // Needs __cxa_atexit interceptor.
|
||||
|
||||
if (MSAN_REPLACE_OPERATORS_NEW_AND_DELETE)
|
||||
ReplaceOperatorsNewAndDelete();
|
||||
DisableCoreDumperIfNecessary();
|
||||
if (StackSizeIsUnlimited()) {
|
||||
VPrintf(1, "Unlimited stack, doing reexec\n");
|
||||
@ -374,7 +391,7 @@ void __msan_init() {
|
||||
__msan_clear_on_return();
|
||||
if (__msan_get_track_origins())
|
||||
VPrintf(1, "msan_track_origins\n");
|
||||
if (!InitShadow(/* map_shadow */ true, __msan_get_track_origins())) {
|
||||
if (!InitShadow(__msan_get_track_origins())) {
|
||||
Printf("FATAL: MemorySanitizer can not mmap the shadow memory.\n");
|
||||
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");
|
||||
@ -394,6 +411,10 @@ void __msan_init() {
|
||||
SetCurrentThread(main_thread);
|
||||
main_thread->ThreadStart();
|
||||
|
||||
#if MSAN_CONTAINS_UBSAN
|
||||
__ubsan::InitAsPlugin();
|
||||
#endif
|
||||
|
||||
VPrintf(1, "MemorySanitizer init done\n");
|
||||
|
||||
msan_init_is_running = 0;
|
||||
@ -543,6 +564,13 @@ u32 __msan_get_origin(const void *a) {
|
||||
return *(u32*)origin_ptr;
|
||||
}
|
||||
|
||||
int __msan_origin_is_descendant_or_same(u32 this_id, u32 prev_id) {
|
||||
Origin o = Origin::FromRawId(this_id);
|
||||
while (o.raw_id() != prev_id && o.isChainedOrigin())
|
||||
o = o.getNextChainedOrigin(nullptr);
|
||||
return o.raw_id() == prev_id;
|
||||
}
|
||||
|
||||
u32 __msan_get_umr_origin() {
|
||||
return __msan_origin_tls;
|
||||
}
|
||||
|
@ -20,11 +20,16 @@
|
||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||
#include "msan_interface_internal.h"
|
||||
#include "msan_flags.h"
|
||||
#include "ubsan/ubsan_platform.h"
|
||||
|
||||
#ifndef MSAN_REPLACE_OPERATORS_NEW_AND_DELETE
|
||||
# define MSAN_REPLACE_OPERATORS_NEW_AND_DELETE 1
|
||||
#endif
|
||||
|
||||
#ifndef MSAN_CONTAINS_UBSAN
|
||||
# define MSAN_CONTAINS_UBSAN CAN_SANITIZE_UB
|
||||
#endif
|
||||
|
||||
struct MappingDesc {
|
||||
uptr start;
|
||||
uptr end;
|
||||
@ -47,6 +52,25 @@ const MappingDesc kMemoryLayout[] = {
|
||||
#define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x4000000000ULL)
|
||||
#define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x002000000000)
|
||||
|
||||
#elif SANITIZER_LINUX && defined(__powerpc64__)
|
||||
|
||||
const MappingDesc kMemoryLayout[] = {
|
||||
{0x000000000000ULL, 0x000100000000ULL, MappingDesc::APP, "low memory"},
|
||||
{0x000100000000ULL, 0x080000000000ULL, MappingDesc::INVALID, "invalid"},
|
||||
{0x080000000000ULL, 0x180100000000ULL, MappingDesc::SHADOW, "shadow"},
|
||||
{0x180100000000ULL, 0x1C0000000000ULL, MappingDesc::INVALID, "invalid"},
|
||||
{0x1C0000000000ULL, 0x2C0100000000ULL, MappingDesc::ORIGIN, "origin"},
|
||||
{0x2C0100000000ULL, 0x300000000000ULL, MappingDesc::INVALID, "invalid"},
|
||||
{0x300000000000ULL, 0x400000000000ULL, MappingDesc::APP, "high memory"}};
|
||||
|
||||
// Maps low and high app ranges to contiguous space with zero base:
|
||||
// Low: 0000 0000 0000 - 0000 ffff ffff -> 1000 0000 0000 - 1000 ffff ffff
|
||||
// High: 3000 0000 0000 - 3fff ffff ffff -> 0000 0000 0000 - 0fff ffff ffff
|
||||
#define LINEARIZE_MEM(mem) \
|
||||
(((uptr)(mem) & ~0x200000000000ULL) ^ 0x100000000000ULL)
|
||||
#define MEM_TO_SHADOW(mem) (LINEARIZE_MEM((mem)) + 0x080000000000ULL)
|
||||
#define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x140000000000ULL)
|
||||
|
||||
#elif SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 64
|
||||
|
||||
// Low memory: main binary, MAP_32BIT mappings and modules
|
||||
@ -120,7 +144,7 @@ extern bool msan_init_is_running;
|
||||
extern int msan_report_count;
|
||||
|
||||
bool ProtectRange(uptr beg, uptr end);
|
||||
bool InitShadow(bool map_shadow, bool init_origins);
|
||||
bool InitShadow(bool init_origins);
|
||||
char *GetProcSelfMaps();
|
||||
void InitializeInterceptors();
|
||||
|
||||
@ -131,7 +155,6 @@ void *MsanReallocate(StackTrace *stack, void *oldp, uptr size,
|
||||
void MsanDeallocate(StackTrace *stack, void *ptr);
|
||||
void InstallTrapHandler();
|
||||
void InstallAtExitHandler();
|
||||
void ReplaceOperatorsNewAndDelete();
|
||||
|
||||
const char *GetStackOriginDescr(u32 id, uptr *pc);
|
||||
|
||||
|
@ -1 +1,2 @@
|
||||
__msan_*
|
||||
__ubsan_*
|
||||
|
@ -55,6 +55,15 @@ struct MsanMapUnmapCallback {
|
||||
static const uptr kMetadataSize = sizeof(Metadata);
|
||||
static const uptr kMaxAllowedMallocSize = 8UL << 30;
|
||||
|
||||
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize,
|
||||
DefaultSizeClassMap,
|
||||
MsanMapUnmapCallback> PrimaryAllocator;
|
||||
#elif defined(__powerpc64__)
|
||||
static const uptr kAllocatorSpace = 0x300000000000;
|
||||
static const uptr kAllocatorSize = 0x020000000000; // 2T
|
||||
static const uptr kMetadataSize = sizeof(Metadata);
|
||||
static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G
|
||||
|
||||
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize,
|
||||
DefaultSizeClassMap,
|
||||
MsanMapUnmapCallback> PrimaryAllocator;
|
||||
|
@ -94,6 +94,13 @@ bool IsInInterceptorScope() {
|
||||
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) )
|
||||
|
||||
#define CHECK_UNPOISONED_STRING(x, n) \
|
||||
CHECK_UNPOISONED_STRING_OF_LEN((x), internal_strlen(x), (n))
|
||||
|
||||
INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) {
|
||||
ENSURE_MSAN_INITED();
|
||||
SIZE_T res = REAL(fread)(ptr, size, nmemb, file);
|
||||
@ -118,6 +125,7 @@ INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb,
|
||||
|
||||
INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) {
|
||||
ENSURE_MSAN_INITED();
|
||||
CHECK_UNPOISONED_STRING(path, 0)
|
||||
SSIZE_T res = REAL(readlink)(path, buf, bufsiz);
|
||||
if (res > 0)
|
||||
__msan_unpoison(buf, res);
|
||||
@ -283,13 +291,11 @@ INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T n) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// FIXME: Add stricter shadow checks in str* interceptors (ex.: strcpy should
|
||||
// check the shadow of the terminating \0 byte).
|
||||
|
||||
INTERCEPTOR(char *, strcpy, char *dest, const char *src) { // NOLINT
|
||||
ENSURE_MSAN_INITED();
|
||||
GET_STORE_STACK_TRACE;
|
||||
SIZE_T n = REAL(strlen)(src);
|
||||
CHECK_UNPOISONED_STRING(src + n, 0);
|
||||
char *res = REAL(strcpy)(dest, src); // NOLINT
|
||||
CopyShadowAndOrigin(dest, src, n + 1, &stack);
|
||||
return res;
|
||||
@ -311,6 +317,7 @@ INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT
|
||||
ENSURE_MSAN_INITED();
|
||||
GET_STORE_STACK_TRACE;
|
||||
SIZE_T n = REAL(strlen)(src);
|
||||
CHECK_UNPOISONED_STRING(src + n, 0);
|
||||
char *res = REAL(stpcpy)(dest, src); // NOLINT
|
||||
CopyShadowAndOrigin(dest, src, n + 1, &stack);
|
||||
return res;
|
||||
@ -322,6 +329,7 @@ INTERCEPTOR(char *, strdup, char *src) {
|
||||
// On FreeBSD strdup() leverages strlen().
|
||||
InterceptorScope interceptor_scope;
|
||||
SIZE_T n = REAL(strlen)(src);
|
||||
CHECK_UNPOISONED_STRING(src + n, 0);
|
||||
char *res = REAL(strdup)(src);
|
||||
CopyShadowAndOrigin(res, src, n + 1, &stack);
|
||||
return res;
|
||||
@ -332,6 +340,7 @@ INTERCEPTOR(char *, __strdup, char *src) {
|
||||
ENSURE_MSAN_INITED();
|
||||
GET_STORE_STACK_TRACE;
|
||||
SIZE_T n = REAL(strlen)(src);
|
||||
CHECK_UNPOISONED_STRING(src + n, 0);
|
||||
char *res = REAL(__strdup)(src);
|
||||
CopyShadowAndOrigin(res, src, n + 1, &stack);
|
||||
return res;
|
||||
@ -381,6 +390,8 @@ INTERCEPTOR(char *, strcat, char *dest, const char *src) { // NOLINT
|
||||
GET_STORE_STACK_TRACE;
|
||||
SIZE_T src_size = REAL(strlen)(src);
|
||||
SIZE_T dest_size = REAL(strlen)(dest);
|
||||
CHECK_UNPOISONED_STRING(src + src_size, 0);
|
||||
CHECK_UNPOISONED_STRING(dest + dest_size, 0);
|
||||
char *res = REAL(strcat)(dest, src); // NOLINT
|
||||
CopyShadowAndOrigin(dest + dest_size, src, src_size + 1, &stack);
|
||||
return res;
|
||||
@ -391,6 +402,7 @@ INTERCEPTOR(char *, strncat, char *dest, const char *src, SIZE_T n) { // NOLINT
|
||||
GET_STORE_STACK_TRACE;
|
||||
SIZE_T dest_size = REAL(strlen)(dest);
|
||||
SIZE_T copy_size = REAL(strnlen)(src, n);
|
||||
CHECK_UNPOISONED_STRING(dest + dest_size, 0);
|
||||
char *res = REAL(strncat)(dest, src, n); // NOLINT
|
||||
CopyShadowAndOrigin(dest + dest_size, src, copy_size, &stack);
|
||||
__msan_unpoison(dest + dest_size + copy_size, 1); // \0
|
||||
@ -667,6 +679,7 @@ static void UnpoisonEnviron() {
|
||||
|
||||
INTERCEPTOR(int, setenv, const char *name, const char *value, int overwrite) {
|
||||
ENSURE_MSAN_INITED();
|
||||
CHECK_UNPOISONED_STRING(name, 0)
|
||||
int res = REAL(setenv)(name, value, overwrite);
|
||||
if (!res) UnpoisonEnviron();
|
||||
return res;
|
||||
@ -1384,6 +1397,14 @@ int OnExit() {
|
||||
if (map) ForEachMappedRegion(map, __msan_unpoison); \
|
||||
} while (false)
|
||||
|
||||
#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
|
||||
if (MsanThread *t = GetCurrentThread()) { \
|
||||
*begin = t->tls_begin(); \
|
||||
*end = t->tls_end(); \
|
||||
} else { \
|
||||
*begin = *end = 0; \
|
||||
}
|
||||
|
||||
#include "sanitizer_common/sanitizer_common_interceptors.inc"
|
||||
|
||||
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s)
|
||||
@ -1420,7 +1441,8 @@ void __msan_clear_and_unpoison(void *a, uptr size) {
|
||||
|
||||
void *__msan_memcpy(void *dest, const void *src, SIZE_T n) {
|
||||
if (!msan_inited) return internal_memcpy(dest, src, n);
|
||||
if (msan_init_is_running) return REAL(memcpy)(dest, src, n);
|
||||
if (msan_init_is_running || __msan::IsInSymbolizer())
|
||||
return REAL(memcpy)(dest, src, n);
|
||||
ENSURE_MSAN_INITED();
|
||||
GET_STORE_STACK_TRACE;
|
||||
void *res = REAL(memcpy)(dest, src, n);
|
||||
|
@ -96,6 +96,13 @@ u32 __msan_chain_origin(u32 id);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
u32 __msan_get_origin(const void *a);
|
||||
|
||||
// Test that this_id is a descendant of prev_id (or they are simply equal).
|
||||
// "descendant" here means that are part of the same chain, created with
|
||||
// __msan_chain_origin.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __msan_origin_is_descendant_or_same(u32 this_id, u32 prev_id);
|
||||
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __msan_clear_on_return();
|
||||
|
||||
|
@ -53,10 +53,19 @@ static bool CheckMemoryRangeAvailability(uptr beg, uptr size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ProtectMemoryRange(uptr beg, uptr size) {
|
||||
static bool ProtectMemoryRange(uptr beg, uptr size, const char *name) {
|
||||
if (size > 0) {
|
||||
uptr end = beg + size - 1;
|
||||
if (!Mprotect(beg, size)) {
|
||||
void *addr = MmapNoAccess(beg, size, name);
|
||||
if (beg == 0 && addr != 0) {
|
||||
// Depending on the kernel configuration, we may not be able to protect
|
||||
// the page at address zero.
|
||||
uptr gap = 16 * GetPageSizeCached();
|
||||
beg += gap;
|
||||
size -= gap;
|
||||
addr = MmapNoAccess(beg, size, name);
|
||||
}
|
||||
if ((uptr)addr != beg) {
|
||||
uptr end = beg + size - 1;
|
||||
Printf("FATAL: Cannot protect memory range %p - %p.\n", beg, end);
|
||||
return false;
|
||||
}
|
||||
@ -95,7 +104,7 @@ static void CheckMemoryLayoutSanity() {
|
||||
}
|
||||
}
|
||||
|
||||
bool InitShadow(bool map_shadow, bool init_origins) {
|
||||
bool InitShadow(bool init_origins) {
|
||||
// Let user know mapping parameters first.
|
||||
VPrintf(1, "__msan_init %p\n", &__msan_init);
|
||||
for (unsigned i = 0; i < kMemoryLayoutSize; ++i)
|
||||
@ -115,15 +124,27 @@ bool InitShadow(bool map_shadow, bool init_origins) {
|
||||
uptr end = kMemoryLayout[i].end;
|
||||
uptr size= end - start;
|
||||
MappingDesc::Type type = kMemoryLayout[i].type;
|
||||
if ((map_shadow && type == MappingDesc::SHADOW) ||
|
||||
(init_origins && type == MappingDesc::ORIGIN)) {
|
||||
if (!CheckMemoryRangeAvailability(start, size)) return false;
|
||||
if ((uptr)MmapFixedNoReserve(start, size) != start) return false;
|
||||
|
||||
bool map = type == MappingDesc::SHADOW ||
|
||||
(init_origins && type == MappingDesc::ORIGIN);
|
||||
bool protect = type == MappingDesc::INVALID ||
|
||||
(!init_origins && type == MappingDesc::ORIGIN);
|
||||
CHECK(!(map && protect));
|
||||
if (!map && !protect)
|
||||
CHECK(type == MappingDesc::APP);
|
||||
if (map) {
|
||||
if (!CheckMemoryRangeAvailability(start, size))
|
||||
return false;
|
||||
if ((uptr)MmapFixedNoReserve(start, size, kMemoryLayout[i].name) != start)
|
||||
return false;
|
||||
if (common_flags()->use_madv_dontdump)
|
||||
DontDumpShadowMemory(start, size);
|
||||
} else if (type == MappingDesc::INVALID) {
|
||||
if (!CheckMemoryRangeAvailability(start, size)) return false;
|
||||
if (!ProtectMemoryRange(start, size)) return false;
|
||||
}
|
||||
if (protect) {
|
||||
if (!CheckMemoryRangeAvailability(start, size))
|
||||
return false;
|
||||
if (!ProtectMemoryRange(start, size, kMemoryLayout[i].name))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,13 +19,6 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace __msan {
|
||||
// This function is a no-op. We need it to make sure that object file
|
||||
// with our replacements will actually be loaded from static MSan
|
||||
// run-time library at link-time.
|
||||
void ReplaceOperatorsNewAndDelete() { }
|
||||
}
|
||||
|
||||
using namespace __msan; // NOLINT
|
||||
|
||||
// Fake std::nothrow_t to avoid including <new>.
|
||||
|
@ -87,7 +87,7 @@ class Origin {
|
||||
CHECK(isChainedOrigin());
|
||||
u32 prev_id;
|
||||
u32 stack_id = ChainedOriginDepotGet(getChainedId(), &prev_id);
|
||||
*stack = StackDepotGet(stack_id);
|
||||
if (stack) *stack = StackDepotGet(stack_id);
|
||||
return Origin(prev_id);
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ void CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack) {
|
||||
void SetShadow(const void *ptr, uptr size, u8 value) {
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
uptr shadow_beg = MEM_TO_SHADOW(ptr);
|
||||
uptr shadow_end = MEM_TO_SHADOW((uptr)ptr + size);
|
||||
uptr shadow_end = shadow_beg + size;
|
||||
if (value ||
|
||||
shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
|
||||
REAL(memset)((void *)shadow_beg, value, shadow_end - shadow_beg);
|
||||
|
@ -103,7 +103,7 @@ void ReportUMR(StackTrace *stack, u32 origin) {
|
||||
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report(" WARNING: MemorySanitizer: use-of-uninitialized-value\n");
|
||||
Report("WARNING: MemorySanitizer: use-of-uninitialized-value\n");
|
||||
Printf("%s", d.End());
|
||||
stack->Print();
|
||||
if (origin) {
|
||||
@ -115,7 +115,7 @@ void ReportUMR(StackTrace *stack, u32 origin) {
|
||||
void ReportExpectedUMRNotFound(StackTrace *stack) {
|
||||
SpinMutexLock l(&CommonSanitizerReportMutex);
|
||||
|
||||
Printf(" WARNING: Expected use of uninitialized value not found\n");
|
||||
Printf("WARNING: Expected use of uninitialized value not found\n");
|
||||
stack->Print();
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ MsanThread *MsanThread::Create(thread_callback_t start_routine,
|
||||
MsanThread *thread = (MsanThread*)MmapOrDie(size, __func__);
|
||||
thread->start_routine_ = start_routine;
|
||||
thread->arg_ = arg;
|
||||
thread->destructor_iterations_ = kPthreadDestructorIterations;
|
||||
thread->destructor_iterations_ = GetPthreadDestructorIterations();
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
@ -134,9 +134,12 @@ static bool TrackingOrigins() {
|
||||
__msan_set_origin(&x, sizeof(x), 0x1234);
|
||||
U4 origin = __msan_get_origin(&x);
|
||||
__msan_set_origin(&x, sizeof(x), 0);
|
||||
return origin == 0x1234;
|
||||
return __msan_origin_is_descendant_or_same(origin, 0x1234);
|
||||
}
|
||||
|
||||
#define EXPECT_ORIGIN(expected, origin) \
|
||||
EXPECT_TRUE(__msan_origin_is_descendant_or_same((origin), (expected)))
|
||||
|
||||
#define EXPECT_UMR(action) \
|
||||
do { \
|
||||
__msan_set_expect_umr(1); \
|
||||
@ -144,14 +147,13 @@ static bool TrackingOrigins() {
|
||||
__msan_set_expect_umr(0); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_UMR_O(action, origin) \
|
||||
do { \
|
||||
__msan_set_expect_umr(1); \
|
||||
action; \
|
||||
__msan_set_expect_umr(0); \
|
||||
if (TrackingOrigins()) \
|
||||
EXPECT_EQ(origin, __msan_get_umr_origin()); \
|
||||
} while (0)
|
||||
#define EXPECT_UMR_O(action, origin) \
|
||||
do { \
|
||||
__msan_set_expect_umr(1); \
|
||||
action; \
|
||||
__msan_set_expect_umr(0); \
|
||||
if (TrackingOrigins()) EXPECT_ORIGIN(origin, __msan_get_umr_origin()); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_POISONED(x) ExpectPoisoned(x)
|
||||
|
||||
@ -166,8 +168,7 @@ void ExpectPoisoned(const T& t) {
|
||||
template<typename T>
|
||||
void ExpectPoisonedWithOrigin(const T& t, unsigned origin) {
|
||||
EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t)));
|
||||
if (TrackingOrigins())
|
||||
EXPECT_EQ(origin, __msan_get_origin((void*)&t));
|
||||
if (TrackingOrigins()) EXPECT_ORIGIN(origin, __msan_get_origin((void *)&t));
|
||||
}
|
||||
|
||||
#define EXPECT_NOT_POISONED(x) EXPECT_EQ(true, TestForNotPoisoned((x)))
|
||||
@ -3902,15 +3903,15 @@ TEST(VectorMaddTest, mmx_pmadd_wd) {
|
||||
#endif // defined(__clang__)
|
||||
|
||||
TEST(MemorySanitizerOrigins, SetGet) {
|
||||
EXPECT_EQ(TrackingOrigins(), __msan_get_track_origins());
|
||||
EXPECT_EQ(TrackingOrigins(), !!__msan_get_track_origins());
|
||||
if (!TrackingOrigins()) return;
|
||||
int x;
|
||||
__msan_set_origin(&x, sizeof(x), 1234);
|
||||
EXPECT_EQ(1234U, __msan_get_origin(&x));
|
||||
EXPECT_ORIGIN(1234U, __msan_get_origin(&x));
|
||||
__msan_set_origin(&x, sizeof(x), 5678);
|
||||
EXPECT_EQ(5678U, __msan_get_origin(&x));
|
||||
EXPECT_ORIGIN(5678U, __msan_get_origin(&x));
|
||||
__msan_set_origin(&x, sizeof(x), 0);
|
||||
EXPECT_EQ(0U, __msan_get_origin(&x));
|
||||
EXPECT_ORIGIN(0U, __msan_get_origin(&x));
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -3926,12 +3927,12 @@ TEST(MemorySanitizerOrigins, InitializedStoreDoesNotChangeOrigin) {
|
||||
S s;
|
||||
U4 origin = rand(); // NOLINT
|
||||
s.a = *GetPoisonedO<U2>(0, origin);
|
||||
EXPECT_EQ(origin, __msan_get_origin(&s.a));
|
||||
EXPECT_EQ(origin, __msan_get_origin(&s.b));
|
||||
EXPECT_ORIGIN(origin, __msan_get_origin(&s.a));
|
||||
EXPECT_ORIGIN(origin, __msan_get_origin(&s.b));
|
||||
|
||||
s.b = 42;
|
||||
EXPECT_EQ(origin, __msan_get_origin(&s.a));
|
||||
EXPECT_EQ(origin, __msan_get_origin(&s.b));
|
||||
EXPECT_ORIGIN(origin, __msan_get_origin(&s.a));
|
||||
EXPECT_ORIGIN(origin, __msan_get_origin(&s.b));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@ -3947,7 +3948,8 @@ void BinaryOpOriginTest(BinaryOp op) {
|
||||
*z = op(*x, *y);
|
||||
U4 origin = __msan_get_origin(z);
|
||||
EXPECT_POISONED_O(*z, origin);
|
||||
EXPECT_EQ(true, origin == ox || origin == oy);
|
||||
EXPECT_EQ(true, __msan_origin_is_descendant_or_same(origin, ox) ||
|
||||
__msan_origin_is_descendant_or_same(origin, oy));
|
||||
|
||||
// y is poisoned, x is not.
|
||||
*x = 10101;
|
||||
@ -3956,7 +3958,7 @@ void BinaryOpOriginTest(BinaryOp op) {
|
||||
__msan_set_origin(z, sizeof(*z), 0);
|
||||
*z = op(*x, *y);
|
||||
EXPECT_POISONED_O(*z, oy);
|
||||
EXPECT_EQ(__msan_get_origin(z), oy);
|
||||
EXPECT_ORIGIN(oy, __msan_get_origin(z));
|
||||
|
||||
// x is poisoned, y is not.
|
||||
*x = *GetPoisonedO<T>(0, ox);
|
||||
@ -3965,7 +3967,7 @@ void BinaryOpOriginTest(BinaryOp op) {
|
||||
__msan_set_origin(z, sizeof(*z), 0);
|
||||
*z = op(*x, *y);
|
||||
EXPECT_POISONED_O(*z, ox);
|
||||
EXPECT_EQ(__msan_get_origin(z), ox);
|
||||
EXPECT_ORIGIN(ox, __msan_get_origin(z));
|
||||
}
|
||||
|
||||
template<class T> INLINE T XOR(const T &a, const T&b) { return a ^ b; }
|
||||
|
@ -20,23 +20,18 @@
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfilingUtil.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#endif
|
||||
#include <sys/file.h>
|
||||
|
||||
#define I386_FREEBSD (defined(__FreeBSD__) && defined(__i386__))
|
||||
|
||||
#if !I386_FREEBSD
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_MSC_VER) && !I386_FREEBSD
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
@ -51,7 +46,6 @@ typedef unsigned long long uint64_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
int mkdir(const char*, unsigned short);
|
||||
#endif
|
||||
|
||||
/* #define DEBUG_GCDAPROFILING */
|
||||
@ -208,21 +202,6 @@ static char *mangle_filename(const char *orig_filename) {
|
||||
return new_filename;
|
||||
}
|
||||
|
||||
static void recursive_mkdir(char *path) {
|
||||
int i;
|
||||
|
||||
for (i = 1; path[i] != '\0'; ++i) {
|
||||
if (path[i] != '/') continue;
|
||||
path[i] = '\0';
|
||||
#ifdef _WIN32
|
||||
_mkdir(path);
|
||||
#else
|
||||
mkdir(path, 0755); /* Some of these will fail, ignore it. */
|
||||
#endif
|
||||
path[i] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
static int map_file() {
|
||||
fseek(output_file, 0L, SEEK_END);
|
||||
file_size = ftell(output_file);
|
||||
@ -282,7 +261,7 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4],
|
||||
fd = open(filename, O_RDWR | O_CREAT, 0644);
|
||||
if (fd == -1) {
|
||||
/* Try creating the directories first then opening the file. */
|
||||
recursive_mkdir(filename);
|
||||
__llvm_profile_recursive_mkdir(filename);
|
||||
fd = open(filename, O_RDWR | O_CREAT, 0644);
|
||||
if (fd == -1) {
|
||||
/* Bah! It's hopeless. */
|
||||
@ -294,6 +273,11 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4],
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to flock the file to serialize concurrent processes writing out to the
|
||||
* same GCDA. This can fail if the filesystem doesn't support it, but in that
|
||||
* case we'll just carry on with the old racy behaviour and hope for the best.
|
||||
*/
|
||||
flock(fd, LOCK_EX);
|
||||
output_file = fdopen(fd, mode);
|
||||
|
||||
/* Initialize the write buffer. */
|
||||
@ -493,6 +477,7 @@ void llvm_gcda_end_file() {
|
||||
}
|
||||
|
||||
fclose(output_file);
|
||||
flock(fd, LOCK_UN);
|
||||
output_file = NULL;
|
||||
write_buffer = NULL;
|
||||
}
|
||||
|
@ -62,7 +62,9 @@ uint64_t *__llvm_profile_end_counters(void);
|
||||
*
|
||||
* Writes to the file with the last name given to \a __llvm_profile_set_filename(),
|
||||
* or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable,
|
||||
* or if that's not set, \c "default.profdata".
|
||||
* or if that's not set, the last name given to
|
||||
* \a __llvm_profile_override_default_filename(), or if that's not set,
|
||||
* \c "default.profraw".
|
||||
*/
|
||||
int __llvm_profile_write_file(void);
|
||||
|
||||
@ -77,6 +79,19 @@ int __llvm_profile_write_file(void);
|
||||
*/
|
||||
void __llvm_profile_set_filename(const char *Name);
|
||||
|
||||
/*!
|
||||
* \brief Set the filename for writing instrumentation data, unless the
|
||||
* \c LLVM_PROFILE_FILE environment variable was set.
|
||||
*
|
||||
* Unless overridden, sets the filename to be used for subsequent calls to
|
||||
* \a __llvm_profile_write_file().
|
||||
*
|
||||
* \c Name is not copied, so it must remain valid. Passing NULL resets the
|
||||
* filename logic to the default behaviour (unless the \c LLVM_PROFILE_FILE
|
||||
* was set in which case it has no effect).
|
||||
*/
|
||||
void __llvm_profile_override_default_filename(const char *Name);
|
||||
|
||||
/*! \brief Register to write instrumentation data to file at exit. */
|
||||
int __llvm_profile_register_write_file_atexit(void);
|
||||
|
||||
|
@ -8,10 +8,11 @@
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfiling.h"
|
||||
#include "InstrProfilingUtil.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
|
||||
|
||||
@ -76,40 +77,61 @@ static int writeFileWithName(const char *OutputName) {
|
||||
__attribute__((weak)) int __llvm_profile_OwnsFilename = 0;
|
||||
__attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL;
|
||||
|
||||
static void setFilename(const char *Filename, int OwnsFilename) {
|
||||
if (__llvm_profile_OwnsFilename)
|
||||
free(UNCONST(__llvm_profile_CurrentFilename));
|
||||
|
||||
__llvm_profile_CurrentFilename = Filename;
|
||||
__llvm_profile_OwnsFilename = OwnsFilename;
|
||||
}
|
||||
|
||||
static void truncateCurrentFile(void) {
|
||||
const char *Filename = __llvm_profile_CurrentFilename;
|
||||
const char *Filename;
|
||||
FILE *File;
|
||||
|
||||
Filename = __llvm_profile_CurrentFilename;
|
||||
if (!Filename || !Filename[0])
|
||||
return;
|
||||
|
||||
/* Create the directory holding the file, if needed. */
|
||||
if (strchr(Filename, '/')) {
|
||||
char *Copy = malloc(strlen(Filename) + 1);
|
||||
strcpy(Copy, Filename);
|
||||
__llvm_profile_recursive_mkdir(Copy);
|
||||
free(Copy);
|
||||
}
|
||||
|
||||
/* Truncate the file. Later we'll reopen and append. */
|
||||
FILE *File = fopen(Filename, "w");
|
||||
File = fopen(Filename, "w");
|
||||
if (!File)
|
||||
return;
|
||||
fclose(File);
|
||||
}
|
||||
|
||||
static void setDefaultFilename(void) { setFilename("default.profraw", 0); }
|
||||
static void setFilename(const char *Filename, int OwnsFilename) {
|
||||
/* Check if this is a new filename and therefore needs truncation. */
|
||||
int NewFile = !__llvm_profile_CurrentFilename ||
|
||||
(Filename && strcmp(Filename, __llvm_profile_CurrentFilename));
|
||||
if (__llvm_profile_OwnsFilename)
|
||||
free(UNCONST(__llvm_profile_CurrentFilename));
|
||||
|
||||
__llvm_profile_CurrentFilename = Filename;
|
||||
__llvm_profile_OwnsFilename = OwnsFilename;
|
||||
|
||||
/* If not a new file, append to support profiling multiple shared objects. */
|
||||
if (NewFile)
|
||||
truncateCurrentFile();
|
||||
}
|
||||
|
||||
static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); }
|
||||
|
||||
int getpid(void);
|
||||
static int setFilenameFromEnvironment(void) {
|
||||
const char *Filename = getenv("LLVM_PROFILE_FILE");
|
||||
if (!Filename || !Filename[0])
|
||||
return -1;
|
||||
|
||||
/* Check the filename for "%p", which indicates a pid-substitution. */
|
||||
static int setFilenamePossiblyWithPid(const char *Filename) {
|
||||
#define MAX_PID_SIZE 16
|
||||
char PidChars[MAX_PID_SIZE] = {0};
|
||||
int NumPids = 0;
|
||||
int PidLength = 0;
|
||||
int I;
|
||||
int NumPids = 0, PidLength = 0;
|
||||
char *Allocated;
|
||||
int I, J;
|
||||
|
||||
/* Reset filename on NULL, except with env var which is checked by caller. */
|
||||
if (!Filename) {
|
||||
resetFilenameToDefault();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check the filename for "%p", which indicates a pid-substitution. */
|
||||
for (I = 0; Filename[I]; ++I)
|
||||
if (Filename[I] == '%' && Filename[++I] == 'p')
|
||||
if (!NumPids++) {
|
||||
@ -123,12 +145,11 @@ static int setFilenameFromEnvironment(void) {
|
||||
}
|
||||
|
||||
/* Allocate enough space for the substituted filename. */
|
||||
char *Allocated = (char*)malloc(I + NumPids*(PidLength - 2) + 1);
|
||||
Allocated = malloc(I + NumPids*(PidLength - 2) + 1);
|
||||
if (!Allocated)
|
||||
return -1;
|
||||
|
||||
/* Construct the new filename. */
|
||||
int J;
|
||||
for (I = 0, J = 0; Filename[I]; ++I)
|
||||
if (Filename[I] == '%') {
|
||||
if (Filename[++I] == 'p') {
|
||||
@ -145,11 +166,20 @@ static int setFilenameFromEnvironment(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setFilenameFromEnvironment(void) {
|
||||
const char *Filename = getenv("LLVM_PROFILE_FILE");
|
||||
|
||||
if (!Filename || !Filename[0])
|
||||
return -1;
|
||||
|
||||
return setFilenamePossiblyWithPid(Filename);
|
||||
}
|
||||
|
||||
static void setFilenameAutomatically(void) {
|
||||
if (!setFilenameFromEnvironment())
|
||||
return;
|
||||
|
||||
setDefaultFilename();
|
||||
resetFilenameToDefault();
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
@ -160,23 +190,32 @@ void __llvm_profile_initialize_file(void) {
|
||||
|
||||
/* Detect the filename and truncate. */
|
||||
setFilenameAutomatically();
|
||||
truncateCurrentFile();
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
void __llvm_profile_set_filename(const char *Filename) {
|
||||
setFilename(Filename, 0);
|
||||
truncateCurrentFile();
|
||||
setFilenamePossiblyWithPid(Filename);
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
void __llvm_profile_override_default_filename(const char *Filename) {
|
||||
/* If the env var is set, skip setting filename from argument. */
|
||||
const char *Env_Filename = getenv("LLVM_PROFILE_FILE");
|
||||
if (Env_Filename && Env_Filename[0])
|
||||
return;
|
||||
setFilenamePossiblyWithPid(Filename);
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
int __llvm_profile_write_file(void) {
|
||||
int rc;
|
||||
|
||||
/* Check the filename. */
|
||||
if (!__llvm_profile_CurrentFilename)
|
||||
return -1;
|
||||
|
||||
/* Write the file. */
|
||||
int rc = writeFileWithName(__llvm_profile_CurrentFilename);
|
||||
rc = writeFileWithName(__llvm_profile_CurrentFilename);
|
||||
if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS"))
|
||||
fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n",
|
||||
__llvm_profile_CurrentFilename, strerror(errno));
|
||||
|
35
contrib/compiler-rt/lib/profile/InstrProfilingUtil.c
Normal file
35
contrib/compiler-rt/lib/profile/InstrProfilingUtil.c
Normal file
@ -0,0 +1,35 @@
|
||||
/*===- InstrProfilingUtil.c - Support library for PGO instrumentation -----===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#include "InstrProfilingUtil.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#elif I386_FREEBSD
|
||||
int mkdir(const char*, unsigned short);
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
void __llvm_profile_recursive_mkdir(char *path) {
|
||||
int i;
|
||||
|
||||
for (i = 1; path[i] != '\0'; ++i) {
|
||||
if (path[i] != '/') continue;
|
||||
path[i] = '\0';
|
||||
#ifdef _WIN32
|
||||
_mkdir(path);
|
||||
#else
|
||||
mkdir(path, 0755); /* Some of these will fail, ignore it. */
|
||||
#endif
|
||||
path[i] = '/';
|
||||
}
|
||||
}
|
16
contrib/compiler-rt/lib/profile/InstrProfilingUtil.h
Normal file
16
contrib/compiler-rt/lib/profile/InstrProfilingUtil.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*===- InstrProfilingUtil.h - Support library for PGO instrumentation -----===*\
|
||||
|*
|
||||
|* The LLVM Compiler Infrastructure
|
||||
|*
|
||||
|* This file is distributed under the University of Illinois Open Source
|
||||
|* License. See LICENSE.TXT for details.
|
||||
|*
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
#ifndef PROFILE_INSTRPROFILINGUTIL_H
|
||||
#define PROFILE_INSTRPROFILINGUTIL_H
|
||||
|
||||
/*! \brief Create a directory tree. */
|
||||
void __llvm_profile_recursive_mkdir(char *Pathname);
|
||||
|
||||
#endif /* PROFILE_INSTRPROFILINGUTIL_H */
|
28
contrib/compiler-rt/lib/safestack/CMakeLists.txt
Normal file
28
contrib/compiler-rt/lib/safestack/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
add_custom_target(safestack)
|
||||
|
||||
set(SAFESTACK_SOURCES safestack.cc)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
set(SAFESTACK_CFLAGS ${SANITIZER_COMMON_CFLAGS})
|
||||
|
||||
if(APPLE)
|
||||
# Build universal binary on APPLE.
|
||||
add_compiler_rt_osx_static_runtime(clang_rt.safestack_osx
|
||||
ARCH ${SAFESTACK_SUPPORTED_ARCH}
|
||||
SOURCES ${SAFESTACK_SOURCES}
|
||||
$<TARGET_OBJECTS:RTInterception.osx>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.osx>
|
||||
CFLAGS ${SAFESTACK_CFLAGS})
|
||||
add_dependencies(safestack clang_rt.safestack_osx)
|
||||
else()
|
||||
# Otherwise, build separate libraries for each target.
|
||||
foreach(arch ${SAFESTACK_SUPPORTED_ARCH})
|
||||
add_compiler_rt_runtime(clang_rt.safestack-${arch} ${arch} STATIC
|
||||
SOURCES ${SAFESTACK_SOURCES}
|
||||
$<TARGET_OBJECTS:RTInterception.${arch}>
|
||||
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
|
||||
CFLAGS ${SAFESTACK_CFLAGS})
|
||||
add_dependencies(safestack clang_rt.safestack-${arch})
|
||||
endforeach()
|
||||
endif()
|
246
contrib/compiler-rt/lib/safestack/safestack.cc
Normal file
246
contrib/compiler-rt/lib/safestack/safestack.cc
Normal file
@ -0,0 +1,246 @@
|
||||
//===-- safestack.cc ------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the runtime support for the safe stack protection
|
||||
// mechanism. The runtime manages allocation/deallocation of the unsafe stack
|
||||
// for the main thread, as well as all pthreads that are created/destroyed
|
||||
// during program execution.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
|
||||
// TODO: The runtime library does not currently protect the safe stack beyond
|
||||
// relying on the system-enforced ASLR. The protection of the (safe) stack can
|
||||
// be provided by three alternative features:
|
||||
//
|
||||
// 1) Protection via hardware segmentation on x86-32 and some x86-64
|
||||
// architectures: the (safe) stack segment (implicitly accessed via the %ss
|
||||
// segment register) can be separated from the data segment (implicitly
|
||||
// accessed via the %ds segment register). Dereferencing a pointer to the safe
|
||||
// segment would result in a segmentation fault.
|
||||
//
|
||||
// 2) Protection via software fault isolation: memory writes that are not meant
|
||||
// to access the safe stack can be prevented from doing so through runtime
|
||||
// instrumentation. One way to do it is to allocate the safe stack(s) in the
|
||||
// upper half of the userspace and bitmask the corresponding upper bit of the
|
||||
// memory addresses of memory writes that are not meant to access the safe
|
||||
// stack.
|
||||
//
|
||||
// 3) Protection via information hiding on 64 bit architectures: the location
|
||||
// of the safe stack(s) can be randomized through secure mechanisms, and the
|
||||
// leakage of the stack pointer can be prevented. Currently, libc can leak the
|
||||
// stack pointer in several ways (e.g. in longjmp, signal handling, user-level
|
||||
// context switching related functions, etc.). These can be fixed in libc and
|
||||
// in other low-level libraries, by either eliminating the escaping/dumping of
|
||||
// the stack pointer (i.e., %rsp) when that's possible, or by using
|
||||
// encryption/PTR_MANGLE (XOR-ing the dumped stack pointer with another secret
|
||||
// we control and protect better, as is already done for setjmp in glibc.)
|
||||
// Furthermore, a static machine code level verifier can be ran after code
|
||||
// generation to make sure that the stack pointer is never written to memory,
|
||||
// or if it is, its written on the safe stack.
|
||||
//
|
||||
// Finally, while the Unsafe Stack pointer is currently stored in a thread
|
||||
// local variable, with libc support it could be stored in the TCB (thread
|
||||
// control block) as well, eliminating another level of indirection and making
|
||||
// such accesses faster. Alternatively, dedicating a separate register for
|
||||
// storing it would also be possible.
|
||||
|
||||
/// Minimum stack alignment for the unsafe stack.
|
||||
const unsigned kStackAlign = 16;
|
||||
|
||||
/// Default size of the unsafe stack. This value is only used if the stack
|
||||
/// size rlimit is set to infinity.
|
||||
const unsigned kDefaultUnsafeStackSize = 0x2800000;
|
||||
|
||||
// TODO: To make accessing the unsafe stack pointer faster, we plan to
|
||||
// eventually store it directly in the thread control block data structure on
|
||||
// platforms where this structure is pointed to by %fs or %gs. This is exactly
|
||||
// the same mechanism as currently being used by the traditional stack
|
||||
// protector pass to store the stack guard (see getStackCookieLocation()
|
||||
// function above). Doing so requires changing the tcbhead_t struct in glibc
|
||||
// on Linux and tcb struct in libc on FreeBSD.
|
||||
//
|
||||
// For now, store it in a thread-local variable.
|
||||
extern "C" {
|
||||
__attribute__((visibility(
|
||||
"default"))) __thread void *__safestack_unsafe_stack_ptr = nullptr;
|
||||
}
|
||||
|
||||
// Per-thread unsafe stack information. It's not frequently accessed, so there
|
||||
// it can be kept out of the tcb in normal thread-local variables.
|
||||
static __thread void *unsafe_stack_start = nullptr;
|
||||
static __thread size_t unsafe_stack_size = 0;
|
||||
static __thread size_t unsafe_stack_guard = 0;
|
||||
|
||||
static inline void *unsafe_stack_alloc(size_t size, size_t guard) {
|
||||
CHECK_GE(size + guard, size);
|
||||
void *addr = MmapOrDie(size + guard, "unsafe_stack_alloc");
|
||||
MprotectNoAccess((uptr)addr, (uptr)guard);
|
||||
return (char *)addr + guard;
|
||||
}
|
||||
|
||||
static inline void unsafe_stack_setup(void *start, size_t size, size_t guard) {
|
||||
CHECK_GE((char *)start + size, (char *)start);
|
||||
CHECK_GE((char *)start + guard, (char *)start);
|
||||
void *stack_ptr = (char *)start + size;
|
||||
CHECK_EQ((((size_t)stack_ptr) & (kStackAlign - 1)), 0);
|
||||
|
||||
__safestack_unsafe_stack_ptr = stack_ptr;
|
||||
unsafe_stack_start = start;
|
||||
unsafe_stack_size = size;
|
||||
unsafe_stack_guard = guard;
|
||||
}
|
||||
|
||||
static void unsafe_stack_free() {
|
||||
if (unsafe_stack_start) {
|
||||
UnmapOrDie((char *)unsafe_stack_start - unsafe_stack_guard,
|
||||
unsafe_stack_size + unsafe_stack_guard);
|
||||
}
|
||||
unsafe_stack_start = nullptr;
|
||||
}
|
||||
|
||||
/// Thread data for the cleanup handler
|
||||
static pthread_key_t thread_cleanup_key;
|
||||
|
||||
/// Safe stack per-thread information passed to the thread_start function
|
||||
struct tinfo {
|
||||
void *(*start_routine)(void *);
|
||||
void *start_routine_arg;
|
||||
|
||||
void *unsafe_stack_start;
|
||||
size_t unsafe_stack_size;
|
||||
size_t unsafe_stack_guard;
|
||||
};
|
||||
|
||||
/// Wrap the thread function in order to deallocate the unsafe stack when the
|
||||
/// thread terminates by returning from its main function.
|
||||
static void *thread_start(void *arg) {
|
||||
struct tinfo *tinfo = (struct tinfo *)arg;
|
||||
|
||||
void *(*start_routine)(void *) = tinfo->start_routine;
|
||||
void *start_routine_arg = tinfo->start_routine_arg;
|
||||
|
||||
// Setup the unsafe stack; this will destroy tinfo content
|
||||
unsafe_stack_setup(tinfo->unsafe_stack_start, tinfo->unsafe_stack_size,
|
||||
tinfo->unsafe_stack_guard);
|
||||
|
||||
// Make sure out thread-specific destructor will be called
|
||||
// FIXME: we can do this only any other specific key is set by
|
||||
// intercepting the pthread_setspecific function itself
|
||||
pthread_setspecific(thread_cleanup_key, (void *)1);
|
||||
|
||||
return start_routine(start_routine_arg);
|
||||
}
|
||||
|
||||
/// Thread-specific data destructor
|
||||
static void thread_cleanup_handler(void *_iter) {
|
||||
// We want to free the unsafe stack only after all other destructors
|
||||
// have already run. We force this function to be called multiple times.
|
||||
// User destructors that might run more then PTHREAD_DESTRUCTOR_ITERATIONS-1
|
||||
// times might still end up executing after the unsafe stack is deallocated.
|
||||
size_t iter = (size_t)_iter;
|
||||
if (iter < PTHREAD_DESTRUCTOR_ITERATIONS) {
|
||||
pthread_setspecific(thread_cleanup_key, (void *)(iter + 1));
|
||||
} else {
|
||||
// This is the last iteration
|
||||
unsafe_stack_free();
|
||||
}
|
||||
}
|
||||
|
||||
/// Intercept thread creation operation to allocate and setup the unsafe stack
|
||||
INTERCEPTOR(int, pthread_create, pthread_t *thread,
|
||||
const pthread_attr_t *attr,
|
||||
void *(*start_routine)(void*), void *arg) {
|
||||
|
||||
size_t size = 0;
|
||||
size_t guard = 0;
|
||||
|
||||
if (attr != NULL) {
|
||||
pthread_attr_getstacksize(attr, &size);
|
||||
pthread_attr_getguardsize(attr, &guard);
|
||||
} else {
|
||||
// get pthread default stack size
|
||||
pthread_attr_t tmpattr;
|
||||
pthread_attr_init(&tmpattr);
|
||||
pthread_attr_getstacksize(&tmpattr, &size);
|
||||
pthread_attr_getguardsize(&tmpattr, &guard);
|
||||
pthread_attr_destroy(&tmpattr);
|
||||
}
|
||||
|
||||
CHECK_NE(size, 0);
|
||||
CHECK_EQ((size & (kStackAlign - 1)), 0);
|
||||
CHECK_EQ((guard & (PAGE_SIZE - 1)), 0);
|
||||
|
||||
void *addr = unsafe_stack_alloc(size, guard);
|
||||
struct tinfo *tinfo =
|
||||
(struct tinfo *)(((char *)addr) + size - sizeof(struct tinfo));
|
||||
tinfo->start_routine = start_routine;
|
||||
tinfo->start_routine_arg = arg;
|
||||
tinfo->unsafe_stack_start = addr;
|
||||
tinfo->unsafe_stack_size = size;
|
||||
tinfo->unsafe_stack_guard = guard;
|
||||
|
||||
return REAL(pthread_create)(thread, attr, thread_start, tinfo);
|
||||
}
|
||||
|
||||
extern "C" __attribute__((visibility("default")))
|
||||
#if !SANITIZER_CAN_USE_PREINIT_ARRAY
|
||||
// On ELF platforms, the constructor is invoked using .preinit_array (see below)
|
||||
__attribute__((constructor(0)))
|
||||
#endif
|
||||
void __safestack_init() {
|
||||
// Determine the stack size for the main thread.
|
||||
size_t size = kDefaultUnsafeStackSize;
|
||||
size_t guard = 4096;
|
||||
|
||||
struct rlimit limit;
|
||||
if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur != RLIM_INFINITY)
|
||||
size = limit.rlim_cur;
|
||||
|
||||
// Allocate unsafe stack for main thread
|
||||
void *addr = unsafe_stack_alloc(size, guard);
|
||||
|
||||
unsafe_stack_setup(addr, size, guard);
|
||||
|
||||
// Initialize pthread interceptors for thread allocation
|
||||
INTERCEPT_FUNCTION(pthread_create);
|
||||
|
||||
// Setup the cleanup handler
|
||||
pthread_key_create(&thread_cleanup_key, thread_cleanup_handler);
|
||||
}
|
||||
|
||||
#if SANITIZER_CAN_USE_PREINIT_ARRAY
|
||||
// On ELF platforms, run safestack initialization before any other constructors.
|
||||
// On other platforms we use the constructor attribute to arrange to run our
|
||||
// initialization early.
|
||||
extern "C" {
|
||||
__attribute__((section(".preinit_array"),
|
||||
used)) void (*__safestack_preinit)(void) = __safestack_init;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
__attribute__((visibility("default"))) void *__get_unsafe_stack_start() {
|
||||
return unsafe_stack_start;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
__attribute__((visibility("default"))) void *__get_unsafe_stack_ptr() {
|
||||
return __safestack_unsafe_stack_ptr;
|
||||
}
|
@ -323,7 +323,7 @@ class SizeClassAllocator64 {
|
||||
|
||||
void Init() {
|
||||
CHECK_EQ(kSpaceBeg,
|
||||
reinterpret_cast<uptr>(Mprotect(kSpaceBeg, kSpaceSize)));
|
||||
reinterpret_cast<uptr>(MmapNoAccess(kSpaceBeg, kSpaceSize)));
|
||||
MapWithCallback(kSpaceEnd, AdditionalSize());
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ struct atomic_uintptr_t {
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
# include "sanitizer_atomic_clang.h"
|
||||
#elif defined(_MSC_VER)
|
||||
# include "sanitizer_atomic_msvc.h"
|
||||
|
@ -21,6 +21,15 @@ extern "C" void _mm_mfence();
|
||||
#pragma intrinsic(_mm_mfence)
|
||||
extern "C" void _mm_pause();
|
||||
#pragma intrinsic(_mm_pause)
|
||||
extern "C" char _InterlockedExchange8( // NOLINT
|
||||
char volatile *Addend, char Value); // NOLINT
|
||||
#pragma intrinsic(_InterlockedExchange8)
|
||||
extern "C" short _InterlockedExchange16( // NOLINT
|
||||
short volatile *Addend, short Value); // NOLINT
|
||||
#pragma intrinsic(_InterlockedExchange16)
|
||||
extern "C" long _InterlockedExchange( // NOLINT
|
||||
long volatile *Addend, long Value); // NOLINT
|
||||
#pragma intrinsic(_InterlockedExchange)
|
||||
extern "C" long _InterlockedExchangeAdd( // NOLINT
|
||||
long volatile * Addend, long Value); // NOLINT
|
||||
#pragma intrinsic(_InterlockedExchangeAdd)
|
||||
@ -145,28 +154,25 @@ INLINE u8 atomic_exchange(volatile atomic_uint8_t *a,
|
||||
u8 v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
__asm {
|
||||
mov eax, a
|
||||
mov cl, v
|
||||
xchg [eax], cl // NOLINT
|
||||
mov v, cl
|
||||
}
|
||||
return v;
|
||||
return (u8)_InterlockedExchange8((volatile char*)&a->val_dont_use, v);
|
||||
}
|
||||
|
||||
INLINE u16 atomic_exchange(volatile atomic_uint16_t *a,
|
||||
u16 v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
__asm {
|
||||
mov eax, a
|
||||
mov cx, v
|
||||
xchg [eax], cx // NOLINT
|
||||
mov v, cx
|
||||
}
|
||||
return v;
|
||||
return (u16)_InterlockedExchange16((volatile short*)&a->val_dont_use, v);
|
||||
}
|
||||
|
||||
INLINE u32 atomic_exchange(volatile atomic_uint32_t *a,
|
||||
u32 v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return (u32)_InterlockedExchange((volatile long*)&a->val_dont_use, v);
|
||||
}
|
||||
|
||||
#ifndef _WIN64
|
||||
|
||||
INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a,
|
||||
u8 *cmp,
|
||||
u8 xchgv,
|
||||
@ -188,6 +194,8 @@ INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a,
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a,
|
||||
uptr *cmp,
|
||||
uptr xchg,
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
#include "sanitizer_stacktrace_printer.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
@ -52,18 +54,23 @@ void ReportFile::ReopenIfNecessary() {
|
||||
if (fd_pid == pid)
|
||||
return;
|
||||
else
|
||||
internal_close(fd);
|
||||
CloseFile(fd);
|
||||
}
|
||||
|
||||
internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
|
||||
uptr openrv = OpenFile(full_path, true);
|
||||
if (internal_iserror(openrv)) {
|
||||
const char *exe_name = GetBinaryBasename();
|
||||
if (common_flags()->log_exe_name && exe_name) {
|
||||
internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
|
||||
exe_name, pid);
|
||||
} else {
|
||||
internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
|
||||
}
|
||||
fd = OpenFile(full_path, WrOnly);
|
||||
if (fd == kInvalidFd) {
|
||||
const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
|
||||
internal_write(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
|
||||
internal_write(kStderrFd, full_path, internal_strlen(full_path));
|
||||
WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
|
||||
WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
|
||||
Die();
|
||||
}
|
||||
fd = openrv;
|
||||
fd_pid = pid;
|
||||
}
|
||||
|
||||
@ -80,7 +87,7 @@ void ReportFile::SetReportPath(const char *path) {
|
||||
|
||||
SpinMutexLock l(mu);
|
||||
if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
|
||||
internal_close(fd);
|
||||
CloseFile(fd);
|
||||
fd = kInvalidFd;
|
||||
if (internal_strcmp(path, "stdout") == 0) {
|
||||
fd = kStdoutFd;
|
||||
@ -134,7 +141,7 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
|
||||
}
|
||||
|
||||
uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
|
||||
uptr max_len, int *errno_p) {
|
||||
uptr max_len, error_t *errno_p) {
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
uptr kMinFileLen = PageSize;
|
||||
uptr read_len = 0;
|
||||
@ -142,9 +149,8 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
|
||||
*buff_size = 0;
|
||||
// The files we usually open are not seekable, so try different buffer sizes.
|
||||
for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
|
||||
uptr openrv = OpenFile(file_name, /*write*/ false);
|
||||
if (internal_iserror(openrv, errno_p)) return 0;
|
||||
fd_t fd = openrv;
|
||||
fd_t fd = OpenFile(file_name, RdOnly, errno_p);
|
||||
if (fd == kInvalidFd) return 0;
|
||||
UnmapOrDie(*buff, *buff_size);
|
||||
*buff = (char*)MmapOrDie(size, __func__);
|
||||
*buff_size = size;
|
||||
@ -152,8 +158,8 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
|
||||
read_len = 0;
|
||||
bool reached_eof = false;
|
||||
while (read_len + PageSize <= size) {
|
||||
uptr just_read = internal_read(fd, *buff + read_len, PageSize);
|
||||
if (internal_iserror(just_read, errno_p)) {
|
||||
uptr just_read;
|
||||
if (!ReadFromFile(fd, *buff + read_len, PageSize, &just_read, errno_p)) {
|
||||
UnmapOrDie(*buff, *buff_size);
|
||||
return 0;
|
||||
}
|
||||
@ -163,7 +169,7 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
|
||||
}
|
||||
read_len += just_read;
|
||||
}
|
||||
internal_close(fd);
|
||||
CloseFile(fd);
|
||||
if (reached_eof) // We've read the whole file.
|
||||
break;
|
||||
}
|
||||
@ -206,19 +212,26 @@ const char *StripPathPrefix(const char *filepath,
|
||||
const char *strip_path_prefix) {
|
||||
if (filepath == 0) return 0;
|
||||
if (strip_path_prefix == 0) return filepath;
|
||||
const char *pos = internal_strstr(filepath, strip_path_prefix);
|
||||
if (pos == 0) return filepath;
|
||||
pos += internal_strlen(strip_path_prefix);
|
||||
if (pos[0] == '.' && pos[1] == '/')
|
||||
pos += 2;
|
||||
return pos;
|
||||
const char *res = filepath;
|
||||
if (const char *pos = internal_strstr(filepath, strip_path_prefix))
|
||||
res = pos + internal_strlen(strip_path_prefix);
|
||||
if (res[0] == '.' && res[1] == '/')
|
||||
res += 2;
|
||||
return res;
|
||||
}
|
||||
|
||||
const char *StripModuleName(const char *module) {
|
||||
if (module == 0)
|
||||
return 0;
|
||||
if (const char *slash_pos = internal_strrchr(module, '/'))
|
||||
if (SANITIZER_WINDOWS) {
|
||||
// On Windows, both slash and backslash are possible.
|
||||
// Pick the one that goes last.
|
||||
if (const char *bslash_pos = internal_strrchr(module, '\\'))
|
||||
return StripModuleName(bslash_pos + 1);
|
||||
}
|
||||
if (const char *slash_pos = internal_strrchr(module, '/')) {
|
||||
return slash_pos + 1;
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
@ -230,26 +243,27 @@ void ReportErrorSummary(const char *error_message) {
|
||||
__sanitizer_report_error_summary(buff.data());
|
||||
}
|
||||
|
||||
void ReportErrorSummary(const char *error_type, const char *file,
|
||||
int line, const char *function) {
|
||||
#ifndef SANITIZER_GO
|
||||
void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
|
||||
if (!common_flags()->print_summary)
|
||||
return;
|
||||
InternalScopedString buff(kMaxSummaryLength);
|
||||
buff.append("%s %s:%d %s", error_type,
|
||||
file ? StripPathPrefix(file, common_flags()->strip_path_prefix)
|
||||
: "??",
|
||||
line, function ? function : "??");
|
||||
buff.append("%s ", error_type);
|
||||
RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
|
||||
common_flags()->strip_path_prefix);
|
||||
ReportErrorSummary(buff.data());
|
||||
}
|
||||
#endif
|
||||
|
||||
LoadedModule::LoadedModule(const char *module_name, uptr base_address) {
|
||||
void LoadedModule::set(const char *module_name, uptr base_address) {
|
||||
clear();
|
||||
full_name_ = internal_strdup(module_name);
|
||||
base_address_ = base_address;
|
||||
ranges_.clear();
|
||||
}
|
||||
|
||||
void LoadedModule::clear() {
|
||||
InternalFree(full_name_);
|
||||
full_name_ = nullptr;
|
||||
while (!ranges_.empty()) {
|
||||
AddressRange *r = ranges_.front();
|
||||
ranges_.pop_front();
|
||||
@ -330,6 +344,32 @@ bool TemplateMatch(const char *templ, const char *str) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char binary_name_cache_str[kMaxPathLength];
|
||||
static const char *binary_basename_cache_str;
|
||||
|
||||
const char *GetBinaryBasename() {
|
||||
return binary_basename_cache_str;
|
||||
}
|
||||
|
||||
// Call once to make sure that binary_name_cache_str is initialized
|
||||
void CacheBinaryName() {
|
||||
if (binary_name_cache_str[0] != '\0')
|
||||
return;
|
||||
ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
|
||||
binary_basename_cache_str = StripModuleName(binary_name_cache_str);
|
||||
}
|
||||
|
||||
uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
|
||||
CacheBinaryName();
|
||||
uptr name_len = internal_strlen(binary_name_cache_str);
|
||||
name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
|
||||
if (buf_len == 0)
|
||||
return 0;
|
||||
internal_memcpy(buf, binary_name_cache_str, name_len);
|
||||
buf[name_len] = '\0';
|
||||
return name_len;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
using namespace __sanitizer; // NOLINT
|
||||
|
@ -23,8 +23,14 @@
|
||||
#include "sanitizer_list.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
extern "C" void _ReadWriteBarrier();
|
||||
#pragma intrinsic(_ReadWriteBarrier)
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
struct StackTrace;
|
||||
struct AddressInfo;
|
||||
|
||||
// Constants.
|
||||
const uptr kWordSize = SANITIZER_WORDSIZE / 8;
|
||||
@ -38,8 +44,15 @@ const uptr kWordSizeInBits = 8 * kWordSize;
|
||||
|
||||
const uptr kMaxPathLength = 4096;
|
||||
|
||||
// 16K loaded modules should be enough for everyone.
|
||||
static const uptr kMaxNumberOfModules = 1 << 14;
|
||||
|
||||
const uptr kMaxThreadStackSize = 1 << 30; // 1Gb
|
||||
|
||||
// Denotes fake PC values that come from JIT/JAVA/etc.
|
||||
// For such PC values __tsan_symbolize_external() will be called.
|
||||
const u64 kExternalPCBit = 1ULL << 60;
|
||||
|
||||
extern const char *SanitizerToolName; // Can be changed by the tool.
|
||||
|
||||
extern atomic_uint32_t current_verbosity;
|
||||
@ -65,12 +78,17 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
||||
// Memory management
|
||||
void *MmapOrDie(uptr size, const char *mem_type);
|
||||
void UnmapOrDie(void *addr, uptr size);
|
||||
void *MmapFixedNoReserve(uptr fixed_addr, uptr size);
|
||||
void *MmapFixedNoReserve(uptr fixed_addr, uptr size,
|
||||
const char *name = nullptr);
|
||||
void *MmapNoReserveOrDie(uptr size, const char *mem_type);
|
||||
void *MmapFixedOrDie(uptr fixed_addr, uptr size);
|
||||
void *Mprotect(uptr fixed_addr, uptr size);
|
||||
void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr);
|
||||
// Map aligned chunk of address space; size and alignment are powers of two.
|
||||
void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type);
|
||||
// Disallow access to a memory range. Use MmapNoAccess to allocate an
|
||||
// unaccessible memory.
|
||||
bool MprotectNoAccess(uptr addr, uptr size);
|
||||
|
||||
// Used to check if we can map shadow memory to a fixed location.
|
||||
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end);
|
||||
void FlushUnneededShadowMemory(uptr addr, uptr size);
|
||||
@ -159,7 +177,7 @@ extern StaticSpinMutex CommonSanitizerReportMutex;
|
||||
|
||||
struct ReportFile {
|
||||
void Write(const char *buffer, uptr length);
|
||||
bool PrintsToTty();
|
||||
bool SupportsColors();
|
||||
void SetReportPath(const char *path);
|
||||
|
||||
// Don't use fields directly. They are only declared public to allow
|
||||
@ -186,18 +204,39 @@ extern ReportFile report_file;
|
||||
extern uptr stoptheworld_tracer_pid;
|
||||
extern uptr stoptheworld_tracer_ppid;
|
||||
|
||||
uptr OpenFile(const char *filename, bool write);
|
||||
enum FileAccessMode {
|
||||
RdOnly,
|
||||
WrOnly,
|
||||
RdWr
|
||||
};
|
||||
|
||||
// Returns kInvalidFd on error.
|
||||
fd_t OpenFile(const char *filename, FileAccessMode mode,
|
||||
error_t *errno_p = nullptr);
|
||||
void CloseFile(fd_t);
|
||||
|
||||
// Return true on success, false on error.
|
||||
bool ReadFromFile(fd_t fd, void *buff, uptr buff_size,
|
||||
uptr *bytes_read = nullptr, error_t *error_p = nullptr);
|
||||
bool WriteToFile(fd_t fd, const void *buff, uptr buff_size,
|
||||
uptr *bytes_written = nullptr, error_t *error_p = nullptr);
|
||||
|
||||
bool RenameFile(const char *oldpath, const char *newpath,
|
||||
error_t *error_p = nullptr);
|
||||
|
||||
bool SupportsColoredOutput(fd_t fd);
|
||||
|
||||
// Opens the file 'file_name" and reads up to 'max_len' bytes.
|
||||
// The resulting buffer is mmaped and stored in '*buff'.
|
||||
// The size of the mmaped region is stored in '*buff_size',
|
||||
// Returns the number of read bytes or 0 if file can not be opened.
|
||||
uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
|
||||
uptr max_len, int *errno_p = nullptr);
|
||||
uptr max_len, error_t *errno_p = nullptr);
|
||||
// Maps given file to virtual memory, and returns pointer to it
|
||||
// (or NULL if the mapping failes). Stores the size of mmaped region
|
||||
// (or NULL if mapping fails). Stores the size of mmaped region
|
||||
// in '*buff_size'.
|
||||
void *MapFileToMemory(const char *file_name, uptr *buff_size);
|
||||
void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset);
|
||||
void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset);
|
||||
|
||||
bool IsAccessibleMemoryRange(uptr beg, uptr size);
|
||||
|
||||
@ -208,6 +247,10 @@ const char *StripPathPrefix(const char *filepath,
|
||||
const char *StripModuleName(const char *module);
|
||||
|
||||
// OS
|
||||
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len);
|
||||
uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len);
|
||||
const char *GetBinaryBasename();
|
||||
void CacheBinaryName();
|
||||
void DisableCoreDumperIfNecessary();
|
||||
void DumpProcessMap();
|
||||
bool FileExists(const char *filename);
|
||||
@ -215,6 +258,9 @@ const char *GetEnv(const char *name);
|
||||
bool SetEnv(const char *name, const char *value);
|
||||
const char *GetPwd();
|
||||
char *FindPathToBinary(const char *name);
|
||||
bool IsPathSeparator(const char c);
|
||||
bool IsAbsolutePath(const char *path);
|
||||
|
||||
u32 GetUid();
|
||||
void ReExec();
|
||||
bool StackSizeIsUnlimited();
|
||||
@ -288,9 +334,9 @@ const int kMaxSummaryLength = 1024;
|
||||
// and pass it to __sanitizer_report_error_summary.
|
||||
void ReportErrorSummary(const char *error_message);
|
||||
// Same as above, but construct error_message as:
|
||||
// error_type file:line function
|
||||
void ReportErrorSummary(const char *error_type, const char *file,
|
||||
int line, const char *function);
|
||||
// error_type file:line[:column][ function]
|
||||
void ReportErrorSummary(const char *error_type, const AddressInfo &info);
|
||||
// Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
|
||||
void ReportErrorSummary(const char *error_type, StackTrace *trace);
|
||||
|
||||
// Math
|
||||
@ -309,7 +355,11 @@ INLINE uptr MostSignificantSetBitIndex(uptr x) {
|
||||
CHECK_NE(x, 0U);
|
||||
unsigned long up; // NOLINT
|
||||
#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
|
||||
# ifdef _WIN64
|
||||
up = SANITIZER_WORDSIZE - 1 - __builtin_clzll(x);
|
||||
# else
|
||||
up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x);
|
||||
# endif
|
||||
#elif defined(_WIN64)
|
||||
_BitScanReverse64(&up, x);
|
||||
#else
|
||||
@ -322,7 +372,11 @@ INLINE uptr LeastSignificantSetBitIndex(uptr x) {
|
||||
CHECK_NE(x, 0U);
|
||||
unsigned long up; // NOLINT
|
||||
#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
|
||||
# ifdef _WIN64
|
||||
up = __builtin_ctzll(x);
|
||||
# else
|
||||
up = __builtin_ctzl(x);
|
||||
# endif
|
||||
#elif defined(_WIN64)
|
||||
_BitScanForward64(&up, x);
|
||||
#else
|
||||
@ -342,7 +396,7 @@ INLINE uptr RoundUpToPowerOfTwo(uptr size) {
|
||||
uptr up = MostSignificantSetBitIndex(size);
|
||||
CHECK(size < (1ULL << (up + 1)));
|
||||
CHECK(size > (1ULL << up));
|
||||
return 1UL << (up + 1);
|
||||
return 1ULL << (up + 1);
|
||||
}
|
||||
|
||||
INLINE uptr RoundUpTo(uptr size, uptr boundary) {
|
||||
@ -360,17 +414,7 @@ INLINE bool IsAligned(uptr a, uptr alignment) {
|
||||
|
||||
INLINE uptr Log2(uptr x) {
|
||||
CHECK(IsPowerOfTwo(x));
|
||||
#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
|
||||
return __builtin_ctzl(x);
|
||||
#elif defined(_WIN64)
|
||||
unsigned long ret; // NOLINT
|
||||
_BitScanForward64(&ret, x);
|
||||
return ret;
|
||||
#else
|
||||
unsigned long ret; // NOLINT
|
||||
_BitScanForward(&ret, x);
|
||||
return ret;
|
||||
#endif
|
||||
return LeastSignificantSetBitIndex(x);
|
||||
}
|
||||
|
||||
// Don't use std::min, std::max or std::swap, to minimize dependency
|
||||
@ -439,11 +483,15 @@ class InternalMmapVectorNoCtor {
|
||||
const T *data() const {
|
||||
return data_;
|
||||
}
|
||||
T *data() {
|
||||
return data_;
|
||||
}
|
||||
uptr capacity() const {
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
void clear() { size_ = 0; }
|
||||
bool empty() const { return size() == 0; }
|
||||
|
||||
private:
|
||||
void Resize(uptr new_capacity) {
|
||||
@ -532,7 +580,8 @@ uptr InternalBinarySearch(const Container &v, uptr first, uptr last,
|
||||
// executable or a shared object).
|
||||
class LoadedModule {
|
||||
public:
|
||||
LoadedModule(const char *module_name, uptr base_address);
|
||||
LoadedModule() : full_name_(nullptr), base_address_(0) { ranges_.clear(); }
|
||||
void set(const char *module_name, uptr base_address);
|
||||
void clear();
|
||||
void addAddressRange(uptr beg, uptr end, bool executable);
|
||||
bool containsAddress(uptr address) const;
|
||||
@ -567,29 +616,42 @@ typedef bool (*string_predicate_t)(const char *);
|
||||
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
|
||||
string_predicate_t filter);
|
||||
|
||||
#if SANITIZER_POSIX
|
||||
const uptr kPthreadDestructorIterations = 4;
|
||||
#else
|
||||
// Unused on Windows.
|
||||
const uptr kPthreadDestructorIterations = 0;
|
||||
#endif
|
||||
|
||||
// Callback type for iterating over a set of memory ranges.
|
||||
typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg);
|
||||
|
||||
enum AndroidApiLevel {
|
||||
ANDROID_NOT_ANDROID = 0,
|
||||
ANDROID_KITKAT = 19,
|
||||
ANDROID_LOLLIPOP_MR1 = 22,
|
||||
ANDROID_POST_LOLLIPOP = 23
|
||||
};
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
// Initialize Android logging. Any writes before this are silently lost.
|
||||
void AndroidLogInit();
|
||||
void AndroidLogWrite(const char *buffer);
|
||||
void GetExtraActivationFlags(char *buf, uptr size);
|
||||
void SanitizerInitializeUnwinder();
|
||||
AndroidApiLevel AndroidGetApiLevel();
|
||||
#else
|
||||
INLINE void AndroidLogInit() {}
|
||||
INLINE void AndroidLogWrite(const char *buffer_unused) {}
|
||||
INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
|
||||
INLINE void SanitizerInitializeUnwinder() {}
|
||||
INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; }
|
||||
#endif
|
||||
|
||||
INLINE uptr GetPthreadDestructorIterations() {
|
||||
#if SANITIZER_ANDROID
|
||||
return (AndroidGetApiLevel() == ANDROID_LOLLIPOP_MR1) ? 8 : 4;
|
||||
#elif SANITIZER_POSIX
|
||||
return 4;
|
||||
#else
|
||||
// Unused on Windows.
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *internal_start_thread(void(*func)(void*), void *arg);
|
||||
void internal_join_thread(void *th);
|
||||
void MaybeStartBackgroudThread();
|
||||
@ -599,14 +661,30 @@ void MaybeStartBackgroudThread();
|
||||
// compiler from recognising it and turning it into an actual call to
|
||||
// memset/memcpy/etc.
|
||||
static inline void SanitizerBreakOptimization(void *arg) {
|
||||
#if _MSC_VER
|
||||
// FIXME: make sure this is actually enough.
|
||||
__asm;
|
||||
#if _MSC_VER && !defined(__clang__)
|
||||
_ReadWriteBarrier();
|
||||
#else
|
||||
__asm__ __volatile__("" : : "r" (arg) : "memory");
|
||||
#endif
|
||||
}
|
||||
|
||||
struct SignalContext {
|
||||
void *context;
|
||||
uptr addr;
|
||||
uptr pc;
|
||||
uptr sp;
|
||||
uptr bp;
|
||||
|
||||
SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) :
|
||||
context(context), addr(addr), pc(pc), sp(sp), bp(bp) {
|
||||
}
|
||||
|
||||
// Creates signal context in a platform-specific manner.
|
||||
static SignalContext Create(void *siginfo, void *context);
|
||||
};
|
||||
|
||||
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
inline void *operator new(__sanitizer::operator_new_size_type size,
|
||||
|
@ -22,6 +22,7 @@
|
||||
// COMMON_INTERCEPTOR_FD_RELEASE
|
||||
// COMMON_INTERCEPTOR_FD_ACCESS
|
||||
// COMMON_INTERCEPTOR_SET_THREAD_NAME
|
||||
// COMMON_INTERCEPTOR_ON_DLOPEN
|
||||
// COMMON_INTERCEPTOR_ON_EXIT
|
||||
// COMMON_INTERCEPTOR_MUTEX_LOCK
|
||||
// COMMON_INTERCEPTOR_MUTEX_UNLOCK
|
||||
@ -46,6 +47,7 @@
|
||||
#define pthread_setname_np pthread_set_name_np
|
||||
#define inet_aton __inet_aton
|
||||
#define inet_pton __inet_pton
|
||||
#define iconv __bsd_iconv
|
||||
#endif
|
||||
|
||||
#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE
|
||||
@ -101,6 +103,21 @@
|
||||
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0)
|
||||
#endif
|
||||
|
||||
#define COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n) \
|
||||
COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \
|
||||
common_flags()->strict_string_checks ? (len) + 1 : (n) )
|
||||
|
||||
#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \
|
||||
COMMON_INTERCEPTOR_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
|
||||
|
||||
#ifndef COMMON_INTERCEPTOR_ON_DLOPEN
|
||||
#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) {}
|
||||
#endif
|
||||
|
||||
#ifndef COMMON_INTERCEPTOR_GET_TLS_RANGE
|
||||
#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) *begin = *end = 0;
|
||||
#endif
|
||||
|
||||
struct FileMetadata {
|
||||
// For open_memstream().
|
||||
char **addr;
|
||||
@ -154,7 +171,8 @@ UNUSED static void DeleteInterceptorMetadata(void *addr) {
|
||||
INTERCEPTOR(char*, textdomain, const char *domainname) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname);
|
||||
char* domain = REAL(textdomain)(domainname);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, domainname, 0);
|
||||
char *domain = REAL(textdomain)(domainname);
|
||||
if (domain) {
|
||||
COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1);
|
||||
}
|
||||
@ -180,8 +198,8 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
|
||||
c2 = (unsigned char)s2[i];
|
||||
if (c1 != c2 || c1 == '\0') break;
|
||||
}
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
|
||||
return CharCmpX(c1, c2);
|
||||
}
|
||||
|
||||
@ -226,8 +244,8 @@ INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) {
|
||||
c2 = (unsigned char)s2[i];
|
||||
if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
|
||||
}
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
|
||||
return CharCaseCmp(c1, c2);
|
||||
}
|
||||
|
||||
@ -253,6 +271,97 @@ INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) {
|
||||
#define INIT_STRNCASECMP
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_STRSTR || SANITIZER_INTERCEPT_STRCASESTR
|
||||
static inline void StrstrCheck(void *ctx, char *r, const char *s1,
|
||||
const char *s2) {
|
||||
uptr len1 = REAL(strlen)(s1);
|
||||
uptr len2 = REAL(strlen)(s2);
|
||||
COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s1, len1,
|
||||
r ? r - s1 + len2 : len1 + 1);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2 + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_STRSTR
|
||||
INTERCEPTOR(char*, strstr, const char *s1, const char *s2) {
|
||||
if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
|
||||
return internal_strstr(s1, s2);
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, strstr, s1, s2);
|
||||
char *r = REAL(strstr)(s1, s2);
|
||||
if (common_flags()->intercept_strstr)
|
||||
StrstrCheck(ctx, r, s1, s2);
|
||||
return r;
|
||||
}
|
||||
|
||||
#define INIT_STRSTR COMMON_INTERCEPT_FUNCTION(strstr);
|
||||
#else
|
||||
#define INIT_STRSTR
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_STRCASESTR
|
||||
INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, strcasestr, s1, s2);
|
||||
char *r = REAL(strcasestr)(s1, s2);
|
||||
if (common_flags()->intercept_strstr)
|
||||
StrstrCheck(ctx, r, s1, s2);
|
||||
return r;
|
||||
}
|
||||
|
||||
#define INIT_STRCASESTR COMMON_INTERCEPT_FUNCTION(strcasestr);
|
||||
#else
|
||||
#define INIT_STRCASESTR
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_STRSPN
|
||||
INTERCEPTOR(SIZE_T, strspn, const char *s1, const char *s2) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, strspn, s1, s2);
|
||||
SIZE_T r = REAL(strspn)(s1, s2);
|
||||
if (common_flags()->intercept_strspn) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
INTERCEPTOR(SIZE_T, strcspn, const char *s1, const char *s2) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, strcspn, s1, s2);
|
||||
SIZE_T r = REAL(strcspn)(s1, s2);
|
||||
if (common_flags()->intercept_strspn) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
#define INIT_STRSPN \
|
||||
COMMON_INTERCEPT_FUNCTION(strspn); \
|
||||
COMMON_INTERCEPT_FUNCTION(strcspn);
|
||||
#else
|
||||
#define INIT_STRSPN
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_STRPBRK
|
||||
INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, strpbrk, s1, s2);
|
||||
char *r = REAL(strpbrk)(s1, s2);
|
||||
if (common_flags()->intercept_strpbrk) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s1,
|
||||
r ? r - s1 + 1 : REAL(strlen)(s1) + 1);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
#define INIT_STRPBRK COMMON_INTERCEPT_FUNCTION(strpbrk);
|
||||
#else
|
||||
#define INIT_STRPBRK
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_MEMCHR
|
||||
INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) {
|
||||
void *ctx;
|
||||
@ -722,12 +831,12 @@ INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) {
|
||||
// its metadata. See
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
|
||||
char *res = REAL(strptime)(s, format, tm);
|
||||
if (res) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0);
|
||||
if (res && tm) {
|
||||
// Do not call unpoison_tm here, because strptime does not, in fact,
|
||||
// initialize the entire struct tm. For example, tm_zone pointer is left
|
||||
// uninitialized.
|
||||
if (tm) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -1029,6 +1138,12 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size,
|
||||
#if SANITIZER_INTERCEPT_IOCTL
|
||||
#include "sanitizer_common_interceptors_ioctl.inc"
|
||||
INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) {
|
||||
// We need a frame pointer, because we call into ioctl_common_[pre|post] which
|
||||
// can trigger a report and we need to be able to unwind through this
|
||||
// function. On Mac in debug mode we might not have a frame pointer, because
|
||||
// ioctl_common_[pre|post] doesn't get inlined here.
|
||||
ENABLE_FRAME_POINTER;
|
||||
|
||||
void *ctx;
|
||||
va_list ap;
|
||||
va_start(ap, request);
|
||||
@ -1502,6 +1617,7 @@ INTERCEPTOR(int, glob, const char *pattern, int flags,
|
||||
__sanitizer_glob_t *pglob) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
|
||||
__sanitizer_glob_t glob_copy = {
|
||||
0, 0, 0,
|
||||
0, wrapped_gl_closedir, wrapped_gl_readdir,
|
||||
@ -1532,6 +1648,7 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags,
|
||||
__sanitizer_glob_t *pglob) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
|
||||
__sanitizer_glob_t glob_copy = {
|
||||
0, 0, 0,
|
||||
0, wrapped_gl_closedir, wrapped_gl_readdir,
|
||||
@ -1678,6 +1795,7 @@ INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) {
|
||||
INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, src, 0);
|
||||
// FIXME: figure out read size based on the address family.
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
@ -2348,6 +2466,37 @@ INTERCEPTOR(char *, get_current_dir_name, int fake) {
|
||||
#define INIT_GET_CURRENT_DIR_NAME
|
||||
#endif
|
||||
|
||||
UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
|
||||
CHECK(endptr);
|
||||
if (nptr == *endptr) {
|
||||
// No digits were found at strtol call, we need to find out the last
|
||||
// symbol accessed by strtoll on our own.
|
||||
// We get this symbol by skipping leading blanks and optional +/- sign.
|
||||
while (IsSpace(*nptr)) nptr++;
|
||||
if (*nptr == '+' || *nptr == '-') nptr++;
|
||||
*endptr = const_cast<char *>(nptr);
|
||||
}
|
||||
CHECK(*endptr >= nptr);
|
||||
}
|
||||
|
||||
UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr,
|
||||
char **endptr, char *real_endptr, int base) {
|
||||
if (endptr != 0) {
|
||||
*endptr = real_endptr;
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
|
||||
}
|
||||
// If base has unsupported value, strtol can exit with EINVAL
|
||||
// without reading any characters. So do additional checks only
|
||||
// if base is valid.
|
||||
bool is_valid_base = (base == 0) || (2 <= base && base <= 36);
|
||||
if (is_valid_base) {
|
||||
FixRealStrtolEndptr(nptr, &real_endptr);
|
||||
}
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, is_valid_base ?
|
||||
(real_endptr - nptr) + 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
#if SANITIZER_INTERCEPT_STRTOIMAX
|
||||
INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
|
||||
void *ctx;
|
||||
@ -2355,8 +2504,9 @@ INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
|
||||
INTMAX_T res = REAL(strtoimax)(nptr, endptr, base);
|
||||
if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
|
||||
char *real_endptr;
|
||||
INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base);
|
||||
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -2366,8 +2516,9 @@ INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
|
||||
INTMAX_T res = REAL(strtoumax)(nptr, endptr, base);
|
||||
if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
|
||||
char *real_endptr;
|
||||
INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base);
|
||||
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -3631,6 +3782,7 @@ INTERCEPTOR(char *, tempnam, char *dir, char *pfx) {
|
||||
INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name);
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
|
||||
COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name);
|
||||
return REAL(pthread_setname_np)(thread, name);
|
||||
}
|
||||
@ -3847,25 +3999,33 @@ INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim,
|
||||
void *stream) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, __getdelim, lineptr, n, delim, stream);
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
|
||||
SSIZE_T res = REAL(__getdelim)(lineptr, n, delim, stream);
|
||||
if (res > 0) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr));
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n));
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1);
|
||||
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt its
|
||||
// metadata. See
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
|
||||
#define GETDELIM_INTERCEPTOR_IMPL(vname) \
|
||||
{ \
|
||||
void *ctx; \
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, vname, lineptr, n, delim, stream); \
|
||||
SSIZE_T res = REAL(vname)(lineptr, n, delim, stream); \
|
||||
if (res > 0) { \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); \
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); \
|
||||
} \
|
||||
return res; \
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim,
|
||||
void *stream)
|
||||
GETDELIM_INTERCEPTOR_IMPL(__getdelim)
|
||||
|
||||
// There's no __getdelim() on FreeBSD so we supply the getdelim() interceptor
|
||||
// with its own body.
|
||||
INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim,
|
||||
void *stream) {
|
||||
return __getdelim(lineptr, n, delim, stream);
|
||||
}
|
||||
void *stream)
|
||||
GETDELIM_INTERCEPTOR_IMPL(getdelim)
|
||||
|
||||
#define INIT_GETLINE \
|
||||
COMMON_INTERCEPT_FUNCTION(getline); \
|
||||
COMMON_INTERCEPT_FUNCTION(__getdelim); \
|
||||
@ -3931,7 +4091,9 @@ INTERCEPTOR(void *, __tls_get_addr, void *arg) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg);
|
||||
void *res = REAL(__tls_get_addr)(arg);
|
||||
DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res);
|
||||
uptr tls_begin, tls_end;
|
||||
COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end);
|
||||
DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end);
|
||||
if (dtv) {
|
||||
// New DTLS block has been allocated.
|
||||
COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size);
|
||||
@ -4688,6 +4850,8 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
|
||||
INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
|
||||
if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
|
||||
COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag);
|
||||
void *res = REAL(dlopen)(filename, flag);
|
||||
COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res);
|
||||
return res;
|
||||
@ -4792,6 +4956,63 @@ INTERCEPTOR(int, munlockall, void) {
|
||||
#define INIT_MLOCKX
|
||||
#endif // SANITIZER_INTERCEPT_MLOCKX
|
||||
|
||||
#if SANITIZER_INTERCEPT_FOPENCOOKIE
|
||||
struct WrappedCookie {
|
||||
void *real_cookie;
|
||||
__sanitizer_cookie_io_functions_t real_io_funcs;
|
||||
};
|
||||
|
||||
static uptr wrapped_read(void *cookie, char *buf, uptr size) {
|
||||
COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
|
||||
WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
|
||||
__sanitizer_cookie_io_read real_read = wrapped_cookie->real_io_funcs.read;
|
||||
return real_read ? real_read(wrapped_cookie->real_cookie, buf, size) : 0;
|
||||
}
|
||||
|
||||
static uptr wrapped_write(void *cookie, const char *buf, uptr size) {
|
||||
COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
|
||||
WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
|
||||
__sanitizer_cookie_io_write real_write = wrapped_cookie->real_io_funcs.write;
|
||||
return real_write ? real_write(wrapped_cookie->real_cookie, buf, size) : size;
|
||||
}
|
||||
|
||||
static int wrapped_seek(void *cookie, u64 *offset, int whence) {
|
||||
COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
|
||||
COMMON_INTERCEPTOR_INITIALIZE_RANGE(offset, sizeof(*offset));
|
||||
WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
|
||||
__sanitizer_cookie_io_seek real_seek = wrapped_cookie->real_io_funcs.seek;
|
||||
return real_seek ? real_seek(wrapped_cookie->real_cookie, offset, whence)
|
||||
: -1;
|
||||
}
|
||||
|
||||
static int wrapped_close(void *cookie) {
|
||||
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
|
||||
WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
|
||||
__sanitizer_cookie_io_close real_close = wrapped_cookie->real_io_funcs.close;
|
||||
int res = real_close ? real_close(wrapped_cookie->real_cookie) : 0;
|
||||
InternalFree(wrapped_cookie);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(__sanitizer_FILE *, fopencookie, void *cookie, const char *mode,
|
||||
__sanitizer_cookie_io_functions_t io_funcs) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, fopencookie, cookie, mode, io_funcs);
|
||||
WrappedCookie *wrapped_cookie =
|
||||
(WrappedCookie *)InternalAlloc(sizeof(WrappedCookie));
|
||||
wrapped_cookie->real_cookie = cookie;
|
||||
wrapped_cookie->real_io_funcs = io_funcs;
|
||||
__sanitizer_FILE *res =
|
||||
REAL(fopencookie)(wrapped_cookie, mode, {wrapped_read, wrapped_write,
|
||||
wrapped_seek, wrapped_close});
|
||||
return res;
|
||||
}
|
||||
|
||||
#define INIT_FOPENCOOKIE COMMON_INTERCEPT_FUNCTION(fopencookie);
|
||||
#else
|
||||
#define INIT_FOPENCOOKIE
|
||||
#endif // SANITIZER_INTERCEPT_FOPENCOOKIE
|
||||
|
||||
static void InitializeCommonInterceptors() {
|
||||
static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
|
||||
interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap();
|
||||
@ -4801,6 +5022,10 @@ static void InitializeCommonInterceptors() {
|
||||
INIT_STRNCMP;
|
||||
INIT_STRCASECMP;
|
||||
INIT_STRNCASECMP;
|
||||
INIT_STRSTR;
|
||||
INIT_STRCASESTR;
|
||||
INIT_STRSPN;
|
||||
INIT_STRPBRK;
|
||||
INIT_MEMCHR;
|
||||
INIT_MEMRCHR;
|
||||
INIT_READ;
|
||||
@ -4955,4 +5180,5 @@ static void InitializeCommonInterceptors() {
|
||||
INIT_GETPASS;
|
||||
INIT_TIMERFD;
|
||||
INIT_MLOCKX;
|
||||
INIT_FOPENCOOKIE;
|
||||
}
|
||||
|
@ -578,14 +578,10 @@ static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d,
|
||||
}
|
||||
if (desc->type != ioctl_desc::CUSTOM)
|
||||
return;
|
||||
switch (request) {
|
||||
case 0x00008912: { // SIOCGIFCONF
|
||||
struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len));
|
||||
break;
|
||||
}
|
||||
if (request == IOCTL_SIOCGIFCONF) {
|
||||
struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d,
|
||||
@ -597,12 +593,8 @@ static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d,
|
||||
}
|
||||
if (desc->type != ioctl_desc::CUSTOM)
|
||||
return;
|
||||
switch (request) {
|
||||
case 0x00008912: { // SIOCGIFCONF
|
||||
struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len);
|
||||
break;
|
||||
}
|
||||
if (request == IOCTL_SIOCGIFCONF) {
|
||||
struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -17,12 +17,16 @@
|
||||
#include "sanitizer_stacktrace.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
|
||||
#if SANITIZER_POSIX
|
||||
#include "sanitizer_posix.h"
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
bool ReportFile::PrintsToTty() {
|
||||
bool ReportFile::SupportsColors() {
|
||||
SpinMutexLock l(mu);
|
||||
ReopenIfNecessary();
|
||||
return internal_isatty(fd) != 0;
|
||||
return SupportsColoredOutput(fd);
|
||||
}
|
||||
|
||||
bool ColorizeReports() {
|
||||
@ -33,7 +37,7 @@ bool ColorizeReports() {
|
||||
|
||||
const char *flag = common_flags()->color;
|
||||
return internal_strcmp(flag, "always") == 0 ||
|
||||
(internal_strcmp(flag, "auto") == 0 && report_file.PrintsToTty());
|
||||
(internal_strcmp(flag, "auto") == 0 && report_file.SupportsColors());
|
||||
}
|
||||
|
||||
static void (*sandboxing_callback)();
|
||||
@ -44,20 +48,16 @@ void SetSandboxingCallback(void (*f)()) {
|
||||
void ReportErrorSummary(const char *error_type, StackTrace *stack) {
|
||||
if (!common_flags()->print_summary)
|
||||
return;
|
||||
#if !SANITIZER_GO
|
||||
if (stack->size > 0 && Symbolizer::GetOrInit()->CanReturnFileLineInfo()) {
|
||||
// Currently, we include the first stack frame into the report summary.
|
||||
// Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
|
||||
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
|
||||
SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
|
||||
const AddressInfo &ai = frame->info;
|
||||
ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
|
||||
frame->ClearAll();
|
||||
if (stack->size == 0) {
|
||||
ReportErrorSummary(error_type);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
AddressInfo ai;
|
||||
ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
|
||||
#endif
|
||||
// Currently, we include the first stack frame into the report summary.
|
||||
// Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
|
||||
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
|
||||
SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
|
||||
ReportErrorSummary(error_type, frame->info);
|
||||
frame->ClearAll();
|
||||
}
|
||||
|
||||
static void (*SoftRssLimitExceededCallback)(bool exceeded);
|
||||
@ -117,12 +117,13 @@ void BackgroundThread(void *arg) {
|
||||
}
|
||||
|
||||
void MaybeStartBackgroudThread() {
|
||||
if (!SANITIZER_LINUX) return; // Need to implement/test on other platforms.
|
||||
#if SANITIZER_LINUX // Need to implement/test on other platforms.
|
||||
// Start the background thread if one of the rss limits is given.
|
||||
if (!common_flags()->hard_rss_limit_mb &&
|
||||
!common_flags()->soft_rss_limit_mb) return;
|
||||
if (!&real_pthread_create) return; // Can't spawn the thread anyway.
|
||||
internal_start_thread(BackgroundThread, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user