Index: projects/clang360-import/contrib/compiler-rt/include/sanitizer/asan_interface.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/include/sanitizer/asan_interface.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/include/sanitizer/asan_interface.h (revision 279191) @@ -1,156 +1,155 @@ //===-- sanitizer/asan_interface.h ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer. // // Public interface header. //===----------------------------------------------------------------------===// #ifndef SANITIZER_ASAN_INTERFACE_H #define SANITIZER_ASAN_INTERFACE_H #include #ifdef __cplusplus extern "C" { #endif // Marks memory region [addr, addr+size) as unaddressable. // This memory must be previously allocated by the user program. Accessing // addresses in this region from instrumented code is forbidden until // this region is unpoisoned. This function is not guaranteed to poison // the whole region - it may poison only subregion of [addr, addr+size) due // to ASan alignment restrictions. // Method is NOT thread-safe in the sense that no two threads can // (un)poison memory in the same memory region simultaneously. void __asan_poison_memory_region(void const volatile *addr, size_t size); // Marks memory region [addr, addr+size) as addressable. // This memory must be previously allocated by the user program. Accessing // addresses in this region is allowed until this region is poisoned again. // This function may unpoison a superregion of [addr, addr+size) due to // ASan alignment restrictions. // Method is NOT thread-safe in the sense that no two threads can // (un)poison memory in the same memory region simultaneously. void __asan_unpoison_memory_region(void const volatile *addr, size_t size); // User code should use macros instead of functions. #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) #define ASAN_POISON_MEMORY_REGION(addr, size) \ __asan_poison_memory_region((addr), (size)) #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ __asan_unpoison_memory_region((addr), (size)) #else #define ASAN_POISON_MEMORY_REGION(addr, size) \ ((void)(addr), (void)(size)) #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ ((void)(addr), (void)(size)) #endif // Returns 1 if addr is poisoned (i.e. 1-byte read/write access to this // address will result in error report from AddressSanitizer). // Otherwise returns 0. int __asan_address_is_poisoned(void const volatile *addr); // If at least one byte in [beg, beg+size) is poisoned, return the address // of the first such byte. Otherwise return 0. void *__asan_region_is_poisoned(void *beg, size_t size); // Print the description of addr (useful when debugging in gdb). void __asan_describe_address(void *addr); // Useful for calling from a debugger to get information about an ASan error. // Returns 1 if an error has been (or is being) reported, otherwise returns 0. int __asan_report_present(); // Useful for calling from a debugger to get information about an ASan error. // If an error has been (or is being) reported, the following functions return // the pc, bp, sp, address, access type (0 = read, 1 = write), access size and // bug description (e.g. "heap-use-after-free"). Otherwise they return 0. void *__asan_get_report_pc(); void *__asan_get_report_bp(); void *__asan_get_report_sp(); void *__asan_get_report_address(); int __asan_get_report_access_type(); size_t __asan_get_report_access_size(); const char *__asan_get_report_description(); // Useful for calling from the debugger to get information about a pointer. // Returns the category of the given pointer as a constant string. // Possible return values are "global", "stack", "stack-fake", "heap", // "heap-invalid", "shadow-low", "shadow-gap", "shadow-high", "unknown". // If global or stack, tries to also return the variable name, address and // size. If heap, tries to return the chunk address and size. 'name' should // point to an allocated buffer of size 'name_size'. const char *__asan_locate_address(void *addr, char *name, size_t name_size, void **region_address, size_t *region_size); // Useful for calling from the debugger to get the allocation stack trace // and thread ID for a heap address. Stores up to 'size' frames into 'trace', // returns the number of stored frames or 0 on error. size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size, int *thread_id); // Useful for calling from the debugger to get the free stack trace // and thread ID for a heap address. Stores up to 'size' frames into 'trace', // returns the number of stored frames or 0 on error. size_t __asan_get_free_stack(void *addr, void **trace, size_t size, int *thread_id); // Useful for calling from the debugger to get the current shadow memory // mapping. void __asan_get_shadow_mapping(size_t *shadow_scale, size_t *shadow_offset); // This is an internal function that is called to report an error. // However it is still a part of the interface because users may want to // set a breakpoint on this function in a debugger. void __asan_report_error(void *pc, void *bp, void *sp, void *addr, int is_write, size_t access_size); // Sets the exit code to use when reporting an error. // Returns the old value. int __asan_set_error_exit_code(int exit_code); - // Sets the callback to be called right before death on error. - // Passing 0 will unset the callback. + // Deprecated. Call __sanitizer_set_death_callback instead. void __asan_set_death_callback(void (*callback)(void)); void __asan_set_error_report_callback(void (*callback)(const char*)); // User may provide function that would be called right when ASan detects // an error. This can be used to notice cases when ASan detects an error, but // the program crashes before ASan report is printed. void __asan_on_error(); // Prints accumulated stats to stderr. Used for debugging. void __asan_print_accumulated_stats(); // This function may be optionally provided by user and should return // a string containing ASan runtime options. See asan_flags.h for details. const char* __asan_default_options(); // The following 2 functions facilitate garbage collection in presence of // asan's fake stack. // Returns an opaque handler to be used later in __asan_addr_is_in_fake_stack. // Returns NULL if the current thread does not have a fake stack. void *__asan_get_current_fake_stack(); // If fake_stack is non-NULL and addr belongs to a fake frame in // fake_stack, returns the address on real stack that corresponds to // the fake frame and sets beg/end to the boundaries of this fake frame. // Otherwise returns NULL and does not touch beg/end. // If beg/end are NULL, they are not touched. // This function may be called from a thread other than the owner of // fake_stack, but the owner thread need to be alive. void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, void **end); #ifdef __cplusplus } // extern "C" #endif #endif // SANITIZER_ASAN_INTERFACE_H Index: projects/clang360-import/contrib/compiler-rt/include/sanitizer/common_interface_defs.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/include/sanitizer/common_interface_defs.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/include/sanitizer/common_interface_defs.h (revision 279191) @@ -1,127 +1,118 @@ //===-- sanitizer/common_interface_defs.h -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Common part of the public sanitizer interface. //===----------------------------------------------------------------------===// #ifndef SANITIZER_COMMON_INTERFACE_DEFS_H #define SANITIZER_COMMON_INTERFACE_DEFS_H #include #include // GCC does not understand __has_feature. #if !defined(__has_feature) # define __has_feature(x) 0 #endif #ifdef __cplusplus extern "C" { #endif // Arguments for __sanitizer_sandbox_on_notify() below. typedef struct { // Enable sandbox support in sanitizer coverage. int coverage_sandboxed; // File descriptor to write coverage data to. If -1 is passed, a file will // be pre-opened by __sanitizer_sandobx_on_notify(). This field has no // effect if coverage_sandboxed == 0. intptr_t coverage_fd; // If non-zero, split the coverage data into well-formed blocks. This is // useful when coverage_fd is a socket descriptor. Each block will contain // a header, allowing data from multiple processes to be sent over the same // socket. unsigned int coverage_max_block_size; } __sanitizer_sandbox_arguments; // Tell the tools to write their reports to "path." instead of stderr. void __sanitizer_set_report_path(const char *path); // Notify the tools that the sandbox is going to be turned on. The reserved // parameter will be used in the future to hold a structure with functions // that the tools may call to bypass the sandbox. void __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args); // This function is called by the tool when it has just finished reporting // an error. 'error_summary' is a one-line string that summarizes // the error message. This function can be overridden by the client. void __sanitizer_report_error_summary(const char *error_summary); // Some of the sanitizers (e.g. asan/tsan) may miss bugs that happen // in unaligned loads/stores. In order to find such bugs reliably one needs // to replace plain unaligned loads/stores with these calls. uint16_t __sanitizer_unaligned_load16(const void *p); uint32_t __sanitizer_unaligned_load32(const void *p); uint64_t __sanitizer_unaligned_load64(const void *p); void __sanitizer_unaligned_store16(void *p, uint16_t x); void __sanitizer_unaligned_store32(void *p, uint32_t x); void __sanitizer_unaligned_store64(void *p, uint64_t x); - // Initialize coverage. - void __sanitizer_cov_init(); - // Record and dump coverage info. - void __sanitizer_cov_dump(); - // Open .sancov.packed in the coverage directory and return the file - // descriptor. Returns -1 on failure, or if coverage dumping is disabled. - // This is intended for use by sandboxing code. - intptr_t __sanitizer_maybe_open_cov_file(const char *name); - // Get the number of total unique covered entities (blocks, edges, calls). - // This can be useful for coverage-directed in-process fuzzers. - uintptr_t __sanitizer_get_total_unique_coverage(); - // Annotate the current state of a contiguous container, such as // std::vector, std::string or similar. // A contiguous container is a container that keeps all of its elements // in a contiguous region of memory. The container owns the region of memory // [beg, end); the memory [beg, mid) is used to store the current elements // and the memory [mid, end) is reserved for future elements; // beg <= mid <= end. For example, in "std::vector<> v" // beg = &v[0]; // end = beg + v.capacity() * sizeof(v[0]); // mid = beg + v.size() * sizeof(v[0]); // // This annotation tells the Sanitizer tool about the current state of the // container so that the tool can report errors when memory from [mid, end) // is accessed. Insert this annotation into methods like push_back/pop_back. // Supply the old and the new values of mid (old_mid/new_mid). // In the initial state mid == end and so should be the final // state when the container is destroyed or when it reallocates the storage. // // Use with caution and don't use for anything other than vector-like classes. // // For AddressSanitizer, 'beg' should be 8-aligned and 'end' should // be either 8-aligned or it should point to the end of a separate heap-, // stack-, or global- allocated buffer. I.e. the following will not work: // int64_t x[2]; // 16 bytes, 8-aligned. // char *beg = (char *)&x[0]; // char *end = beg + 12; // Not 8 aligned, not the end of the buffer. // This however will work fine: // int32_t x[3]; // 12 bytes, but 8-aligned under AddressSanitizer. // char *beg = (char*)&x[0]; // char *end = beg + 12; // Not 8-aligned, but is the end of the buffer. void __sanitizer_annotate_contiguous_container(const void *beg, const void *end, const void *old_mid, const void *new_mid); // Returns true if the contiguous container [beg, end) is properly poisoned // (e.g. with __sanitizer_annotate_contiguous_container), i.e. if // - [beg, mid) is addressable, // - [mid, end) is unaddressable. // Full verification requires O(end-beg) time; this function tries to avoid // such complexity by touching only parts of the container around beg/mid/end. int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, const void *end); // Print the stack trace leading to this call. Useful for debugging user code. void __sanitizer_print_stack_trace(); + // Sets the callback to be called right before death on error. + // Passing 0 will unset the callback. + void __sanitizer_set_death_callback(void (*callback)(void)); #ifdef __cplusplus } // extern "C" #endif #endif // SANITIZER_COMMON_INTERFACE_DEFS_H Index: projects/clang360-import/contrib/compiler-rt/include/sanitizer/coverage_interface.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/include/sanitizer/coverage_interface.h (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/include/sanitizer/coverage_interface.h (revision 279191) @@ -0,0 +1,46 @@ +//===-- sanitizer/coverage_interface.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Public interface for sanitizer coverage. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_COVERAG_INTERFACE_H +#define SANITIZER_COVERAG_INTERFACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + // Initialize coverage. + void __sanitizer_cov_init(); + // Record and dump coverage info. + void __sanitizer_cov_dump(); + // Open .sancov.packed in the coverage directory and return the file + // descriptor. Returns -1 on failure, or if coverage dumping is disabled. + // This is intended for use by sandboxing code. + intptr_t __sanitizer_maybe_open_cov_file(const char *name); + // Get the number of total unique covered entities (blocks, edges, calls). + // This can be useful for coverage-directed in-process fuzzers. + uintptr_t __sanitizer_get_total_unique_coverage(); + + // Reset the basic-block (edge) coverage to the initial state. + // Useful for in-process fuzzing to start collecting coverage from scratch. + // Experimental, will likely not work for multi-threaded process. + void __sanitizer_reset_coverage(); + // Set *data to the array of covered PCs and return the size of that array. + // Some of the entries in *data will be zero. + uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_COVERAG_INTERFACE_H Property changes on: projects/clang360-import/contrib/compiler-rt/include/sanitizer/coverage_interface.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/include/sanitizer/msan_interface.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/include/sanitizer/msan_interface.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/include/sanitizer/msan_interface.h (revision 279191) @@ -1,100 +1,102 @@ //===-- msan_interface.h --------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // Public interface header. //===----------------------------------------------------------------------===// #ifndef MSAN_INTERFACE_H #define MSAN_INTERFACE_H #include #ifdef __cplusplus extern "C" { #endif /* Set raw origin for the memory range. */ void __msan_set_origin(const volatile void *a, size_t size, uint32_t origin); /* Get raw origin for an address. */ uint32_t __msan_get_origin(const volatile void *a); /* Returns non-zero if tracking origins. */ int __msan_get_track_origins(); /* Returns the origin id of the latest UMR in the calling thread. */ uint32_t __msan_get_umr_origin(); /* Make memory region fully initialized (without changing its contents). */ void __msan_unpoison(const volatile void *a, size_t size); /* Make a null-terminated string fully initialized (without changing its contents). */ void __msan_unpoison_string(const volatile char *a); - /* Make memory region fully uninitialized (without changing its contents). */ + /* Make memory region fully uninitialized (without changing its contents). + This is a legacy interface that does not update origin information. Use + __msan_allocated_memory() instead. */ void __msan_poison(const volatile void *a, size_t size); /* Make memory region partially uninitialized (without changing its contents). */ void __msan_partial_poison(const volatile void *data, void *shadow, size_t size); /* Returns the offset of the first (at least partially) poisoned byte in the memory range, or -1 if the whole range is good. */ intptr_t __msan_test_shadow(const volatile void *x, size_t size); /* Checks that memory range is fully initialized, and reports an error if it * is not. */ void __msan_check_mem_is_initialized(const volatile void *x, size_t size); /* Set exit code when error(s) were detected. Value of 0 means don't change the program exit code. */ void __msan_set_exit_code(int exit_code); /* For testing: __msan_set_expect_umr(1); ... some buggy code ... __msan_set_expect_umr(0); The last line will verify that a UMR happened. */ void __msan_set_expect_umr(int expect_umr); /* Change the value of keep_going flag. Non-zero value means don't terminate program execution when an error is detected. This will not affect error in modules that were compiled without the corresponding compiler flag. */ void __msan_set_keep_going(int keep_going); /* Print shadow and origin for the memory range to stderr in a human-readable format. */ void __msan_print_shadow(const volatile void *x, size_t size); /* Print shadow for the memory range to stderr in a minimalistic human-readable format. */ void __msan_dump_shadow(const volatile void *x, size_t size); /* Returns true if running under a dynamic tool (DynamoRio-based). */ int __msan_has_dynamic_component(); /* Tell MSan about newly allocated memory (ex.: custom allocator). Memory will be marked uninitialized, with origin at the call site. */ void __msan_allocated_memory(const volatile void* data, size_t size); /* This function may be optionally provided by user and should return a string containing Msan runtime options. See msan_flags.h for details. */ const char* __msan_default_options(); /* Sets the callback to be called right before death on error. Passing 0 will unset the callback. */ void __msan_set_death_callback(void (*callback)(void)); #ifdef __cplusplus } // extern "C" #endif #endif Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_allocator2.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_allocator2.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_allocator2.cc (nonexistent) @@ -1,792 +0,0 @@ -//===-- asan_allocator2.cc ------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of AddressSanitizer, an address sanity checker. -// -// Implementation of ASan's memory allocator, 2-nd version. -// This variant uses the allocator from sanitizer_common, i.e. the one shared -// with ThreadSanitizer and MemorySanitizer. -// -//===----------------------------------------------------------------------===// -#include "asan_allocator.h" - -#include "asan_mapping.h" -#include "asan_poisoning.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_allocator_interface.h" -#include "sanitizer_common/sanitizer_flags.h" -#include "sanitizer_common/sanitizer_internal_defs.h" -#include "sanitizer_common/sanitizer_list.h" -#include "sanitizer_common/sanitizer_stackdepot.h" -#include "sanitizer_common/sanitizer_quarantine.h" -#include "lsan/lsan_common.h" - -namespace __asan { - -void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const { - PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); - // Statistics. - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.mmaps++; - thread_stats.mmaped += size; -} -void AsanMapUnmapCallback::OnUnmap(uptr p, uptr size) const { - PoisonShadow(p, size, 0); - // We are about to unmap a chunk of user memory. - // Mark the corresponding shadow memory as not needed. - FlushUnneededASanShadowMemory(p, size); - // Statistics. - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.munmaps++; - thread_stats.munmaped += size; -} - -// We can not use THREADLOCAL because it is not supported on some of the -// platforms we care about (OSX 10.6, Android). -// static THREADLOCAL AllocatorCache cache; -AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) { - CHECK(ms); - return &ms->allocator2_cache; -} - -static Allocator allocator; - -static const uptr kMaxAllowedMallocSize = - FIRST_32_SECOND_64(3UL << 30, 64UL << 30); - -static const uptr kMaxThreadLocalQuarantine = - FIRST_32_SECOND_64(1 << 18, 1 << 20); - -// Every chunk of memory allocated by this allocator can be in one of 3 states: -// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. -// CHUNK_ALLOCATED: the chunk is allocated and not yet freed. -// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone. -enum { - CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it. - CHUNK_ALLOCATED = 2, - CHUNK_QUARANTINE = 3 -}; - -// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits. -// We use adaptive redzones: for larger allocation larger redzones are used. -static u32 RZLog2Size(u32 rz_log) { - CHECK_LT(rz_log, 8); - return 16 << rz_log; -} - -static u32 RZSize2Log(u32 rz_size) { - CHECK_GE(rz_size, 16); - CHECK_LE(rz_size, 2048); - CHECK(IsPowerOfTwo(rz_size)); - u32 res = Log2(rz_size) - 4; - CHECK_EQ(rz_size, RZLog2Size(res)); - return res; -} - -static uptr ComputeRZLog(uptr user_requested_size) { - u32 rz_log = - user_requested_size <= 64 - 16 ? 0 : - user_requested_size <= 128 - 32 ? 1 : - user_requested_size <= 512 - 64 ? 2 : - user_requested_size <= 4096 - 128 ? 3 : - user_requested_size <= (1 << 14) - 256 ? 4 : - user_requested_size <= (1 << 15) - 512 ? 5 : - user_requested_size <= (1 << 16) - 1024 ? 6 : 7; - return Min(Max(rz_log, RZSize2Log(flags()->redzone)), - RZSize2Log(flags()->max_redzone)); -} - -// The memory chunk allocated from the underlying allocator looks like this: -// L L L L L L H H U U U U U U R R -// L -- left redzone words (0 or more bytes) -// H -- ChunkHeader (16 bytes), which is also a part of the left redzone. -// U -- user memory. -// R -- right redzone (0 or more bytes) -// ChunkBase consists of ChunkHeader and other bytes that overlap with user -// memory. - -// If the left redzone is greater than the ChunkHeader size we store a magic -// value in the first uptr word of the memory block and store the address of -// ChunkBase in the next uptr. -// M B L L L L L L L L L H H U U U U U U -// | ^ -// ---------------------| -// M -- magic value kAllocBegMagic -// B -- address of ChunkHeader pointing to the first 'H' -static const uptr kAllocBegMagic = 0xCC6E96B9; - -struct ChunkHeader { - // 1-st 8 bytes. - u32 chunk_state : 8; // Must be first. - u32 alloc_tid : 24; - - u32 free_tid : 24; - u32 from_memalign : 1; - u32 alloc_type : 2; - u32 rz_log : 3; - u32 lsan_tag : 2; - // 2-nd 8 bytes - // This field is used for small sizes. For large sizes it is equal to - // SizeClassMap::kMaxSize and the actual size is stored in the - // SecondaryAllocator's metadata. - u32 user_requested_size; - u32 alloc_context_id; -}; - -struct ChunkBase : ChunkHeader { - // Header2, intersects with user memory. - u32 free_context_id; -}; - -static const uptr kChunkHeaderSize = sizeof(ChunkHeader); -static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize; -COMPILER_CHECK(kChunkHeaderSize == 16); -COMPILER_CHECK(kChunkHeader2Size <= 16); - -struct AsanChunk: ChunkBase { - uptr Beg() { return reinterpret_cast(this) + kChunkHeaderSize; } - uptr UsedSize(bool locked_version = false) { - if (user_requested_size != SizeClassMap::kMaxSize) - return user_requested_size; - return *reinterpret_cast( - allocator.GetMetaData(AllocBeg(locked_version))); - } - void *AllocBeg(bool locked_version = false) { - if (from_memalign) { - if (locked_version) - return allocator.GetBlockBeginFastLocked( - reinterpret_cast(this)); - return allocator.GetBlockBegin(reinterpret_cast(this)); - } - return reinterpret_cast(Beg() - RZLog2Size(rz_log)); - } - bool AddrIsInside(uptr addr, bool locked_version = false) { - return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); - } -}; - -bool AsanChunkView::IsValid() { - return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE; -} -uptr AsanChunkView::Beg() { return chunk_->Beg(); } -uptr AsanChunkView::End() { return Beg() + UsedSize(); } -uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); } -uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; } -uptr AsanChunkView::FreeTid() { return chunk_->free_tid; } - -static StackTrace GetStackTraceFromId(u32 id) { - CHECK(id); - StackTrace res = StackDepotGet(id); - CHECK(res.trace); - return res; -} - -StackTrace AsanChunkView::GetAllocStack() { - return GetStackTraceFromId(chunk_->alloc_context_id); -} - -StackTrace AsanChunkView::GetFreeStack() { - return GetStackTraceFromId(chunk_->free_context_id); -} - -struct QuarantineCallback; -typedef Quarantine AsanQuarantine; -typedef AsanQuarantine::Cache QuarantineCache; -static AsanQuarantine quarantine(LINKER_INITIALIZED); -static QuarantineCache fallback_quarantine_cache(LINKER_INITIALIZED); -static AllocatorCache fallback_allocator_cache; -static SpinMutex fallback_mutex; - -QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) { - CHECK(ms); - CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache)); - return reinterpret_cast(ms->quarantine_cache); -} - -struct QuarantineCallback { - explicit QuarantineCallback(AllocatorCache *cache) - : cache_(cache) { - } - - void Recycle(AsanChunk *m) { - CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); - atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed); - CHECK_NE(m->alloc_tid, kInvalidTid); - CHECK_NE(m->free_tid, kInvalidTid); - PoisonShadow(m->Beg(), - RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), - kAsanHeapLeftRedzoneMagic); - void *p = reinterpret_cast(m->AllocBeg()); - if (p != m) { - uptr *alloc_magic = reinterpret_cast(p); - CHECK_EQ(alloc_magic[0], kAllocBegMagic); - // Clear the magic value, as allocator internals may overwrite the - // contents of deallocated chunk, confusing GetAsanChunk lookup. - alloc_magic[0] = 0; - CHECK_EQ(alloc_magic[1], reinterpret_cast(m)); - } - - // Statistics. - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.real_frees++; - thread_stats.really_freed += m->UsedSize(); - - allocator.Deallocate(cache_, p); - } - - void *Allocate(uptr size) { - return allocator.Allocate(cache_, size, 1, false); - } - - void Deallocate(void *p) { - allocator.Deallocate(cache_, p); - } - - AllocatorCache *cache_; -}; - -void InitializeAllocator() { - allocator.Init(); - quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine); -} - -void ReInitializeAllocator() { - quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine); -} - -static void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack, - AllocType alloc_type, bool can_fill) { - if (UNLIKELY(!asan_inited)) - AsanInitFromRtl(); - Flags &fl = *flags(); - CHECK(stack); - const uptr min_alignment = SHADOW_GRANULARITY; - if (alignment < min_alignment) - alignment = min_alignment; - if (size == 0) { - // We'd be happy to avoid allocating memory for zero-size requests, but - // some programs/tests depend on this behavior and assume that malloc would - // not return NULL even for zero-size allocations. Moreover, it looks like - // operator new should never return NULL, and results of consecutive "new" - // calls must be different even if the allocated size is zero. - size = 1; - } - CHECK(IsPowerOfTwo(alignment)); - uptr rz_log = ComputeRZLog(size); - uptr rz_size = RZLog2Size(rz_log); - uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); - uptr needed_size = rounded_size + rz_size; - if (alignment > min_alignment) - needed_size += alignment; - bool using_primary_allocator = true; - // If we are allocating from the secondary allocator, there will be no - // automatic right redzone, so add the right redzone manually. - if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) { - needed_size += rz_size; - using_primary_allocator = false; - } - CHECK(IsAligned(needed_size, min_alignment)); - if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { - Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", - (void*)size); - return AllocatorReturnNull(); - } - - AsanThread *t = GetCurrentThread(); - void *allocated; - if (t) { - AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); - allocated = allocator.Allocate(cache, needed_size, 8, false); - } else { - SpinMutexLock l(&fallback_mutex); - AllocatorCache *cache = &fallback_allocator_cache; - allocated = allocator.Allocate(cache, needed_size, 8, false); - } - - if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && flags()->poison_heap) { - // Heap poisoning is enabled, but the allocator provides an unpoisoned - // chunk. This is possible if flags()->poison_heap was disabled for some - // time, for example, due to flags()->start_disabled. - // Anyway, poison the block before using it for anything else. - uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated); - PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic); - } - - uptr alloc_beg = reinterpret_cast(allocated); - uptr alloc_end = alloc_beg + needed_size; - uptr beg_plus_redzone = alloc_beg + rz_size; - uptr user_beg = beg_plus_redzone; - if (!IsAligned(user_beg, alignment)) - user_beg = RoundUpTo(user_beg, alignment); - uptr user_end = user_beg + size; - CHECK_LE(user_end, alloc_end); - uptr chunk_beg = user_beg - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast(chunk_beg); - m->alloc_type = alloc_type; - m->rz_log = rz_log; - u32 alloc_tid = t ? t->tid() : 0; - m->alloc_tid = alloc_tid; - CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield? - m->free_tid = kInvalidTid; - m->from_memalign = user_beg != beg_plus_redzone; - if (alloc_beg != chunk_beg) { - CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); - reinterpret_cast(alloc_beg)[0] = kAllocBegMagic; - reinterpret_cast(alloc_beg)[1] = chunk_beg; - } - if (using_primary_allocator) { - CHECK(size); - m->user_requested_size = size; - CHECK(allocator.FromPrimary(allocated)); - } else { - CHECK(!allocator.FromPrimary(allocated)); - m->user_requested_size = SizeClassMap::kMaxSize; - uptr *meta = reinterpret_cast(allocator.GetMetaData(allocated)); - meta[0] = size; - meta[1] = chunk_beg; - } - - m->alloc_context_id = StackDepotPut(*stack); - - uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY); - // Unpoison the bulk of the memory region. - if (size_rounded_down_to_granularity) - PoisonShadow(user_beg, size_rounded_down_to_granularity, 0); - // Deal with the end of the region if size is not aligned to granularity. - if (size != size_rounded_down_to_granularity && fl.poison_heap) { - u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity); - *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; - } - - AsanStats &thread_stats = GetCurrentThreadStats(); - 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++; - - void *res = reinterpret_cast(user_beg); - if (can_fill && fl.max_malloc_fill_size) { - uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); - REAL(memset)(res, fl.malloc_fill_byte, fill_size); - } -#if CAN_SANITIZE_LEAKS - m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored - : __lsan::kDirectlyLeaked; -#endif - // Must be the last mutation of metadata in this function. - atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); - ASAN_MALLOC_HOOK(res, size); - return res; -} - -static void ReportInvalidFree(void *ptr, u8 chunk_state, - BufferedStackTrace *stack) { - if (chunk_state == CHUNK_QUARANTINE) - ReportDoubleFree((uptr)ptr, stack); - else - ReportFreeNotMalloced((uptr)ptr, stack); -} - -static void AtomicallySetQuarantineFlag(AsanChunk *m, void *ptr, - BufferedStackTrace *stack) { - u8 old_chunk_state = CHUNK_ALLOCATED; - // Flip the chunk_state atomically to avoid race on double-free. - if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state, - CHUNK_QUARANTINE, memory_order_acquire)) - ReportInvalidFree(ptr, old_chunk_state, stack); - CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); -} - -// Expects the chunk to already be marked as quarantined by using -// AtomicallySetQuarantineFlag. -static void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack, - AllocType alloc_type) { - CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); - - if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch) - ReportAllocTypeMismatch((uptr)ptr, stack, - (AllocType)m->alloc_type, (AllocType)alloc_type); - - CHECK_GE(m->alloc_tid, 0); - if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area. - CHECK_EQ(m->free_tid, kInvalidTid); - AsanThread *t = GetCurrentThread(); - m->free_tid = t ? t->tid() : 0; - m->free_context_id = StackDepotPut(*stack); - // Poison the region. - PoisonShadow(m->Beg(), - RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), - kAsanHeapFreeMagic); - - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.frees++; - thread_stats.freed += m->UsedSize(); - - // Push into quarantine. - if (t) { - AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); - AllocatorCache *ac = GetAllocatorCache(ms); - quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac), - m, m->UsedSize()); - } else { - SpinMutexLock l(&fallback_mutex); - AllocatorCache *ac = &fallback_allocator_cache; - quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac), - m, m->UsedSize()); - } -} - -static void Deallocate(void *ptr, uptr delete_size, BufferedStackTrace *stack, - AllocType alloc_type) { - uptr p = reinterpret_cast(ptr); - if (p == 0) return; - - uptr chunk_beg = p - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast(chunk_beg); - if (delete_size && flags()->new_delete_type_mismatch && - delete_size != m->UsedSize()) { - ReportNewDeleteSizeMismatch(p, delete_size, stack); - } - ASAN_FREE_HOOK(ptr); - // Must mark the chunk as quarantined before any changes to its metadata. - AtomicallySetQuarantineFlag(m, ptr, stack); - QuarantineChunk(m, ptr, stack, alloc_type); -} - -static void *Reallocate(void *old_ptr, uptr new_size, - BufferedStackTrace *stack) { - CHECK(old_ptr && new_size); - uptr p = reinterpret_cast(old_ptr); - uptr chunk_beg = p - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast(chunk_beg); - - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.reallocs++; - thread_stats.realloced += new_size; - - void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); - if (new_ptr) { - u8 chunk_state = m->chunk_state; - if (chunk_state != CHUNK_ALLOCATED) - ReportInvalidFree(old_ptr, chunk_state, stack); - CHECK_NE(REAL(memcpy), (void*)0); - uptr memcpy_size = Min(new_size, m->UsedSize()); - // If realloc() races with free(), we may start copying freed memory. - // However, we will report racy double-free later anyway. - REAL(memcpy)(new_ptr, old_ptr, memcpy_size); - Deallocate(old_ptr, 0, stack, FROM_MALLOC); - } - return new_ptr; -} - -// Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). -static AsanChunk *GetAsanChunk(void *alloc_beg) { - if (!alloc_beg) return 0; - if (!allocator.FromPrimary(alloc_beg)) { - uptr *meta = reinterpret_cast(allocator.GetMetaData(alloc_beg)); - AsanChunk *m = reinterpret_cast(meta[1]); - return m; - } - uptr *alloc_magic = reinterpret_cast(alloc_beg); - if (alloc_magic[0] == kAllocBegMagic) - return reinterpret_cast(alloc_magic[1]); - return reinterpret_cast(alloc_beg); -} - -static AsanChunk *GetAsanChunkByAddr(uptr p) { - void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast(p)); - return GetAsanChunk(alloc_beg); -} - -// Allocator must be locked when this function is called. -static AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { - void *alloc_beg = - allocator.GetBlockBeginFastLocked(reinterpret_cast(p)); - return GetAsanChunk(alloc_beg); -} - -static uptr AllocationSize(uptr p) { - AsanChunk *m = GetAsanChunkByAddr(p); - if (!m) return 0; - if (m->chunk_state != CHUNK_ALLOCATED) return 0; - if (m->Beg() != p) return 0; - return m->UsedSize(); -} - -// We have an address between two chunks, and we want to report just one. -AsanChunk *ChooseChunk(uptr addr, - AsanChunk *left_chunk, AsanChunk *right_chunk) { - // Prefer an allocated chunk over freed chunk and freed chunk - // over available chunk. - if (left_chunk->chunk_state != right_chunk->chunk_state) { - if (left_chunk->chunk_state == CHUNK_ALLOCATED) - return left_chunk; - if (right_chunk->chunk_state == CHUNK_ALLOCATED) - return right_chunk; - if (left_chunk->chunk_state == CHUNK_QUARANTINE) - return left_chunk; - if (right_chunk->chunk_state == CHUNK_QUARANTINE) - return right_chunk; - } - // Same chunk_state: choose based on offset. - sptr l_offset = 0, r_offset = 0; - CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); - CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); - if (l_offset < r_offset) - return left_chunk; - return right_chunk; -} - -AsanChunkView FindHeapChunkByAddress(uptr addr) { - AsanChunk *m1 = GetAsanChunkByAddr(addr); - if (!m1) return AsanChunkView(m1); - sptr offset = 0; - if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) { - // The address is in the chunk's left redzone, so maybe it is actually - // a right buffer overflow from the other chunk to the left. - // Search a bit to the left to see if there is another chunk. - AsanChunk *m2 = 0; - for (uptr l = 1; l < GetPageSizeCached(); l++) { - m2 = GetAsanChunkByAddr(addr - l); - if (m2 == m1) continue; // Still the same chunk. - break; - } - if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset)) - m1 = ChooseChunk(addr, m2, m1); - } - return AsanChunkView(m1); -} - -void AsanThreadLocalMallocStorage::CommitBack() { - AllocatorCache *ac = GetAllocatorCache(this); - quarantine.Drain(GetQuarantineCache(this), QuarantineCallback(ac)); - allocator.SwallowCache(GetAllocatorCache(this)); -} - -void PrintInternalAllocatorStats() { - allocator.PrintStats(); -} - -void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, - AllocType alloc_type) { - return Allocate(size, alignment, stack, alloc_type, true); -} - -void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) { - Deallocate(ptr, 0, stack, alloc_type); -} - -void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack, - AllocType alloc_type) { - Deallocate(ptr, size, stack, alloc_type); -} - -void *asan_malloc(uptr size, BufferedStackTrace *stack) { - return Allocate(size, 8, stack, FROM_MALLOC, true); -} - -void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) - return AllocatorReturnNull(); - void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); - // If the memory comes from the secondary allocator no need to clear it - // as it comes directly from mmap. - if (ptr && allocator.FromPrimary(ptr)) - REAL(memset)(ptr, 0, nmemb * size); - return ptr; -} - -void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { - if (p == 0) - return Allocate(size, 8, stack, FROM_MALLOC, true); - if (size == 0) { - Deallocate(p, 0, stack, FROM_MALLOC); - return 0; - } - return Reallocate(p, size, stack); -} - -void *asan_valloc(uptr size, BufferedStackTrace *stack) { - return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true); -} - -void *asan_pvalloc(uptr size, BufferedStackTrace *stack) { - uptr PageSize = GetPageSizeCached(); - size = RoundUpTo(size, PageSize); - if (size == 0) { - // pvalloc(0) should allocate one page. - size = PageSize; - } - return Allocate(size, PageSize, stack, FROM_MALLOC, true); -} - -int asan_posix_memalign(void **memptr, uptr alignment, uptr size, - BufferedStackTrace *stack) { - void *ptr = Allocate(size, alignment, stack, FROM_MALLOC, true); - CHECK(IsAligned((uptr)ptr, alignment)); - *memptr = ptr; - return 0; -} - -uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) { - if (ptr == 0) return 0; - uptr usable_size = AllocationSize(reinterpret_cast(ptr)); - if (flags()->check_malloc_usable_size && (usable_size == 0)) { - GET_STACK_TRACE_FATAL(pc, bp); - ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); - } - return usable_size; -} - -uptr asan_mz_size(const void *ptr) { - return AllocationSize(reinterpret_cast(ptr)); -} - -void asan_mz_force_lock() { - allocator.ForceLock(); - fallback_mutex.Lock(); -} - -void asan_mz_force_unlock() { - fallback_mutex.Unlock(); - allocator.ForceUnlock(); -} - -} // namespace __asan - -// --- Implementation of LSan-specific functions --- {{{1 -namespace __lsan { -void LockAllocator() { - __asan::allocator.ForceLock(); -} - -void UnlockAllocator() { - __asan::allocator.ForceUnlock(); -} - -void GetAllocatorGlobalRange(uptr *begin, uptr *end) { - *begin = (uptr)&__asan::allocator; - *end = *begin + sizeof(__asan::allocator); -} - -uptr PointsIntoChunk(void* p) { - uptr addr = reinterpret_cast(p); - __asan::AsanChunk *m = __asan::GetAsanChunkByAddrFastLocked(addr); - if (!m) return 0; - uptr chunk = m->Beg(); - if (m->chunk_state != __asan::CHUNK_ALLOCATED) - return 0; - if (m->AddrIsInside(addr, /*locked_version=*/true)) - return chunk; - if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true), - addr)) - return chunk; - return 0; -} - -uptr GetUserBegin(uptr chunk) { - __asan::AsanChunk *m = - __asan::GetAsanChunkByAddrFastLocked(chunk); - CHECK(m); - return m->Beg(); -} - -LsanMetadata::LsanMetadata(uptr chunk) { - metadata_ = reinterpret_cast(chunk - __asan::kChunkHeaderSize); -} - -bool LsanMetadata::allocated() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->chunk_state == __asan::CHUNK_ALLOCATED; -} - -ChunkTag LsanMetadata::tag() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return static_cast(m->lsan_tag); -} - -void LsanMetadata::set_tag(ChunkTag value) { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - m->lsan_tag = value; -} - -uptr LsanMetadata::requested_size() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->UsedSize(/*locked_version=*/true); -} - -u32 LsanMetadata::stack_trace_id() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->alloc_context_id; -} - -void ForEachChunk(ForEachChunkCallback callback, void *arg) { - __asan::allocator.ForEachChunk(callback, arg); -} - -IgnoreObjectResult IgnoreObjectLocked(const void *p) { - uptr addr = reinterpret_cast(p); - __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr); - if (!m) return kIgnoreObjectInvalid; - if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) { - if (m->lsan_tag == kIgnored) - return kIgnoreObjectAlreadyIgnored; - m->lsan_tag = __lsan::kIgnored; - return kIgnoreObjectSuccess; - } else { - return kIgnoreObjectInvalid; - } -} -} // namespace __lsan - -// ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -// ASan allocator doesn't reserve extra bytes, so normally we would -// just return "size". We don't want to expose our redzone sizes, etc here. -uptr __sanitizer_get_estimated_allocated_size(uptr size) { - return size; -} - -int __sanitizer_get_ownership(const void *p) { - uptr ptr = reinterpret_cast(p); - return (AllocationSize(ptr) > 0); -} - -uptr __sanitizer_get_allocated_size(const void *p) { - if (p == 0) return 0; - uptr ptr = reinterpret_cast(p); - uptr allocated_size = AllocationSize(ptr); - // Die if p is not malloced or if it is already freed. - if (allocated_size == 0) { - GET_STACK_TRACE_FATAL_HERE; - ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack); - } - return allocated_size; -} - -#if !SANITIZER_SUPPORTS_WEAK_HOOKS -// Provide default (no-op) implementation of malloc hooks. -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -void __sanitizer_malloc_hook(void *ptr, uptr size) { - (void)ptr; - (void)size; -} -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -void __sanitizer_free_hook(void *ptr) { - (void)ptr; -} -} // extern "C" -#endif Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_activation.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_activation.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_activation.cc (revision 279191) @@ -1,88 +1,142 @@ //===-- asan_activation.cc --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // ASan activation/deactivation logic. //===----------------------------------------------------------------------===// #include "asan_activation.h" #include "asan_allocator.h" #include "asan_flags.h" #include "asan_internal.h" +#include "asan_poisoning.h" +#include "asan_stack.h" #include "sanitizer_common/sanitizer_flags.h" namespace __asan { static struct AsanDeactivatedFlags { - int quarantine_size; - int max_redzone; + AllocatorOptions allocator_options; int malloc_context_size; bool poison_heap; - bool alloc_dealloc_mismatch; - bool allocator_may_return_null; + bool coverage; + const char *coverage_dir; + + void RegisterActivationFlags(FlagParser *parser, Flags *f, CommonFlags *cf) { +#define ASAN_ACTIVATION_FLAG(Type, Name) \ + RegisterFlag(parser, #Name, "", &f->Name); +#define COMMON_ACTIVATION_FLAG(Type, Name) \ + RegisterFlag(parser, #Name, "", &cf->Name); +#include "asan_activation_flags.inc" +#undef ASAN_ACTIVATION_FLAG +#undef COMMON_ACTIVATION_FLAG + + RegisterIncludeFlag(parser, cf); + } + + void OverrideFromActivationFlags() { + Flags f; + CommonFlags cf; + FlagParser parser; + RegisterActivationFlags(&parser, &f, &cf); + + // Copy the current activation flags. + allocator_options.CopyTo(&f, &cf); + cf.malloc_context_size = malloc_context_size; + f.poison_heap = poison_heap; + cf.coverage = coverage; + cf.coverage_dir = coverage_dir; + cf.verbosity = Verbosity(); + cf.help = false; // this is activation-specific help + + // Check if activation flags need to be overriden. + if (const char *env = GetEnv("ASAN_ACTIVATION_OPTIONS")) { + parser.ParseString(env); + } + + // Override from getprop asan.options. + char buf[100]; + GetExtraActivationFlags(buf, sizeof(buf)); + parser.ParseString(buf); + + SetVerbosity(cf.verbosity); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (cf.help) parser.PrintFlagDescriptions(); + + allocator_options.SetFrom(&f, &cf); + malloc_context_size = cf.malloc_context_size; + poison_heap = f.poison_heap; + coverage = cf.coverage; + coverage_dir = cf.coverage_dir; + } + + void Print() { + Report( + "quarantine_size_mb %d, max_redzone %d, poison_heap %d, " + "malloc_context_size %d, alloc_dealloc_mismatch %d, " + "allocator_may_return_null %d, coverage %d, coverage_dir %s\n", + allocator_options.quarantine_size_mb, allocator_options.max_redzone, + poison_heap, malloc_context_size, + allocator_options.alloc_dealloc_mismatch, + allocator_options.may_return_null, coverage, coverage_dir); + } } asan_deactivated_flags; static bool asan_is_deactivated; -void AsanStartDeactivated() { +void AsanDeactivate() { + CHECK(!asan_is_deactivated); VReport(1, "Deactivating ASan\n"); - // Save flag values. - asan_deactivated_flags.quarantine_size = flags()->quarantine_size; - asan_deactivated_flags.max_redzone = flags()->max_redzone; - asan_deactivated_flags.poison_heap = flags()->poison_heap; - asan_deactivated_flags.malloc_context_size = - common_flags()->malloc_context_size; - asan_deactivated_flags.alloc_dealloc_mismatch = - flags()->alloc_dealloc_mismatch; - asan_deactivated_flags.allocator_may_return_null = - common_flags()->allocator_may_return_null; - flags()->quarantine_size = 0; - flags()->max_redzone = 16; - flags()->poison_heap = false; - common_flags()->malloc_context_size = 0; - flags()->alloc_dealloc_mismatch = false; - common_flags()->allocator_may_return_null = true; + // Stash runtime state. + GetAllocatorOptions(&asan_deactivated_flags.allocator_options); + asan_deactivated_flags.malloc_context_size = GetMallocContextSize(); + asan_deactivated_flags.poison_heap = CanPoisonMemory(); + asan_deactivated_flags.coverage = common_flags()->coverage; + asan_deactivated_flags.coverage_dir = common_flags()->coverage_dir; + // Deactivate the runtime. + SetCanPoisonMemory(false); + SetMallocContextSize(1); + ReInitializeCoverage(false, nullptr); + + AllocatorOptions disabled = asan_deactivated_flags.allocator_options; + disabled.quarantine_size_mb = 0; + disabled.min_redzone = 16; // Redzone must be at least 16 bytes long. + disabled.max_redzone = 16; + disabled.alloc_dealloc_mismatch = false; + disabled.may_return_null = true; + ReInitializeAllocator(disabled); + asan_is_deactivated = true; } void AsanActivate() { if (!asan_is_deactivated) return; VReport(1, "Activating ASan\n"); - // Restore flag values. - // FIXME: this is not atomic, and there may be other threads alive. - flags()->quarantine_size = asan_deactivated_flags.quarantine_size; - flags()->max_redzone = asan_deactivated_flags.max_redzone; - flags()->poison_heap = asan_deactivated_flags.poison_heap; - common_flags()->malloc_context_size = - asan_deactivated_flags.malloc_context_size; - flags()->alloc_dealloc_mismatch = - asan_deactivated_flags.alloc_dealloc_mismatch; - common_flags()->allocator_may_return_null = - asan_deactivated_flags.allocator_may_return_null; + asan_deactivated_flags.OverrideFromActivationFlags(); - ParseExtraActivationFlags(); + SetCanPoisonMemory(asan_deactivated_flags.poison_heap); + SetMallocContextSize(asan_deactivated_flags.malloc_context_size); + ReInitializeCoverage(asan_deactivated_flags.coverage, + asan_deactivated_flags.coverage_dir); + ReInitializeAllocator(asan_deactivated_flags.allocator_options); - ReInitializeAllocator(); - asan_is_deactivated = false; - VReport( - 1, - "quarantine_size %d, max_redzone %d, poison_heap %d, " - "malloc_context_size %d, alloc_dealloc_mismatch %d, " - "allocator_may_return_null %d\n", - flags()->quarantine_size, flags()->max_redzone, flags()->poison_heap, - common_flags()->malloc_context_size, flags()->alloc_dealloc_mismatch, - common_flags()->allocator_may_return_null); + if (Verbosity()) { + Report("Activated with flags:\n"); + asan_deactivated_flags.Print(); + } } } // namespace __asan Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_activation.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_activation.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_activation.h (revision 279191) @@ -1,23 +1,23 @@ //===-- asan_activation.h ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // ASan activation/deactivation logic. //===----------------------------------------------------------------------===// #ifndef ASAN_ACTIVATION_H #define ASAN_ACTIVATION_H namespace __asan { -void AsanStartDeactivated(); +void AsanDeactivate(); void AsanActivate(); } // namespace __asan #endif // ASAN_ACTIVATION_H Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_activation_flags.inc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_activation_flags.inc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_activation_flags.inc (revision 279191) @@ -0,0 +1,35 @@ +//===-- asan_activation_flags.inc -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A subset of ASan (and common) runtime flags supported at activation time. +// +//===----------------------------------------------------------------------===// +#ifndef ASAN_ACTIVATION_FLAG +# error "Define ASAN_ACTIVATION_FLAG prior to including this file!" +#endif + +#ifndef COMMON_ACTIVATION_FLAG +# error "Define COMMON_ACTIVATION_FLAG prior to including this file!" +#endif + +// ASAN_ACTIVATION_FLAG(Type, Name) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +ASAN_ACTIVATION_FLAG(int, redzone) +ASAN_ACTIVATION_FLAG(int, max_redzone) +ASAN_ACTIVATION_FLAG(int, quarantine_size_mb) +ASAN_ACTIVATION_FLAG(bool, alloc_dealloc_mismatch) +ASAN_ACTIVATION_FLAG(bool, poison_heap) + +COMMON_ACTIVATION_FLAG(bool, allocator_may_return_null) +COMMON_ACTIVATION_FLAG(int, malloc_context_size) +COMMON_ACTIVATION_FLAG(bool, coverage) +COMMON_ACTIVATION_FLAG(const char *, coverage_dir) +COMMON_ACTIVATION_FLAG(int, verbosity) +COMMON_ACTIVATION_FLAG(bool, help) Property changes on: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_activation_flags.inc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_allocator.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_allocator.cc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_allocator.cc (revision 279191) @@ -0,0 +1,909 @@ +//===-- asan_allocator.cc -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Implementation of ASan's memory allocator, 2-nd version. +// This variant uses the allocator from sanitizer_common, i.e. the one shared +// with ThreadSanitizer and MemorySanitizer. +// +//===----------------------------------------------------------------------===// +#include "asan_allocator.h" + +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_allocator_interface.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_list.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_quarantine.h" +#include "lsan/lsan_common.h" + +namespace __asan { + +// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits. +// We use adaptive redzones: for larger allocation larger redzones are used. +static u32 RZLog2Size(u32 rz_log) { + CHECK_LT(rz_log, 8); + return 16 << rz_log; +} + +static u32 RZSize2Log(u32 rz_size) { + CHECK_GE(rz_size, 16); + CHECK_LE(rz_size, 2048); + CHECK(IsPowerOfTwo(rz_size)); + u32 res = Log2(rz_size) - 4; + CHECK_EQ(rz_size, RZLog2Size(res)); + return res; +} + +static AsanAllocator &get_allocator(); + +// The memory chunk allocated from the underlying allocator looks like this: +// L L L L L L H H U U U U U U R R +// L -- left redzone words (0 or more bytes) +// H -- ChunkHeader (16 bytes), which is also a part of the left redzone. +// U -- user memory. +// R -- right redzone (0 or more bytes) +// ChunkBase consists of ChunkHeader and other bytes that overlap with user +// memory. + +// If the left redzone is greater than the ChunkHeader size we store a magic +// value in the first uptr word of the memory block and store the address of +// ChunkBase in the next uptr. +// M B L L L L L L L L L H H U U U U U U +// | ^ +// ---------------------| +// M -- magic value kAllocBegMagic +// B -- address of ChunkHeader pointing to the first 'H' +static const uptr kAllocBegMagic = 0xCC6E96B9; + +struct ChunkHeader { + // 1-st 8 bytes. + u32 chunk_state : 8; // Must be first. + u32 alloc_tid : 24; + + u32 free_tid : 24; + u32 from_memalign : 1; + u32 alloc_type : 2; + u32 rz_log : 3; + u32 lsan_tag : 2; + // 2-nd 8 bytes + // This field is used for small sizes. For large sizes it is equal to + // SizeClassMap::kMaxSize and the actual size is stored in the + // SecondaryAllocator's metadata. + u32 user_requested_size; + u32 alloc_context_id; +}; + +struct ChunkBase : ChunkHeader { + // Header2, intersects with user memory. + u32 free_context_id; +}; + +static const uptr kChunkHeaderSize = sizeof(ChunkHeader); +static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize; +COMPILER_CHECK(kChunkHeaderSize == 16); +COMPILER_CHECK(kChunkHeader2Size <= 16); + +// Every chunk of memory allocated by this allocator can be in one of 3 states: +// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. +// CHUNK_ALLOCATED: the chunk is allocated and not yet freed. +// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone. +enum { + CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it. + CHUNK_ALLOCATED = 2, + CHUNK_QUARANTINE = 3 +}; + +struct AsanChunk: ChunkBase { + uptr Beg() { return reinterpret_cast(this) + kChunkHeaderSize; } + uptr UsedSize(bool locked_version = false) { + if (user_requested_size != SizeClassMap::kMaxSize) + return user_requested_size; + return *reinterpret_cast( + get_allocator().GetMetaData(AllocBeg(locked_version))); + } + void *AllocBeg(bool locked_version = false) { + if (from_memalign) { + if (locked_version) + return get_allocator().GetBlockBeginFastLocked( + reinterpret_cast(this)); + return get_allocator().GetBlockBegin(reinterpret_cast(this)); + } + return reinterpret_cast(Beg() - RZLog2Size(rz_log)); + } + bool AddrIsInside(uptr addr, bool locked_version = false) { + return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); + } +}; + +struct QuarantineCallback { + explicit QuarantineCallback(AllocatorCache *cache) + : cache_(cache) { + } + + void Recycle(AsanChunk *m) { + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed); + CHECK_NE(m->alloc_tid, kInvalidTid); + CHECK_NE(m->free_tid, kInvalidTid); + PoisonShadow(m->Beg(), + RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), + kAsanHeapLeftRedzoneMagic); + void *p = reinterpret_cast(m->AllocBeg()); + if (p != m) { + uptr *alloc_magic = reinterpret_cast(p); + CHECK_EQ(alloc_magic[0], kAllocBegMagic); + // Clear the magic value, as allocator internals may overwrite the + // contents of deallocated chunk, confusing GetAsanChunk lookup. + alloc_magic[0] = 0; + CHECK_EQ(alloc_magic[1], reinterpret_cast(m)); + } + + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.real_frees++; + thread_stats.really_freed += m->UsedSize(); + + get_allocator().Deallocate(cache_, p); + } + + void *Allocate(uptr size) { + return get_allocator().Allocate(cache_, size, 1, false); + } + + void Deallocate(void *p) { + get_allocator().Deallocate(cache_, p); + } + + AllocatorCache *cache_; +}; + +typedef Quarantine AsanQuarantine; +typedef AsanQuarantine::Cache QuarantineCache; + +void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const { + PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.mmaps++; + thread_stats.mmaped += size; +} +void AsanMapUnmapCallback::OnUnmap(uptr p, uptr size) const { + PoisonShadow(p, size, 0); + // We are about to unmap a chunk of user memory. + // Mark the corresponding shadow memory as not needed. + FlushUnneededASanShadowMemory(p, size); + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.munmaps++; + thread_stats.munmaped += size; +} + +// We can not use THREADLOCAL because it is not supported on some of the +// platforms we care about (OSX 10.6, Android). +// static THREADLOCAL AllocatorCache cache; +AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) { + CHECK(ms); + return &ms->allocator_cache; +} + +QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) { + CHECK(ms); + CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache)); + return reinterpret_cast(ms->quarantine_cache); +} + +void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) { + quarantine_size_mb = f->quarantine_size_mb; + min_redzone = f->redzone; + max_redzone = f->max_redzone; + may_return_null = cf->allocator_may_return_null; + alloc_dealloc_mismatch = f->alloc_dealloc_mismatch; +} + +void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) { + f->quarantine_size_mb = quarantine_size_mb; + f->redzone = min_redzone; + f->max_redzone = max_redzone; + cf->allocator_may_return_null = may_return_null; + f->alloc_dealloc_mismatch = alloc_dealloc_mismatch; +} + +struct Allocator { + static const uptr kMaxAllowedMallocSize = + FIRST_32_SECOND_64(3UL << 30, 64UL << 30); + static const uptr kMaxThreadLocalQuarantine = + FIRST_32_SECOND_64(1 << 18, 1 << 20); + + AsanAllocator allocator; + AsanQuarantine quarantine; + StaticSpinMutex fallback_mutex; + AllocatorCache fallback_allocator_cache; + QuarantineCache fallback_quarantine_cache; + + // ------------------- Options -------------------------- + atomic_uint16_t min_redzone; + atomic_uint16_t max_redzone; + atomic_uint8_t alloc_dealloc_mismatch; + + // ------------------- Initialization ------------------------ + explicit Allocator(LinkerInitialized) + : quarantine(LINKER_INITIALIZED), + fallback_quarantine_cache(LINKER_INITIALIZED) {} + + void CheckOptions(const AllocatorOptions &options) const { + CHECK_GE(options.min_redzone, 16); + CHECK_GE(options.max_redzone, options.min_redzone); + CHECK_LE(options.max_redzone, 2048); + CHECK(IsPowerOfTwo(options.min_redzone)); + CHECK(IsPowerOfTwo(options.max_redzone)); + } + + void SharedInitCode(const AllocatorOptions &options) { + CheckOptions(options); + quarantine.Init((uptr)options.quarantine_size_mb << 20, + kMaxThreadLocalQuarantine); + atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch, + memory_order_release); + atomic_store(&min_redzone, options.min_redzone, memory_order_release); + atomic_store(&max_redzone, options.max_redzone, memory_order_release); + } + + void Initialize(const AllocatorOptions &options) { + allocator.Init(options.may_return_null); + SharedInitCode(options); + } + + void ReInitialize(const AllocatorOptions &options) { + allocator.SetMayReturnNull(options.may_return_null); + SharedInitCode(options); + } + + void GetOptions(AllocatorOptions *options) const { + options->quarantine_size_mb = quarantine.GetSize() >> 20; + options->min_redzone = atomic_load(&min_redzone, memory_order_acquire); + options->max_redzone = atomic_load(&max_redzone, memory_order_acquire); + options->may_return_null = allocator.MayReturnNull(); + options->alloc_dealloc_mismatch = + atomic_load(&alloc_dealloc_mismatch, memory_order_acquire); + } + + // -------------------- Helper methods. ------------------------- + uptr ComputeRZLog(uptr user_requested_size) { + u32 rz_log = + user_requested_size <= 64 - 16 ? 0 : + user_requested_size <= 128 - 32 ? 1 : + user_requested_size <= 512 - 64 ? 2 : + user_requested_size <= 4096 - 128 ? 3 : + user_requested_size <= (1 << 14) - 256 ? 4 : + user_requested_size <= (1 << 15) - 512 ? 5 : + user_requested_size <= (1 << 16) - 1024 ? 6 : 7; + u32 min_rz = atomic_load(&min_redzone, memory_order_acquire); + u32 max_rz = atomic_load(&max_redzone, memory_order_acquire); + return Min(Max(rz_log, RZSize2Log(min_rz)), RZSize2Log(max_rz)); + } + + // We have an address between two chunks, and we want to report just one. + AsanChunk *ChooseChunk(uptr addr, AsanChunk *left_chunk, + AsanChunk *right_chunk) { + // Prefer an allocated chunk over freed chunk and freed chunk + // over available chunk. + if (left_chunk->chunk_state != right_chunk->chunk_state) { + if (left_chunk->chunk_state == CHUNK_ALLOCATED) + return left_chunk; + if (right_chunk->chunk_state == CHUNK_ALLOCATED) + return right_chunk; + if (left_chunk->chunk_state == CHUNK_QUARANTINE) + return left_chunk; + if (right_chunk->chunk_state == CHUNK_QUARANTINE) + return right_chunk; + } + // Same chunk_state: choose based on offset. + sptr l_offset = 0, r_offset = 0; + CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); + CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); + if (l_offset < r_offset) + return left_chunk; + return right_chunk; + } + + // -------------------- Allocation/Deallocation routines --------------- + void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack, + AllocType alloc_type, bool can_fill) { + if (UNLIKELY(!asan_inited)) + AsanInitFromRtl(); + Flags &fl = *flags(); + CHECK(stack); + const uptr min_alignment = SHADOW_GRANULARITY; + if (alignment < min_alignment) + alignment = min_alignment; + if (size == 0) { + // We'd be happy to avoid allocating memory for zero-size requests, but + // some programs/tests depend on this behavior and assume that malloc + // would not return NULL even for zero-size allocations. Moreover, it + // looks like operator new should never return NULL, and results of + // consecutive "new" calls must be different even if the allocated size + // is zero. + size = 1; + } + CHECK(IsPowerOfTwo(alignment)); + uptr rz_log = ComputeRZLog(size); + uptr rz_size = RZLog2Size(rz_log); + uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); + uptr needed_size = rounded_size + rz_size; + if (alignment > min_alignment) + needed_size += alignment; + bool using_primary_allocator = true; + // If we are allocating from the secondary allocator, there will be no + // automatic right redzone, so add the right redzone manually. + if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) { + needed_size += rz_size; + using_primary_allocator = false; + } + CHECK(IsAligned(needed_size, min_alignment)); + if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { + Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", + (void*)size); + return allocator.ReturnNullOrDie(); + } + + AsanThread *t = GetCurrentThread(); + void *allocated; + bool check_rss_limit = true; + if (t) { + AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); + allocated = + allocator.Allocate(cache, needed_size, 8, false, check_rss_limit); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *cache = &fallback_allocator_cache; + allocated = + allocator.Allocate(cache, needed_size, 8, false, check_rss_limit); + } + + if (!allocated) + return allocator.ReturnNullOrDie(); + + if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) { + // Heap poisoning is enabled, but the allocator provides an unpoisoned + // chunk. This is possible if CanPoisonMemory() was false for some + // time, for example, due to flags()->start_disabled. + // Anyway, poison the block before using it for anything else. + uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated); + PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic); + } + + uptr alloc_beg = reinterpret_cast(allocated); + uptr alloc_end = alloc_beg + needed_size; + uptr beg_plus_redzone = alloc_beg + rz_size; + uptr user_beg = beg_plus_redzone; + if (!IsAligned(user_beg, alignment)) + user_beg = RoundUpTo(user_beg, alignment); + uptr user_end = user_beg + size; + CHECK_LE(user_end, alloc_end); + uptr chunk_beg = user_beg - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + m->alloc_type = alloc_type; + m->rz_log = rz_log; + u32 alloc_tid = t ? t->tid() : 0; + m->alloc_tid = alloc_tid; + CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield? + m->free_tid = kInvalidTid; + m->from_memalign = user_beg != beg_plus_redzone; + if (alloc_beg != chunk_beg) { + CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); + reinterpret_cast(alloc_beg)[0] = kAllocBegMagic; + reinterpret_cast(alloc_beg)[1] = chunk_beg; + } + if (using_primary_allocator) { + CHECK(size); + m->user_requested_size = size; + CHECK(allocator.FromPrimary(allocated)); + } else { + CHECK(!allocator.FromPrimary(allocated)); + m->user_requested_size = SizeClassMap::kMaxSize; + uptr *meta = reinterpret_cast(allocator.GetMetaData(allocated)); + meta[0] = size; + meta[1] = chunk_beg; + } + + m->alloc_context_id = StackDepotPut(*stack); + + uptr size_rounded_down_to_granularity = + RoundDownTo(size, SHADOW_GRANULARITY); + // Unpoison the bulk of the memory region. + if (size_rounded_down_to_granularity) + PoisonShadow(user_beg, size_rounded_down_to_granularity, 0); + // Deal with the end of the region if size is not aligned to granularity. + if (size != size_rounded_down_to_granularity && CanPoisonMemory()) { + u8 *shadow = + (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity); + *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; + } + + AsanStats &thread_stats = GetCurrentThreadStats(); + 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++; + + void *res = reinterpret_cast(user_beg); + if (can_fill && fl.max_malloc_fill_size) { + uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); + REAL(memset)(res, fl.malloc_fill_byte, fill_size); + } +#if CAN_SANITIZE_LEAKS + m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored + : __lsan::kDirectlyLeaked; +#endif + // Must be the last mutation of metadata in this function. + atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); + ASAN_MALLOC_HOOK(res, size); + return res; + } + + void AtomicallySetQuarantineFlag(AsanChunk *m, void *ptr, + BufferedStackTrace *stack) { + u8 old_chunk_state = CHUNK_ALLOCATED; + // Flip the chunk_state atomically to avoid race on double-free. + if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state, + CHUNK_QUARANTINE, memory_order_acquire)) + ReportInvalidFree(ptr, old_chunk_state, stack); + CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); + } + + // Expects the chunk to already be marked as quarantined by using + // AtomicallySetQuarantineFlag. + void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack, + AllocType alloc_type) { + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + + if (m->alloc_type != alloc_type) { + if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) { + ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type, + (AllocType)alloc_type); + } + } + + CHECK_GE(m->alloc_tid, 0); + if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area. + CHECK_EQ(m->free_tid, kInvalidTid); + AsanThread *t = GetCurrentThread(); + m->free_tid = t ? t->tid() : 0; + m->free_context_id = StackDepotPut(*stack); + // Poison the region. + PoisonShadow(m->Beg(), + RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), + kAsanHeapFreeMagic); + + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.frees++; + thread_stats.freed += m->UsedSize(); + + // Push into quarantine. + if (t) { + AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); + AllocatorCache *ac = GetAllocatorCache(ms); + quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac), m, + m->UsedSize()); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *ac = &fallback_allocator_cache; + quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac), m, + m->UsedSize()); + } + } + + void Deallocate(void *ptr, uptr delete_size, BufferedStackTrace *stack, + AllocType alloc_type) { + uptr p = reinterpret_cast(ptr); + if (p == 0) return; + + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + if (delete_size && flags()->new_delete_type_mismatch && + delete_size != m->UsedSize()) { + ReportNewDeleteSizeMismatch(p, delete_size, stack); + } + ASAN_FREE_HOOK(ptr); + // Must mark the chunk as quarantined before any changes to its metadata. + AtomicallySetQuarantineFlag(m, ptr, stack); + QuarantineChunk(m, ptr, stack, alloc_type); + } + + void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) { + CHECK(old_ptr && new_size); + uptr p = reinterpret_cast(old_ptr); + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.reallocs++; + thread_stats.realloced += new_size; + + void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); + if (new_ptr) { + u8 chunk_state = m->chunk_state; + if (chunk_state != CHUNK_ALLOCATED) + ReportInvalidFree(old_ptr, chunk_state, stack); + CHECK_NE(REAL(memcpy), (void*)0); + uptr memcpy_size = Min(new_size, m->UsedSize()); + // If realloc() races with free(), we may start copying freed memory. + // However, we will report racy double-free later anyway. + REAL(memcpy)(new_ptr, old_ptr, memcpy_size); + Deallocate(old_ptr, 0, stack, FROM_MALLOC); + } + return new_ptr; + } + + void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return allocator.ReturnNullOrDie(); + void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); + // If the memory comes from the secondary allocator no need to clear it + // as it comes directly from mmap. + if (ptr && allocator.FromPrimary(ptr)) + REAL(memset)(ptr, 0, nmemb * size); + return ptr; + } + + void ReportInvalidFree(void *ptr, u8 chunk_state, BufferedStackTrace *stack) { + if (chunk_state == CHUNK_QUARANTINE) + ReportDoubleFree((uptr)ptr, stack); + else + ReportFreeNotMalloced((uptr)ptr, stack); + } + + void CommitBack(AsanThreadLocalMallocStorage *ms) { + AllocatorCache *ac = GetAllocatorCache(ms); + quarantine.Drain(GetQuarantineCache(ms), QuarantineCallback(ac)); + allocator.SwallowCache(ac); + } + + // -------------------------- Chunk lookup ---------------------- + + // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). + AsanChunk *GetAsanChunk(void *alloc_beg) { + if (!alloc_beg) return 0; + if (!allocator.FromPrimary(alloc_beg)) { + uptr *meta = reinterpret_cast(allocator.GetMetaData(alloc_beg)); + AsanChunk *m = reinterpret_cast(meta[1]); + return m; + } + uptr *alloc_magic = reinterpret_cast(alloc_beg); + if (alloc_magic[0] == kAllocBegMagic) + return reinterpret_cast(alloc_magic[1]); + return reinterpret_cast(alloc_beg); + } + + AsanChunk *GetAsanChunkByAddr(uptr p) { + void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast(p)); + return GetAsanChunk(alloc_beg); + } + + // Allocator must be locked when this function is called. + AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { + void *alloc_beg = + allocator.GetBlockBeginFastLocked(reinterpret_cast(p)); + return GetAsanChunk(alloc_beg); + } + + uptr AllocationSize(uptr p) { + AsanChunk *m = GetAsanChunkByAddr(p); + if (!m) return 0; + if (m->chunk_state != CHUNK_ALLOCATED) return 0; + if (m->Beg() != p) return 0; + return m->UsedSize(); + } + + AsanChunkView FindHeapChunkByAddress(uptr addr) { + AsanChunk *m1 = GetAsanChunkByAddr(addr); + if (!m1) return AsanChunkView(m1); + sptr offset = 0; + if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) { + // The address is in the chunk's left redzone, so maybe it is actually + // a right buffer overflow from the other chunk to the left. + // Search a bit to the left to see if there is another chunk. + AsanChunk *m2 = 0; + for (uptr l = 1; l < GetPageSizeCached(); l++) { + m2 = GetAsanChunkByAddr(addr - l); + if (m2 == m1) continue; // Still the same chunk. + break; + } + if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset)) + m1 = ChooseChunk(addr, m2, m1); + } + return AsanChunkView(m1); + } + + void PrintStats() { + allocator.PrintStats(); + } + + void ForceLock() { + allocator.ForceLock(); + fallback_mutex.Lock(); + } + + void ForceUnlock() { + fallback_mutex.Unlock(); + allocator.ForceUnlock(); + } +}; + +static Allocator instance(LINKER_INITIALIZED); + +static AsanAllocator &get_allocator() { + return instance.allocator; +} + +bool AsanChunkView::IsValid() { + return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE; +} +uptr AsanChunkView::Beg() { return chunk_->Beg(); } +uptr AsanChunkView::End() { return Beg() + UsedSize(); } +uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); } +uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; } +uptr AsanChunkView::FreeTid() { return chunk_->free_tid; } + +static StackTrace GetStackTraceFromId(u32 id) { + CHECK(id); + StackTrace res = StackDepotGet(id); + CHECK(res.trace); + return res; +} + +StackTrace AsanChunkView::GetAllocStack() { + return GetStackTraceFromId(chunk_->alloc_context_id); +} + +StackTrace AsanChunkView::GetFreeStack() { + return GetStackTraceFromId(chunk_->free_context_id); +} + +void InitializeAllocator(const AllocatorOptions &options) { + instance.Initialize(options); +} + +void ReInitializeAllocator(const AllocatorOptions &options) { + instance.ReInitialize(options); +} + +void GetAllocatorOptions(AllocatorOptions *options) { + instance.GetOptions(options); +} + +AsanChunkView FindHeapChunkByAddress(uptr addr) { + return instance.FindHeapChunkByAddress(addr); +} + +void AsanThreadLocalMallocStorage::CommitBack() { + instance.CommitBack(this); +} + +void PrintInternalAllocatorStats() { + instance.PrintStats(); +} + +void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, + AllocType alloc_type) { + return instance.Allocate(size, alignment, stack, alloc_type, true); +} + +void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) { + instance.Deallocate(ptr, 0, stack, alloc_type); +} + +void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack, + AllocType alloc_type) { + instance.Deallocate(ptr, size, stack, alloc_type); +} + +void *asan_malloc(uptr size, BufferedStackTrace *stack) { + return instance.Allocate(size, 8, stack, FROM_MALLOC, true); +} + +void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { + return instance.Calloc(nmemb, size, stack); +} + +void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { + if (p == 0) + return instance.Allocate(size, 8, stack, FROM_MALLOC, true); + if (size == 0) { + instance.Deallocate(p, 0, stack, FROM_MALLOC); + return 0; + } + return instance.Reallocate(p, size, stack); +} + +void *asan_valloc(uptr size, BufferedStackTrace *stack) { + return instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true); +} + +void *asan_pvalloc(uptr size, BufferedStackTrace *stack) { + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + return instance.Allocate(size, PageSize, stack, FROM_MALLOC, true); +} + +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, + BufferedStackTrace *stack) { + void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC, true); + CHECK(IsAligned((uptr)ptr, alignment)); + *memptr = ptr; + return 0; +} + +uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) { + if (ptr == 0) return 0; + uptr usable_size = instance.AllocationSize(reinterpret_cast(ptr)); + if (flags()->check_malloc_usable_size && (usable_size == 0)) { + GET_STACK_TRACE_FATAL(pc, bp); + ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); + } + return usable_size; +} + +uptr asan_mz_size(const void *ptr) { + return instance.AllocationSize(reinterpret_cast(ptr)); +} + +void asan_mz_force_lock() { + instance.ForceLock(); +} + +void asan_mz_force_unlock() { + instance.ForceUnlock(); +} + +void AsanSoftRssLimitExceededCallback(bool exceeded) { + instance.allocator.SetRssLimitIsExceeded(exceeded); +} + +} // namespace __asan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +void LockAllocator() { + __asan::get_allocator().ForceLock(); +} + +void UnlockAllocator() { + __asan::get_allocator().ForceUnlock(); +} + +void GetAllocatorGlobalRange(uptr *begin, uptr *end) { + *begin = (uptr)&__asan::get_allocator(); + *end = *begin + sizeof(__asan::get_allocator()); +} + +uptr PointsIntoChunk(void* p) { + uptr addr = reinterpret_cast(p); + __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(addr); + if (!m) return 0; + uptr chunk = m->Beg(); + if (m->chunk_state != __asan::CHUNK_ALLOCATED) + return 0; + if (m->AddrIsInside(addr, /*locked_version=*/true)) + return chunk; + if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true), + addr)) + return chunk; + return 0; +} + +uptr GetUserBegin(uptr chunk) { + __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk); + CHECK(m); + return m->Beg(); +} + +LsanMetadata::LsanMetadata(uptr chunk) { + metadata_ = reinterpret_cast(chunk - __asan::kChunkHeaderSize); +} + +bool LsanMetadata::allocated() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->chunk_state == __asan::CHUNK_ALLOCATED; +} + +ChunkTag LsanMetadata::tag() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return static_cast(m->lsan_tag); +} + +void LsanMetadata::set_tag(ChunkTag value) { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + m->lsan_tag = value; +} + +uptr LsanMetadata::requested_size() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->UsedSize(/*locked_version=*/true); +} + +u32 LsanMetadata::stack_trace_id() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->alloc_context_id; +} + +void ForEachChunk(ForEachChunkCallback callback, void *arg) { + __asan::get_allocator().ForEachChunk(callback, arg); +} + +IgnoreObjectResult IgnoreObjectLocked(const void *p) { + uptr addr = reinterpret_cast(p); + __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddr(addr); + if (!m) return kIgnoreObjectInvalid; + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) { + if (m->lsan_tag == kIgnored) + return kIgnoreObjectAlreadyIgnored; + m->lsan_tag = __lsan::kIgnored; + return kIgnoreObjectSuccess; + } else { + return kIgnoreObjectInvalid; + } +} +} // namespace __lsan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +// ASan allocator doesn't reserve extra bytes, so normally we would +// just return "size". We don't want to expose our redzone sizes, etc here. +uptr __sanitizer_get_estimated_allocated_size(uptr size) { + return size; +} + +int __sanitizer_get_ownership(const void *p) { + uptr ptr = reinterpret_cast(p); + return instance.AllocationSize(ptr) > 0; +} + +uptr __sanitizer_get_allocated_size(const void *p) { + if (p == 0) return 0; + uptr ptr = reinterpret_cast(p); + uptr allocated_size = instance.AllocationSize(ptr); + // Die if p is not malloced or if it is already freed. + if (allocated_size == 0) { + GET_STACK_TRACE_FATAL_HERE; + ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack); + } + return allocated_size; +} + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +// Provide default (no-op) implementation of malloc hooks. +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_malloc_hook(void *ptr, uptr size) { + (void)ptr; + (void)size; +} +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_free_hook(void *ptr) { + (void)ptr; +} +} // extern "C" +#endif Property changes on: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_allocator.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_allocator.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_allocator.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_allocator.h (revision 279191) @@ -1,165 +1,179 @@ //===-- asan_allocator.h ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_allocator2.cc. +// ASan-private header for asan_allocator.cc. //===----------------------------------------------------------------------===// #ifndef ASAN_ALLOCATOR_H #define ASAN_ALLOCATOR_H +#include "asan_flags.h" #include "asan_internal.h" #include "asan_interceptors.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_list.h" namespace __asan { enum AllocType { FROM_MALLOC = 1, // Memory block came from malloc, calloc, realloc, etc. FROM_NEW = 2, // Memory block came from operator new. FROM_NEW_BR = 3 // Memory block came from operator new [ ] }; static const uptr kNumberOfSizeClasses = 255; struct AsanChunk; -void InitializeAllocator(); -void ReInitializeAllocator(); +struct AllocatorOptions { + u32 quarantine_size_mb; + u16 min_redzone; + u16 max_redzone; + u8 may_return_null; + u8 alloc_dealloc_mismatch; + void SetFrom(const Flags *f, const CommonFlags *cf); + void CopyTo(Flags *f, CommonFlags *cf); +}; + +void InitializeAllocator(const AllocatorOptions &options); +void ReInitializeAllocator(const AllocatorOptions &options); +void GetAllocatorOptions(AllocatorOptions *options); + class AsanChunkView { public: explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {} bool IsValid(); // Checks if AsanChunkView points to a valid allocated // or quarantined chunk. uptr Beg(); // First byte of user memory. uptr End(); // Last byte of user memory. uptr UsedSize(); // Size requested by the user. uptr AllocTid(); uptr FreeTid(); bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; } StackTrace GetAllocStack(); StackTrace GetFreeStack(); bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) { if (addr >= Beg() && (addr + access_size) <= End()) { *offset = addr - Beg(); return true; } return false; } bool AddrIsAtLeft(uptr addr, uptr access_size, sptr *offset) { (void)access_size; if (addr < Beg()) { *offset = Beg() - addr; return true; } return false; } bool AddrIsAtRight(uptr addr, uptr access_size, sptr *offset) { if (addr + access_size > End()) { *offset = addr - End(); return true; } return false; } private: AsanChunk *const chunk_; }; AsanChunkView FindHeapChunkByAddress(uptr address); // List of AsanChunks with total size. class AsanChunkFifoList: public IntrusiveList { public: explicit AsanChunkFifoList(LinkerInitialized) { } AsanChunkFifoList() { clear(); } void Push(AsanChunk *n); void PushList(AsanChunkFifoList *q); AsanChunk *Pop(); uptr size() { return size_; } void clear() { IntrusiveList::clear(); size_ = 0; } private: uptr size_; }; struct AsanMapUnmapCallback { void OnMap(uptr p, uptr size) const; void OnUnmap(uptr p, uptr size) const; }; #if SANITIZER_CAN_USE_ALLOCATOR64 # if defined(__powerpc64__) const uptr kAllocatorSpace = 0xa0000000000ULL; const uptr kAllocatorSize = 0x20000000000ULL; // 2T. # else const uptr kAllocatorSpace = 0x600000000000ULL; const uptr kAllocatorSize = 0x40000000000ULL; // 4T. # endif typedef DefaultSizeClassMap SizeClassMap; typedef SizeClassAllocator64 PrimaryAllocator; #else // Fallback to SizeClassAllocator32. static const uptr kRegionSizeLog = 20; static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; # if SANITIZER_WORDSIZE == 32 typedef FlatByteMap ByteMap; # elif SANITIZER_WORDSIZE == 64 typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; # endif typedef CompactSizeClassMap SizeClassMap; typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, 16, SizeClassMap, kRegionSizeLog, ByteMap, AsanMapUnmapCallback> PrimaryAllocator; #endif // SANITIZER_CAN_USE_ALLOCATOR64 typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator SecondaryAllocator; typedef CombinedAllocator Allocator; + SecondaryAllocator> AsanAllocator; struct AsanThreadLocalMallocStorage { uptr quarantine_cache[16]; - AllocatorCache allocator2_cache; + AllocatorCache allocator_cache; void CommitBack(); private: // These objects are allocated via mmap() and are zero-initialized. AsanThreadLocalMallocStorage() {} }; void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, AllocType alloc_type); void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type); void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack, AllocType alloc_type); void *asan_malloc(uptr size, BufferedStackTrace *stack); void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack); void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack); void *asan_valloc(uptr size, BufferedStackTrace *stack); void *asan_pvalloc(uptr size, BufferedStackTrace *stack); int asan_posix_memalign(void **memptr, uptr alignment, uptr size, BufferedStackTrace *stack); uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp); uptr asan_mz_size(const void *ptr); void asan_mz_force_lock(); void asan_mz_force_unlock(); void PrintInternalAllocatorStats(); +void AsanSoftRssLimitExceededCallback(bool exceeded); } // namespace __asan #endif // ASAN_ALLOCATOR_H Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_debugging.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_debugging.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_debugging.cc (revision 279191) @@ -1,141 +1,141 @@ //===-- asan_debugging.cc -------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // This file contains various functions that are generally useful to call when // using a debugger (LLDB, GDB). //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_flags.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_report.h" #include "asan_thread.h" namespace __asan { void GetInfoForStackVar(uptr addr, AddressDescription *descr, AsanThread *t) { descr->name[0] = 0; descr->region_address = 0; descr->region_size = 0; descr->region_kind = "stack"; AsanThread::StackFrameAccess access; if (!t->GetStackFrameAccessByAddr(addr, &access)) return; InternalMmapVector vars(16); if (!ParseFrameDescription(access.frame_descr, &vars)) { return; } for (uptr i = 0; i < vars.size(); i++) { if (access.offset <= vars[i].beg + vars[i].size) { internal_strncat(descr->name, vars[i].name_pos, Min(descr->name_size, vars[i].name_len)); descr->region_address = addr - (access.offset - vars[i].beg); descr->region_size = vars[i].size; return; } } } void GetInfoForHeapAddress(uptr addr, AddressDescription *descr) { AsanChunkView chunk = FindHeapChunkByAddress(addr); descr->name[0] = 0; descr->region_address = 0; descr->region_size = 0; if (!chunk.IsValid()) { descr->region_kind = "heap-invalid"; return; } descr->region_address = chunk.Beg(); descr->region_size = chunk.UsedSize(); descr->region_kind = "heap"; } void AsanLocateAddress(uptr addr, AddressDescription *descr) { if (DescribeAddressIfShadow(addr, descr, /* print */ false)) { return; } if (GetInfoForAddressIfGlobal(addr, descr)) { return; } asanThreadRegistry().Lock(); AsanThread *thread = FindThreadByStackAddress(addr); asanThreadRegistry().Unlock(); if (thread) { GetInfoForStackVar(addr, descr, thread); return; } GetInfoForHeapAddress(addr, descr); } -uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id, - bool alloc_stack) { +static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id, + bool alloc_stack) { AsanChunkView chunk = FindHeapChunkByAddress(addr); if (!chunk.IsValid()) return 0; StackTrace stack(nullptr, 0); if (alloc_stack) { if (chunk.AllocTid() == kInvalidTid) return 0; stack = chunk.GetAllocStack(); if (thread_id) *thread_id = chunk.AllocTid(); } else { if (chunk.FreeTid() == kInvalidTid) return 0; stack = chunk.GetFreeStack(); if (thread_id) *thread_id = chunk.FreeTid(); } if (trace && size) { size = Min(size, Min(stack.size, kStackTraceMax)); for (uptr i = 0; i < size; i++) trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]); return size; } return 0; } } // namespace __asan using namespace __asan; SANITIZER_INTERFACE_ATTRIBUTE const char *__asan_locate_address(uptr addr, char *name, uptr name_size, uptr *region_address, uptr *region_size) { AddressDescription descr = { name, name_size, 0, 0, 0 }; AsanLocateAddress(addr, &descr); if (region_address) *region_address = descr.region_address; if (region_size) *region_size = descr.region_size; return descr.region_kind; } SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) { return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true); } SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) { return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ false); } SANITIZER_INTERFACE_ATTRIBUTE void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset) { if (shadow_scale) *shadow_scale = SHADOW_SCALE; if (shadow_offset) *shadow_offset = SHADOW_OFFSET; } Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_fake_stack.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_fake_stack.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_fake_stack.cc (revision 279191) @@ -1,257 +1,256 @@ //===-- asan_fake_stack.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // FakeStack is used to detect use-after-return bugs. //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_poisoning.h" #include "asan_thread.h" namespace __asan { static const u64 kMagic1 = kAsanStackAfterReturnMagic; static const u64 kMagic2 = (kMagic1 << 8) | kMagic1; static const u64 kMagic4 = (kMagic2 << 16) | kMagic2; static const u64 kMagic8 = (kMagic4 << 32) | kMagic4; // 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. u64 *shadow = reinterpret_cast(MemToShadow(ptr)); if (class_id <= 6) { for (uptr i = 0; i < (1U << class_id); i++) { shadow[i] = magic; SanitizerBreakOptimization(0); // Make sure this does not become memset. } } else { // The size class is too big, it's cheaper to poison only size bytes. PoisonShadow(ptr, size, static_cast(magic)); } } FakeStack *FakeStack::Create(uptr stack_size_log) { static uptr kMinStackSizeLog = 16; static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28); if (stack_size_log < kMinStackSizeLog) stack_size_log = kMinStackSizeLog; if (stack_size_log > kMaxStackSizeLog) stack_size_log = kMaxStackSizeLog; uptr size = RequiredSize(stack_size_log); FakeStack *res = reinterpret_cast( flags()->uar_noreserve ? MmapNoReserveOrDie(size, "FakeStack") : MmapOrDie(size, "FakeStack")); res->stack_size_log_ = stack_size_log; u8 *p = reinterpret_cast(res); VReport(1, "T%d: FakeStack created: %p -- %p stack_size_log: %zd; " "mmapped %zdK, noreserve=%d \n", GetCurrentTidOrInvalid(), p, p + FakeStack::RequiredSize(stack_size_log), stack_size_log, size >> 10, flags()->uar_noreserve); return res; } void FakeStack::Destroy(int tid) { PoisonAll(0); - if (common_flags()->verbosity >= 2) { + if (Verbosity() >= 2) { InternalScopedString str(kNumberOfSizeClasses * 50); for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) str.append("%zd: %zd/%zd; ", class_id, hint_position_[class_id], NumberOfFrames(stack_size_log(), class_id)); Report("T%d: FakeStack destroyed: %s\n", tid, str.data()); } uptr size = RequiredSize(stack_size_log_); FlushUnneededASanShadowMemory(reinterpret_cast(this), size); UnmapOrDie(this, size); } void FakeStack::PoisonAll(u8 magic) { PoisonShadow(reinterpret_cast(this), RequiredSize(stack_size_log()), magic); } ALWAYS_INLINE USED FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, uptr real_stack) { CHECK_LT(class_id, kNumberOfSizeClasses); if (needs_gc_) GC(real_stack); uptr &hint_position = hint_position_[class_id]; const int num_iter = NumberOfFrames(stack_size_log, class_id); u8 *flags = GetFlags(stack_size_log, class_id); for (int i = 0; i < num_iter; i++) { uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++); // This part is tricky. On one hand, checking and setting flags[pos] // should be atomic to ensure async-signal safety. But on the other hand, // if the signal arrives between checking and setting flags[pos], the // signal handler's fake stack will start from a different hint_position // and so will not touch this particular byte. So, it is safe to do this // with regular non-atimic load and store (at least I was not able to make // this code crash). if (flags[pos]) continue; flags[pos] = 1; FakeFrame *res = reinterpret_cast( GetFrame(stack_size_log, class_id, pos)); res->real_stack = real_stack; *SavedFlagPtr(reinterpret_cast(res), class_id) = &flags[pos]; return res; } return 0; // We are out of fake stack. } uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr *frame_beg, uptr *frame_end) { uptr stack_size_log = this->stack_size_log(); uptr beg = reinterpret_cast(GetFrame(stack_size_log, 0, 0)); uptr end = reinterpret_cast(this) + RequiredSize(stack_size_log); if (ptr < beg || ptr >= end) return 0; uptr class_id = (ptr - beg) >> stack_size_log; uptr base = beg + (class_id << stack_size_log); CHECK_LE(base, ptr); CHECK_LT(ptr, base + (1UL << stack_size_log)); uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id); uptr res = base + pos * BytesInSizeClass(class_id); *frame_end = res + BytesInSizeClass(class_id); *frame_beg = res + sizeof(FakeFrame); return res; } void FakeStack::HandleNoReturn() { needs_gc_ = true; } // When throw, longjmp or some such happens we don't call OnFree() and // as the result may leak one or more fake frames, but the good news is that // we are notified about all such events by HandleNoReturn(). // If we recently had such no-return event we need to collect garbage frames. // We do it based on their 'real_stack' values -- everything that is lower // than the current real_stack is garbage. NOINLINE void FakeStack::GC(uptr real_stack) { uptr collected = 0; for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { u8 *flags = GetFlags(stack_size_log(), class_id); for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; i++) { if (flags[i] == 0) continue; // not allocated. FakeFrame *ff = reinterpret_cast( GetFrame(stack_size_log(), class_id, i)); if (ff->real_stack < real_stack) { flags[i] = 0; collected++; } } } needs_gc_ = false; } void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) { for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { u8 *flags = GetFlags(stack_size_log(), class_id); for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; i++) { if (flags[i] == 0) continue; // not allocated. FakeFrame *ff = reinterpret_cast( GetFrame(stack_size_log(), class_id, i)); uptr begin = reinterpret_cast(ff); callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg); } } } #if SANITIZER_LINUX && !SANITIZER_ANDROID static THREADLOCAL FakeStack *fake_stack_tls; FakeStack *GetTLSFakeStack() { return fake_stack_tls; } void SetTLSFakeStack(FakeStack *fs) { fake_stack_tls = fs; } #else FakeStack *GetTLSFakeStack() { return 0; } void SetTLSFakeStack(FakeStack *fs) { } #endif // SANITIZER_LINUX && !SANITIZER_ANDROID static FakeStack *GetFakeStack() { AsanThread *t = GetCurrentThread(); if (!t) return 0; return t->fake_stack(); } static FakeStack *GetFakeStackFast() { if (FakeStack *fs = GetTLSFakeStack()) return fs; if (!__asan_option_detect_stack_use_after_return) return 0; return GetFakeStack(); } -ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) { +ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) { FakeStack *fs = GetFakeStackFast(); - if (!fs) return real_stack; + if (!fs) return 0; + uptr local_stack; + uptr real_stack = reinterpret_cast(&local_stack); FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack); - if (!ff) - return real_stack; // Out of fake stack, return the real one. + if (!ff) return 0; // Out of fake stack. uptr ptr = reinterpret_cast(ff); SetShadow(ptr, size, class_id, 0); return ptr; } -ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) { - if (ptr == real_stack) - return; +ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) { FakeStack::Deallocate(ptr, class_id); SetShadow(ptr, size, class_id, kMagic8); } } // namespace __asan // ---------------------- Interface ---------------- {{{1 using namespace __asan; #define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \ extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \ - __asan_stack_malloc_##class_id(uptr size, uptr real_stack) { \ - return OnMalloc(class_id, size, real_stack); \ + __asan_stack_malloc_##class_id(uptr size) { \ + return OnMalloc(class_id, size); \ } \ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \ - uptr ptr, uptr size, uptr real_stack) { \ - OnFree(ptr, class_id, size, real_stack); \ + uptr ptr, uptr size) { \ + OnFree(ptr, class_id, size); \ } DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0) DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1) DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2) DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3) DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4) DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5) DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6) DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7) DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8) DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9) DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10) extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void *__asan_get_current_fake_stack() { return GetFakeStackFast(); } SANITIZER_INTERFACE_ATTRIBUTE void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, void **end) { FakeStack *fs = reinterpret_cast(fake_stack); if (!fs) return 0; uptr frame_beg, frame_end; FakeFrame *frame = reinterpret_cast(fs->AddrIsInFakeStack( reinterpret_cast(addr), &frame_beg, &frame_end)); if (!frame) return 0; if (frame->magic != kCurrentStackFrameMagic) return 0; if (beg) *beg = reinterpret_cast(frame_beg); if (end) *end = reinterpret_cast(frame_end); return reinterpret_cast(frame->real_stack); } } // extern "C" Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.cc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.cc (revision 279191) @@ -0,0 +1,141 @@ +//===-- asan_flags.cc -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan flag parsing logic. +//===----------------------------------------------------------------------===// + +#include "asan_activation.h" +#include "asan_flags.h" +#include "asan_interface_internal.h" +#include "asan_stack.h" +#include "lsan/lsan_common.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" + +namespace __asan { + +Flags asan_flags_dont_use_directly; // use via flags(). + +static const char *MaybeCallAsanDefaultOptions() { + return (&__asan_default_options) ? __asan_default_options() : ""; +} + +static const char *MaybeUseAsanDefaultOptionsCompileDefinition() { +#ifdef ASAN_DEFAULT_OPTIONS +// Stringize the macro value. +# define ASAN_STRINGIZE(x) #x +# define ASAN_STRINGIZE_OPTIONS(options) ASAN_STRINGIZE(options) + return ASAN_STRINGIZE_OPTIONS(ASAN_DEFAULT_OPTIONS); +#else + return ""; +#endif +} + +void Flags::SetDefaults() { +#define ASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "asan_flags.inc" +#undef ASAN_FLAG +} + +void RegisterAsanFlags(FlagParser *parser, Flags *f) { +#define ASAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "asan_flags.inc" +#undef ASAN_FLAG +} + +void InitializeFlags(Flags *f) { + FlagParser parser; + RegisterAsanFlags(&parser, f); + RegisterCommonFlags(&parser); + + SetCommonFlagsDefaults(); + { + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.detect_leaks = CAN_SANITIZE_LEAKS; + cf.external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); + cf.malloc_context_size = kDefaultMallocContextSize; + cf.intercept_tls_get_addr = true; + OverrideCommonFlags(cf); + } + + const int kDefaultQuarantineSizeMb = (ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8; + f->SetDefaults(); + + // Override from compile definition. + const char *compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); + parser.ParseString(compile_def); + + // Override from user-specified string. + const char *default_options = MaybeCallAsanDefaultOptions(); + parser.ParseString(default_options); + + // Override from command line. + const char *env = GetEnv("ASAN_OPTIONS"); + if (env) parser.ParseString(env); + + // Let activation flags override current settings. On Android they come + // from a system property. On other platforms this is no-op. + if (!flags()->start_deactivated) { + char buf[100]; + GetExtraActivationFlags(buf, sizeof(buf)); + parser.ParseString(buf); + } + + SetVerbosity(common_flags()->verbosity); + + // TODO(eugenis): dump all flags at verbosity>=2? + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) parser.PrintFlagDescriptions(); + + // Flag validation: + if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) { + Report("%s: detect_leaks is not supported on this platform.\n", + SanitizerToolName); + Die(); + } + // Make "strict_init_order" imply "check_initialization_order". + // TODO(samsonov): Use a single runtime flag for an init-order checker. + if (f->strict_init_order) { + f->check_initialization_order = true; + } + CHECK_LE((uptr)common_flags()->malloc_context_size, kStackTraceMax); + CHECK_LE(f->min_uar_stack_size_log, f->max_uar_stack_size_log); + CHECK_GE(f->redzone, 16); + CHECK_GE(f->max_redzone, f->redzone); + CHECK_LE(f->max_redzone, 2048); + CHECK(IsPowerOfTwo(f->redzone)); + CHECK(IsPowerOfTwo(f->max_redzone)); + + // quarantine_size is deprecated but we still honor it. + // quarantine_size can not be used together with quarantine_size_mb. + if (f->quarantine_size >= 0 && f->quarantine_size_mb >= 0) { + Report("%s: please use either 'quarantine_size' (deprecated) or " + "quarantine_size_mb, but not both\n", SanitizerToolName); + Die(); + } + if (f->quarantine_size >= 0) + f->quarantine_size_mb = f->quarantine_size >> 20; + if (f->quarantine_size_mb < 0) + f->quarantine_size_mb = kDefaultQuarantineSizeMb; +} + +} // namespace __asan + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +const char* __asan_default_options() { return ""; } +} // extern "C" +#endif Property changes on: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.h (revision 279191) @@ -1,79 +1,49 @@ //===-- asan_flags.h -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // ASan runtime flags. //===----------------------------------------------------------------------===// #ifndef ASAN_FLAGS_H #define ASAN_FLAGS_H #include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_flag_parser.h" // ASan flag values can be defined in four ways: // 1) initialized with default values at startup. // 2) overriden during compilation of ASan runtime by providing // compile definition ASAN_DEFAULT_OPTIONS. // 3) overriden from string returned by user-specified function // __asan_default_options(). // 4) overriden from env variable ASAN_OPTIONS. +// 5) overriden during ASan activation (for now used on Android only). namespace __asan { struct Flags { - // Flag descriptions are in asan_rtl.cc. - int quarantine_size; - int redzone; - int max_redzone; - bool debug; - int report_globals; - bool check_initialization_order; - bool replace_str; - bool replace_intrin; - bool mac_ignore_invalid_free; - bool detect_stack_use_after_return; - int min_uar_stack_size_log; - int max_uar_stack_size_log; - bool uar_noreserve; - int max_malloc_fill_size, malloc_fill_byte; - int exitcode; - bool allow_user_poisoning; - int sleep_before_dying; - bool check_malloc_usable_size; - bool unmap_shadow_on_exit; - bool abort_on_error; - bool print_stats; - bool print_legend; - bool atexit; - bool allow_reexec; - bool print_full_thread_history; - bool poison_heap; - bool poison_partial; - bool poison_array_cookie; - bool alloc_dealloc_mismatch; - bool new_delete_type_mismatch; - bool strict_memcmp; - bool strict_init_order; - bool start_deactivated; - int detect_invalid_pointer_pairs; - bool detect_container_overflow; - int detect_odr_violation; - bool dump_instruction_bytes; +#define ASAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "asan_flags.inc" +#undef ASAN_FLAG + + void SetDefaults(); }; extern Flags asan_flags_dont_use_directly; inline Flags *flags() { return &asan_flags_dont_use_directly; } -void InitializeFlags(Flags *f, const char *env); +void RegisterAsanFlags(FlagParser *parser, Flags *f); +void InitializeFlags(Flags *f); } // namespace __asan #endif // ASAN_FLAGS_H Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.inc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.inc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.inc (revision 279191) @@ -0,0 +1,144 @@ +//===-- asan_flags.inc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// ASan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef ASAN_FLAG +# error "Define ASAN_FLAG prior to including this file!" +#endif + +// ASAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +ASAN_FLAG(int, quarantine_size, -1, + "Deprecated, please use quarantine_size_mb.") +ASAN_FLAG(int, quarantine_size_mb, -1, + "Size (in Mb) of quarantine used to detect use-after-free " + "errors. Lower value may reduce memory usage but increase the " + "chance of false negatives.") +ASAN_FLAG(int, redzone, 16, + "Minimal size (in bytes) of redzones around heap objects. " + "Requirement: redzone >= 16, is a power of two.") +ASAN_FLAG(int, max_redzone, 2048, + "Maximal size (in bytes) of redzones around heap objects.") +ASAN_FLAG( + bool, debug, false, + "If set, prints some debugging information and does additional checks.") +ASAN_FLAG( + int, report_globals, 1, + "Controls the way to handle globals (0 - don't detect buffer overflow on " + "globals, 1 - detect buffer overflow, 2 - print data about registered " + "globals).") +ASAN_FLAG(bool, check_initialization_order, false, + "If set, attempts to catch initialization order issues.") +ASAN_FLAG( + bool, replace_str, true, + "If set, uses custom wrappers and replacements for libc string functions " + "to find more errors.") +ASAN_FLAG(bool, replace_intrin, true, + "If set, uses custom wrappers for memset/memcpy/memmove intinsics.") +ASAN_FLAG(bool, mac_ignore_invalid_free, false, + "Ignore invalid free() calls to work around some bugs. Used on OS X " + "only.") +ASAN_FLAG(bool, detect_stack_use_after_return, false, + "Enables stack-use-after-return checking at run-time.") +ASAN_FLAG(int, min_uar_stack_size_log, 16, // We can't do smaller anyway. + "Minimum fake stack size log.") +ASAN_FLAG(int, max_uar_stack_size_log, + 20, // 1Mb per size class, i.e. ~11Mb per thread + "Maximum fake stack size log.") +ASAN_FLAG(bool, uar_noreserve, false, + "Use mmap with 'noreserve' flag to allocate fake stack.") +ASAN_FLAG( + int, max_malloc_fill_size, 0x1000, // By default, fill only the first 4K. + "ASan allocator flag. max_malloc_fill_size is the maximal amount of " + "bytes that will be filled with malloc_fill_byte on malloc.") +ASAN_FLAG(int, malloc_fill_byte, 0xbe, + "Value used to fill the newly allocated memory.") +ASAN_FLAG(int, exitcode, ASAN_DEFAULT_FAILURE_EXITCODE, + "Override the program exit status if the tool found an error.") +ASAN_FLAG(bool, allow_user_poisoning, true, + "If set, user may manually mark memory regions as poisoned or " + "unpoisoned.") +ASAN_FLAG( + int, sleep_before_dying, 0, + "Number of seconds to sleep between printing an error report and " + "terminating the program. Useful for debugging purposes (e.g. when one " + "needs to attach gdb).") +ASAN_FLAG(bool, check_malloc_usable_size, true, + "Allows the users to work around the bug in Nvidia drivers prior to " + "295.*.") +ASAN_FLAG(bool, unmap_shadow_on_exit, false, + "If set, explicitly unmaps the (huge) shadow at exit.") +ASAN_FLAG( + bool, abort_on_error, false, + "If set, the tool calls abort() instead of _exit() after printing the " + "error report.") +ASAN_FLAG(bool, print_stats, false, + "Print various statistics after printing an error message or if " + "atexit=1.") +ASAN_FLAG(bool, print_legend, true, "Print the legend for the shadow bytes.") +ASAN_FLAG(bool, atexit, false, + "If set, prints ASan exit stats even after program terminates " + "successfully.") +ASAN_FLAG( + bool, print_full_thread_history, true, + "If set, prints thread creation stacks for the threads involved in the " + "report and their ancestors up to the main thread.") +ASAN_FLAG( + bool, poison_heap, true, + "Poison (or not) the heap memory on [de]allocation. Zero value is useful " + "for benchmarking the allocator or instrumentator.") +ASAN_FLAG(bool, poison_partial, true, + "If true, poison partially addressable 8-byte aligned words " + "(default=true). This flag affects heap and global buffers, but not " + "stack buffers.") +ASAN_FLAG(bool, poison_array_cookie, true, + "Poison (or not) the array cookie after operator new[].") + +// Turn off alloc/dealloc mismatch checker on Mac and Windows for now. +// https://code.google.com/p/address-sanitizer/issues/detail?id=131 +// https://code.google.com/p/address-sanitizer/issues/detail?id=309 +// TODO(glider,timurrrr): Fix known issues and enable this back. +ASAN_FLAG(bool, alloc_dealloc_mismatch, + (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0), + "Report errors on malloc/delete, new/free, new/delete[], etc.") + +ASAN_FLAG(bool, new_delete_type_mismatch, true, + "Report errors on mismatch betwen size of new and delete.") +ASAN_FLAG(bool, strict_memcmp, true, + "If true, assume that memcmp(p1, p2, n) always reads n bytes before " + "comparing p1 and p2.") +ASAN_FLAG( + bool, strict_init_order, false, + "If true, assume that dynamic initializers can never access globals from " + "other modules, even if the latter are already initialized.") +ASAN_FLAG( + bool, start_deactivated, false, + "If true, ASan tweaks a bunch of other flags (quarantine, redzone, heap " + "poisoning) to reduce memory consumption as much as possible, and " + "restores them to original values when the first instrumented module is " + "loaded into the process. This is mainly intended to be used on " + "Android. ") +ASAN_FLAG( + int, detect_invalid_pointer_pairs, 0, + "If non-zero, try to detect operations like <, <=, >, >= and - on " + "invalid pointer pairs (e.g. when pointers belong to different objects). " + "The bigger the value the harder we try.") +ASAN_FLAG( + bool, detect_container_overflow, true, + "If true, honor the container overflow annotations. " + "See https://code.google.com/p/address-sanitizer/wiki/ContainerOverflow") +ASAN_FLAG(int, detect_odr_violation, 2, + "If >=2, detect violation of One-Definition-Rule (ODR); " + "If ==1, detect ODR-violation only if the two variables " + "have different sizes") +ASAN_FLAG(bool, dump_instruction_bytes, false, + "If true, dump 16 bytes starting at the instruction that caused SEGV") Property changes on: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_flags.inc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_globals.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_globals.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_globals.cc (revision 279191) @@ -1,293 +1,293 @@ //===-- asan_globals.cc ---------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Handle globals. //===----------------------------------------------------------------------===// #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stackdepot.h" namespace __asan { typedef __asan_global Global; struct ListOfGlobals { const Global *g; ListOfGlobals *next; }; static BlockingMutex mu_for_globals(LINKER_INITIALIZED); static LowLevelAllocator allocator_for_globals; static ListOfGlobals *list_of_all_globals; static const int kDynamicInitGlobalsInitialCapacity = 512; struct DynInitGlobal { Global g; bool initialized; }; typedef InternalMmapVector VectorOfGlobals; // Lazy-initialized and never deleted. static VectorOfGlobals *dynamic_init_globals; // We want to remember where a certain range of globals was registered. struct GlobalRegistrationSite { u32 stack_id; Global *g_first, *g_last; }; typedef InternalMmapVector GlobalRegistrationSiteVector; static GlobalRegistrationSiteVector *global_registration_site_vector; ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { FastPoisonShadow(g->beg, g->size_with_redzone, value); } ALWAYS_INLINE void PoisonRedZones(const Global &g) { uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, kAsanGlobalRedzoneMagic); if (g.size != aligned_size) { FastPoisonShadowPartialRightRedzone( g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), g.size % SHADOW_GRANULARITY, SHADOW_GRANULARITY, kAsanGlobalRedzoneMagic); } } const uptr kMinimalDistanceFromAnotherGlobal = 64; 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; } static void ReportGlobal(const Global &g, const char *prefix) { Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n", prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name, g.module_name, g.has_dynamic_init); if (g.location) { Report(" location (%p): name=%s[%p], %d %d\n", g.location, g.location->filename, g.location->filename, g.location->line_no, g.location->column_no); } } 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) { 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]; if (g >= grs.g_first && g <= grs.g_last) return grs.stack_id; } return 0; } // Register a global variable. // This function may be called more than once for every global // so we store the globals in a map. static void RegisterGlobal(const Global *g) { CHECK(asan_inited); if (flags()->report_globals >= 2) ReportGlobal(*g, "Added"); CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); if (flags()->detect_odr_violation) { // Try detecting ODR (One Definition Rule) violation, i.e. the situation // where two globals with the same name are defined in different modules. if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) { // This check may not be enough: if the first global is much larger // 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)) ReportODRViolation(g, FindRegistrationSite(g), l->g, FindRegistrationSite(l->g)); } } } - if (flags()->poison_heap) + if (CanPoisonMemory()) PoisonRedZones(*g); ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals; l->g = g; l->next = list_of_all_globals; list_of_all_globals = l; if (g->has_dynamic_init) { if (dynamic_init_globals == 0) { dynamic_init_globals = new(allocator_for_globals) VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); } DynInitGlobal dyn_global = { *g, false }; dynamic_init_globals->push_back(dyn_global); } } static void UnregisterGlobal(const Global *g) { CHECK(asan_inited); CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - if (flags()->poison_heap) + if (CanPoisonMemory()) PoisonShadowForGlobal(g, 0); // We unpoison the shadow memory for the global but we do not remove it from // the list because that would require O(n^2) time with the current list // implementation. It might not be worth doing anyway. } void StopInitOrderChecking() { BlockingMutexLock lock(&mu_for_globals); if (!flags()->check_initialization_order || !dynamic_init_globals) return; flags()->check_initialization_order = false; for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; const Global *g = &dyn_g.g; // Unpoison the whole global. PoisonShadowForGlobal(g, 0); // Poison redzones back. PoisonRedZones(*g); } } } // namespace __asan // ---------------------- Interface ---------------- {{{1 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; u32 stack_id = StackDepotPut(stack); BlockingMutexLock lock(&mu_for_globals); if (!global_registration_site_vector) global_registration_site_vector = new(allocator_for_globals) GlobalRegistrationSiteVector(128); GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]}; global_registration_site_vector->push_back(site); if (flags()->report_globals >= 2) { PRINT_CURRENT_STACK(); Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]); } for (uptr i = 0; i < n; i++) { RegisterGlobal(&globals[i]); } } // Unregister an array of globals. // We must do this when a shared objects gets dlclosed. void __asan_unregister_globals(__asan_global *globals, uptr n) { if (!flags()->report_globals) return; BlockingMutexLock lock(&mu_for_globals); for (uptr i = 0; i < n; i++) { UnregisterGlobal(&globals[i]); } } // This method runs immediately prior to dynamic initialization in each TU, // when all dynamically initialized globals are unpoisoned. This method // poisons all global variables not defined in this TU, so that a dynamic // initializer can only touch global variables in the same TU. void __asan_before_dynamic_init(const char *module_name) { if (!flags()->check_initialization_order || - !flags()->poison_heap) + !CanPoisonMemory()) return; bool strict_init_order = flags()->strict_init_order; CHECK(dynamic_init_globals); CHECK(module_name); CHECK(asan_inited); BlockingMutexLock lock(&mu_for_globals); if (flags()->report_globals >= 3) Printf("DynInitPoison module: %s\n", module_name); for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; const Global *g = &dyn_g.g; if (dyn_g.initialized) continue; if (g->module_name != module_name) PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); else if (!strict_init_order) dyn_g.initialized = true; } } // This method runs immediately after dynamic initialization in each TU, when // all dynamically initialized globals except for those defined in the current // TU are poisoned. It simply unpoisons all dynamically initialized globals. void __asan_after_dynamic_init() { if (!flags()->check_initialization_order || - !flags()->poison_heap) + !CanPoisonMemory()) return; CHECK(asan_inited); BlockingMutexLock lock(&mu_for_globals); // FIXME: Optionally report that we're unpoisoning globals from a module. for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; const Global *g = &dyn_g.g; if (!dyn_g.initialized) { // Unpoison the whole global. PoisonShadowForGlobal(g, 0); // Poison redzones back. PoisonRedZones(*g); } } } Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_init_version.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_init_version.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_init_version.h (revision 279191) @@ -1,32 +1,34 @@ //===-- asan_init_version.h -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // This header defines a versioned __asan_init function to be called at the // startup of the instrumented program. //===----------------------------------------------------------------------===// #ifndef ASAN_INIT_VERSION_H #define ASAN_INIT_VERSION_H extern "C" { // Every time the ASan ABI changes we also change the version number in the // __asan_init function name. Objects built with incompatible ASan ABI // versions will not link with run-time. // Changes between ABI versions: // v1=>v2: added 'module_name' to __asan_global // v2=>v3: stack frame description (created by the compiler) // contains the function PC as the 3-rd field (see // DescribeAddressIfStack). // v3=>v4: added '__asan_global_source_location' to __asan_global. - #define __asan_init __asan_init_v4 - #define __asan_init_name "__asan_init_v4" + // v4=>v5: changed the semantics and format of __asan_stack_malloc_ and + // __asan_stack_free_ functions. + #define __asan_init __asan_init_v5 + #define __asan_init_name "__asan_init_v5" } #endif // ASAN_INIT_VERSION_H Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_interceptors.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_interceptors.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_interceptors.cc (revision 279191) @@ -1,924 +1,930 @@ //===-- asan_interceptors.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Intercept various libc functions. //===----------------------------------------------------------------------===// #include "asan_interceptors.h" #include "asan_allocator.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_suppressions.h" #include "sanitizer_common/sanitizer_libc.h" namespace __asan { // Return true if we can quickly decide that the region is unpoisoned. static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) { if (size == 0) return true; if (size <= 32) return !AddressIsPoisoned(beg) && !AddressIsPoisoned(beg + size - 1) && !AddressIsPoisoned(beg + size / 2); return false; } struct AsanInterceptorContext { const char *interceptor_name; }; // We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE, // and ASAN_WRITE_RANGE as macro instead of function so // that no extra frames are created, and stack trace contains // relevant information only. // We check all shadow bytes. #define ACCESS_MEMORY_RANGE(ctx, offset, size, isWrite) do { \ uptr __offset = (uptr)(offset); \ uptr __size = (uptr)(size); \ uptr __bad = 0; \ if (__offset > __offset + __size) { \ GET_STACK_TRACE_FATAL_HERE; \ ReportStringFunctionSizeOverflow(__offset, __size, &stack); \ } \ if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \ (__bad = __asan_region_is_poisoned(__offset, __size))) { \ AsanInterceptorContext *_ctx = (AsanInterceptorContext *)ctx; \ bool suppressed = false; \ if (_ctx) { \ suppressed = IsInterceptorSuppressed(_ctx->interceptor_name); \ if (!suppressed && HaveStackTraceBasedSuppressions()) { \ GET_STACK_TRACE_FATAL_HERE; \ suppressed = IsStackTraceSuppressed(&stack); \ } \ } \ if (!suppressed) { \ GET_CURRENT_PC_BP_SP; \ __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \ } \ } \ } while (0) #define ASAN_READ_RANGE(ctx, offset, size) \ ACCESS_MEMORY_RANGE(ctx, offset, size, false) #define ASAN_WRITE_RANGE(ctx, offset, size) \ ACCESS_MEMORY_RANGE(ctx, offset, size, true) // 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. static inline bool RangesOverlap(const char *offset1, uptr length1, const char *offset2, uptr length2) { return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1)); } #define CHECK_RANGES_OVERLAP(name, _offset1, length1, _offset2, length2) do { \ const char *offset1 = (const char*)_offset1; \ const char *offset2 = (const char*)_offset2; \ if (RangesOverlap(offset1, length1, offset2, length2)) { \ GET_STACK_TRACE_FATAL_HERE; \ ReportStringFunctionMemoryRangesOverlap(name, offset1, length1, \ offset2, length2, &stack); \ } \ } while (0) static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { #if ASAN_INTERCEPT_STRNLEN if (REAL(strnlen) != 0) { return REAL(strnlen)(s, maxlen); } #endif return internal_strnlen(s, maxlen); } void SetThreadName(const char *name) { AsanThread *t = GetCurrentThread(); if (t) asanThreadRegistry().SetThreadName(t->tid(), name); } int OnExit() { // FIXME: ask frontend whether we need to return failure. return 0; } } // namespace __asan // ---------------------- Wrappers ---------------- {{{1 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; \ (void) ctx; \ #define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ ASAN_WRITE_RANGE(ctx, ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ ASAN_READ_RANGE(ctx, ptr, size) #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + ASAN_INTERCEPTOR_ENTER(ctx, func); \ do { \ if (asan_init_is_running) \ return REAL(func)(__VA_ARGS__); \ - ASAN_INTERCEPTOR_ENTER(ctx, func); \ if (SANITIZER_MAC && UNLIKELY(!asan_inited)) \ return REAL(func)(__VA_ARGS__); \ ENSURE_ASAN_INITED(); \ } while (false) +#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ + do { \ + } while (false) #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name) // Should be asanThreadRegistry().SetThreadNameByUserId(thread, name) // But asan does not remember UserId's for threads (pthread_t); // and remembers all ever existed threads, so the linear search by UserId // can be slow. #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ do { \ } while (false) #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() -#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res) CovUpdateMapping() -#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CovUpdateMapping() +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ + CoverageUpdateMapping() +#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CoverageUpdateMapping() #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited) #include "sanitizer_common/sanitizer_common_interceptors.inc" // Syscall interceptors don't have contexts, we don't support suppressions // for them. #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(nullptr, p, s) #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(nullptr, p, s) #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ do { \ (void)(p); \ (void)(s); \ } while (false) #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ do { \ (void)(p); \ (void)(s); \ } while (false) #include "sanitizer_common/sanitizer_common_syscalls.inc" struct ThreadStartParam { atomic_uintptr_t t; atomic_uintptr_t is_registered; }; 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(arg); AsanThread *t = nullptr; while ((t = reinterpret_cast( atomic_load(¶m->t, memory_order_acquire))) == 0) internal_sched_yield(); SetCurrentThread(t); return t->ThreadStart(GetTid(), ¶m->is_registered); +#endif } #if ASAN_INTERCEPT_PTHREAD_CREATE INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*start_routine)(void*), void *arg) { EnsureMainThreadIDIsCorrect(); // Strict init-order checking is thread-hostile. if (flags()->strict_init_order) StopInitOrderChecking(); GET_STACK_TRACE_THREAD; int detached = 0; if (attr != 0) REAL(pthread_attr_getdetachstate)(attr, &detached); ThreadStartParam param; atomic_store(¶m.t, 0, memory_order_relaxed); atomic_store(¶m.is_registered, 0, memory_order_relaxed); int result = REAL(pthread_create)(thread, attr, asan_thread_start, ¶m); if (result == 0) { u32 current_tid = GetCurrentTidOrInvalid(); AsanThread *t = AsanThread::Create(start_routine, arg, current_tid, &stack, detached); atomic_store(¶m.t, reinterpret_cast(t), memory_order_release); // Wait until the AsanThread object is initialized and the ThreadRegistry // entry is in "started" state. One reason for this is that after this // interceptor exits, the child thread's stack may be the only thing holding // the |arg| pointer. This may cause LSan to report a leak if leak checking // happens at a point when the interceptor has already exited, but the stack // range for the child thread is not yet known. while (atomic_load(¶m.is_registered, memory_order_acquire) == 0) internal_sched_yield(); } return result; } + +INTERCEPTOR(int, pthread_join, void *t, void **arg) { + return real_pthread_join(t, arg); +} + +DEFINE_REAL_PTHREAD_FUNCTIONS #endif // ASAN_INTERCEPT_PTHREAD_CREATE #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION #if SANITIZER_ANDROID INTERCEPTOR(void*, bsd_signal, int signum, void *handler) { - if (!AsanInterceptsSignal(signum) || - common_flags()->allow_user_segv_handler) { + if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) { return REAL(bsd_signal)(signum, handler); } return 0; } #else INTERCEPTOR(void*, signal, int signum, void *handler) { - if (!AsanInterceptsSignal(signum) || - common_flags()->allow_user_segv_handler) { + if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) { return REAL(signal)(signum, handler); } return 0; } #endif INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) { - if (!AsanInterceptsSignal(signum) || - common_flags()->allow_user_segv_handler) { + if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) { return REAL(sigaction)(signum, act, oldact); } return 0; } namespace __sanitizer { int real_sigaction(int signum, const void *act, void *oldact) { return REAL(sigaction)(signum, (const struct sigaction *)act, (struct sigaction *)oldact); } } // namespace __sanitizer #elif SANITIZER_POSIX // We need to have defined REAL(sigaction) on posix systems. DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) #endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION #if ASAN_INTERCEPT_SWAPCONTEXT static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) { // Align to page size. uptr PageSize = GetPageSizeCached(); uptr bottom = stack & ~(PageSize - 1); ssize += stack - bottom; ssize = RoundUpTo(ssize, PageSize); static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb if (ssize && ssize <= kMaxSaneContextStackSize) { PoisonShadow(bottom, ssize, 0); } } INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp, struct ucontext_t *ucp) { static bool reported_warning = false; if (!reported_warning) { Report("WARNING: ASan doesn't fully support makecontext/swapcontext " "functions and may produce false positives in some cases!\n"); reported_warning = true; } // Clear shadow memory for new context (it may share stack // with current context). uptr stack, ssize; ReadContextStack(ucp, &stack, &ssize); ClearShadowMemoryForContextStack(stack, ssize); int res = REAL(swapcontext)(oucp, ucp); // swapcontext technically does not return, but program may swap context to // "oucp" later, that would look as if swapcontext() returned 0. // We need to clear shadow for ucp once again, as it may be in arbitrary // state. ClearShadowMemoryForContextStack(stack, ssize); return res; } #endif // ASAN_INTERCEPT_SWAPCONTEXT INTERCEPTOR(void, longjmp, void *env, int val) { __asan_handle_no_return(); REAL(longjmp)(env, val); } #if ASAN_INTERCEPT__LONGJMP INTERCEPTOR(void, _longjmp, void *env, int val) { __asan_handle_no_return(); REAL(_longjmp)(env, val); } #endif #if ASAN_INTERCEPT_SIGLONGJMP INTERCEPTOR(void, siglongjmp, void *env, int val) { __asan_handle_no_return(); REAL(siglongjmp)(env, val); } #endif #if ASAN_INTERCEPT___CXA_THROW INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { CHECK(REAL(__cxa_throw)); __asan_handle_no_return(); REAL(__cxa_throw)(a, b, 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; } INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, memcmp); if (UNLIKELY(!asan_inited)) return internal_memcmp(a1, a2, size); ENSURE_ASAN_INITED(); if (flags()->replace_intrin) { if (flags()->strict_memcmp) { // Check the entire regions even if the first bytes of the buffers are // different. ASAN_READ_RANGE(ctx, a1, size); ASAN_READ_RANGE(ctx, a2, size); // Fallthrough to REAL(memcmp) below. } else { unsigned char c1 = 0, c2 = 0; const unsigned char *s1 = (const unsigned char*)a1; const unsigned char *s2 = (const unsigned char*)a2; uptr i; for (i = 0; i < size; i++) { c1 = s1[i]; c2 = s2[i]; if (c1 != c2) break; } ASAN_READ_RANGE(ctx, s1, Min(i + 1, size)); ASAN_READ_RANGE(ctx, s2, Min(i + 1, size)); return CharCmp(c1, c2); } } return REAL(memcmp(a1, a2, size)); } // memcpy is called during __asan_init() from the internals of printf(...). // We do not treat memcpy with to==from as a bug. // See http://llvm.org/bugs/show_bug.cgi?id=11763. #define ASAN_MEMCPY_IMPL(ctx, to, from, size) do { \ if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); \ if (asan_init_is_running) { \ return REAL(memcpy)(to, from, size); \ } \ ENSURE_ASAN_INITED(); \ if (flags()->replace_intrin) { \ if (to != from) { \ CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); \ } \ ASAN_READ_RANGE(ctx, from, size); \ ASAN_WRITE_RANGE(ctx, to, size); \ } \ return REAL(memcpy)(to, from, size); \ } while (0) void *__asan_memcpy(void *to, const void *from, uptr size) { ASAN_MEMCPY_IMPL(nullptr, to, from, size); } // memset is called inside Printf. #define ASAN_MEMSET_IMPL(ctx, block, c, size) do { \ if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); \ if (asan_init_is_running) { \ return REAL(memset)(block, c, size); \ } \ ENSURE_ASAN_INITED(); \ if (flags()->replace_intrin) { \ ASAN_WRITE_RANGE(ctx, block, size); \ } \ return REAL(memset)(block, c, size); \ } while (0) void *__asan_memset(void *block, int c, uptr size) { ASAN_MEMSET_IMPL(nullptr, block, c, size); } #define ASAN_MEMMOVE_IMPL(ctx, to, from, size) do { \ if (UNLIKELY(!asan_inited)) \ return internal_memmove(to, from, size); \ ENSURE_ASAN_INITED(); \ if (flags()->replace_intrin) { \ ASAN_READ_RANGE(ctx, from, size); \ ASAN_WRITE_RANGE(ctx, to, size); \ } \ return internal_memmove(to, from, size); \ } while (0) void *__asan_memmove(void *to, const void *from, uptr size) { ASAN_MEMMOVE_IMPL(nullptr, to, from, size); } INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, memmove); ASAN_MEMMOVE_IMPL(ctx, to, from, size); } INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, memcpy); #if !SANITIZER_MAC ASAN_MEMCPY_IMPL(ctx, to, from, size); #else // At least on 10.7 and 10.8 both memcpy() and memmove() are being replaced // with WRAP(memcpy). As a result, false positives are reported for memmove() // calls. If we just disable error reporting with // ASAN_OPTIONS=replace_intrin=0, memmove() is still replaced with // internal_memcpy(), which may lead to crashes, see // http://llvm.org/bugs/show_bug.cgi?id=16362. ASAN_MEMMOVE_IMPL(ctx, to, from, size); #endif // !SANITIZER_MAC } INTERCEPTOR(void*, memset, void *block, int c, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, memset); ASAN_MEMSET_IMPL(ctx, block, c, size); } INTERCEPTOR(char*, strchr, const char *str, int c) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strchr); if (UNLIKELY(!asan_inited)) return internal_strchr(str, c); // strchr is called inside create_purgeable_zone() when MallocGuardEdges=1 is // used. if (asan_init_is_running) { return REAL(strchr)(str, 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); } return result; } #if ASAN_INTERCEPT_INDEX # if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX INTERCEPTOR(char*, index, const char *string, int c) ALIAS(WRAPPER_NAME(strchr)); # else # if SANITIZER_MAC DECLARE_REAL(char*, index, const char *string, int c) OVERRIDE_FUNCTION(index, strchr); # else DEFINE_REAL(char*, index, const char *string, int c) # endif # endif #endif // ASAN_INTERCEPT_INDEX // For both strcat() and strncat() we need to check the validity of |to| // argument irrespective of the |from| length. INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strcat); // NOLINT ENSURE_ASAN_INITED(); if (flags()->replace_str) { 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_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 // to_length + from_length + 1. if (from_length > 0) { CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1, from, from_length + 1); } } return REAL(strcat)(to, from); // NOLINT } INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strncat); ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_length = MaybeRealStrnlen(from, 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_WRITE_RANGE(ctx, to + to_length, from_length + 1); if (from_length > 0) { CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1, from, copy_length); } } return REAL(strncat)(to, from, size); } INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strcpy); // NOLINT #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(strcpy)(to, from); // NOLINT #endif // strcpy is called from malloc_default_purgeable_zone() // in __asan::ReplaceSystemAlloc() on Mac. if (asan_init_is_running) { return REAL(strcpy)(to, from); // NOLINT } ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_size = REAL(strlen)(from) + 1; CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size); ASAN_READ_RANGE(ctx, from, from_size); ASAN_WRITE_RANGE(ctx, to, from_size); } return REAL(strcpy)(to, from); // NOLINT } #if ASAN_INTERCEPT_STRDUP INTERCEPTOR(char*, strdup, const char *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strdup); if (UNLIKELY(!asan_inited)) return internal_strdup(s); ENSURE_ASAN_INITED(); uptr length = REAL(strlen)(s); if (flags()->replace_str) { ASAN_READ_RANGE(ctx, s, length + 1); } GET_STACK_TRACE_MALLOC; void *new_mem = asan_malloc(length + 1, &stack); REAL(memcpy)(new_mem, s, length + 1); return reinterpret_cast(new_mem); } #endif INTERCEPTOR(SIZE_T, strlen, const char *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strlen); if (UNLIKELY(!asan_inited)) return internal_strlen(s); // strlen is called from malloc_default_purgeable_zone() // in __asan::ReplaceSystemAlloc() on Mac. if (asan_init_is_running) { return REAL(strlen)(s); } ENSURE_ASAN_INITED(); SIZE_T length = REAL(strlen)(s); if (flags()->replace_str) { ASAN_READ_RANGE(ctx, s, length + 1); } return length; } INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, wcslen); SIZE_T length = REAL(wcslen)(s); if (!asan_init_is_running) { ENSURE_ASAN_INITED(); ASAN_READ_RANGE(ctx, s, (length + 1) * sizeof(wchar_t)); } return length; } INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strncpy); ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size); ASAN_READ_RANGE(ctx, from, from_size); ASAN_WRITE_RANGE(ctx, to, size); } return REAL(strncpy)(to, from, size); } #if ASAN_INTERCEPT_STRNLEN INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strnlen); ENSURE_ASAN_INITED(); uptr length = REAL(strnlen)(s, maxlen); if (flags()->replace_str) { ASAN_READ_RANGE(ctx, s, Min(length + 1, maxlen)); } return length; } #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(nptr); } CHECK(*endptr >= nptr); } INTERCEPTOR(long, strtol, const char *nptr, // NOLINT char **endptr, int base) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strtol); ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(strtol)(nptr, endptr, base); } 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); } return result; } INTERCEPTOR(int, atoi, const char *nptr) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, atoi); #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(atoi)(nptr); #endif ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(atoi)(nptr); } char *real_endptr; // "man atoi" tells that behavior of atoi(nptr) is the same as // strtol(nptr, 0, 10), i.e. it sets errno to ERANGE if the // parsed integer can't be stored in *long* type (even if it's // 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); return result; } INTERCEPTOR(long, atol, const char *nptr) { // NOLINT void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, atol); #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(atol)(nptr); #endif ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(atol)(nptr); } 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); return result; } #if ASAN_INTERCEPT_ATOLL_AND_STRTOLL INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT char **endptr, int base) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strtoll); ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(strtoll)(nptr, endptr, base); } 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); } return result; } INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, atoll); ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(atoll)(nptr); } 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); return result; } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL static void AtCxaAtexit(void *unused) { (void)unused; StopInitOrderChecking(); } #if ASAN_INTERCEPT___CXA_ATEXIT INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, void *dso_handle) { #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(__cxa_atexit)(func, arg, dso_handle); #endif ENSURE_ASAN_INITED(); int res = REAL(__cxa_atexit)(func, arg, dso_handle); REAL(__cxa_atexit)(AtCxaAtexit, 0, 0); return res; } #endif // ASAN_INTERCEPT___CXA_ATEXIT #if ASAN_INTERCEPT_FORK INTERCEPTOR(int, fork, void) { ENSURE_ASAN_INITED(); if (common_flags()->coverage) CovBeforeFork(); int pid = REAL(fork)(); if (common_flags()->coverage) CovAfterFork(pid); return pid; } #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? - ThreadStartParam param; - atomic_store(¶m.t, 0, memory_order_relaxed); - atomic_store(¶m.is_registered, 0, memory_order_relaxed); - DWORD result = REAL(CreateThread)(security, stack_size, asan_thread_start, - ¶m, thr_flags, tid); - if (result) { - u32 current_tid = GetCurrentTidOrInvalid(); - AsanThread *t = + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(start_routine, arg, current_tid, &stack, detached); - atomic_store(¶m.t, reinterpret_cast(t), memory_order_release); - // The pthread_create interceptor waits here, so we do the same for - // consistency. - while (atomic_load(¶m.is_registered, memory_order_acquire) == 0) - internal_sched_yield(); - } - return result; + 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() { static bool was_called_once; CHECK(was_called_once == false); was_called_once = true; InitializeCommonInterceptors(); // Intercept mem* functions. ASAN_INTERCEPT_FUNC(memcmp); ASAN_INTERCEPT_FUNC(memmove); ASAN_INTERCEPT_FUNC(memset); if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { ASAN_INTERCEPT_FUNC(memcpy); } // Intercept str* functions. ASAN_INTERCEPT_FUNC(strcat); // NOLINT ASAN_INTERCEPT_FUNC(strchr); ASAN_INTERCEPT_FUNC(strcpy); // NOLINT ASAN_INTERCEPT_FUNC(strlen); ASAN_INTERCEPT_FUNC(wcslen); ASAN_INTERCEPT_FUNC(strncat); ASAN_INTERCEPT_FUNC(strncpy); #if ASAN_INTERCEPT_STRDUP ASAN_INTERCEPT_FUNC(strdup); #endif #if ASAN_INTERCEPT_STRNLEN ASAN_INTERCEPT_FUNC(strnlen); #endif #if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX ASAN_INTERCEPT_FUNC(index); #endif ASAN_INTERCEPT_FUNC(atoi); ASAN_INTERCEPT_FUNC(atol); ASAN_INTERCEPT_FUNC(strtol); #if ASAN_INTERCEPT_ATOLL_AND_STRTOLL ASAN_INTERCEPT_FUNC(atoll); ASAN_INTERCEPT_FUNC(strtoll); #endif // Intecept signal- and jump-related functions. ASAN_INTERCEPT_FUNC(longjmp); #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION ASAN_INTERCEPT_FUNC(sigaction); #if SANITIZER_ANDROID ASAN_INTERCEPT_FUNC(bsd_signal); #else ASAN_INTERCEPT_FUNC(signal); #endif #endif #if ASAN_INTERCEPT_SWAPCONTEXT ASAN_INTERCEPT_FUNC(swapcontext); #endif #if ASAN_INTERCEPT__LONGJMP ASAN_INTERCEPT_FUNC(_longjmp); #endif #if ASAN_INTERCEPT_SIGLONGJMP ASAN_INTERCEPT_FUNC(siglongjmp); #endif // Intercept exception handling functions. #if ASAN_INTERCEPT___CXA_THROW ASAN_INTERCEPT_FUNC(__cxa_throw); #endif // Intercept threading-related functions #if ASAN_INTERCEPT_PTHREAD_CREATE ASAN_INTERCEPT_FUNC(pthread_create); + ASAN_INTERCEPT_FUNC(pthread_join); #endif // Intercept atexit function. #if ASAN_INTERCEPT___CXA_ATEXIT ASAN_INTERCEPT_FUNC(__cxa_atexit); #endif #if ASAN_INTERCEPT_FORK ASAN_INTERCEPT_FUNC(fork); #endif // Some Windows-specific interceptors. #if SANITIZER_WINDOWS InitializeWindowsInterceptors(); #endif VReport(1, "AddressSanitizer: libc interceptors initialized\n"); } } // namespace __asan Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_internal.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_internal.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_internal.h (revision 279191) @@ -1,162 +1,161 @@ //===-- asan_internal.h -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // ASan-private header which defines various general utilities. //===----------------------------------------------------------------------===// #ifndef ASAN_INTERNAL_H #define ASAN_INTERNAL_H #include "asan_flags.h" #include "asan_interface_internal.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_libc.h" #define ASAN_DEFAULT_FAILURE_EXITCODE 1 #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) # error "The AddressSanitizer run-time should not be" " instrumented by AddressSanitizer" #endif // Build-time configuration options. // If set, asan will intercept C++ exception api call(s). #ifndef ASAN_HAS_EXCEPTIONS # define ASAN_HAS_EXCEPTIONS 1 #endif // If set, values like allocator chunk size, as well as defaults for some flags // will be changed towards less memory overhead. #ifndef ASAN_LOW_MEMORY #if SANITIZER_WORDSIZE == 32 # define ASAN_LOW_MEMORY 1 #else # define ASAN_LOW_MEMORY 0 # endif #endif #ifndef ASAN_DYNAMIC # ifdef PIC # define ASAN_DYNAMIC 1 # else # define ASAN_DYNAMIC 0 # endif #endif // All internal functions in asan reside inside the __asan namespace // to avoid namespace collisions with the user programs. // Separate namespace also makes it simpler to distinguish the asan run-time // functions from the instrumented user code in a profile. 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 void NORETURN ShowStatsAndAbort(); // asan_malloc_linux.cc / asan_malloc_mac.cc void ReplaceSystemMalloc(); // asan_linux.cc / asan_mac.cc / asan_win.cc void *AsanDoesNotSupportStaticLinkage(); void AsanCheckDynamicRTPrereqs(); void AsanCheckIncompatibleRT(); void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); void AsanOnSIGSEGV(int, void *siginfo, void *context); void MaybeReexec(); -bool AsanInterceptsSignal(int signum); void ReadContextStack(void *context, uptr *stack, uptr *ssize); void AsanPlatformThreadInit(); void StopInitOrderChecking(); // Wrapper for TLS/TSD. void AsanTSDInit(void (*destructor)(void *tsd)); void *AsanTSDGet(); void AsanTSDSet(void *tsd); void PlatformTSDDtor(void *tsd); void AppendToErrorMessageBuffer(const char *buffer); -void ParseExtraActivationFlags(); - void *AsanDlSymNext(const char *sym); + +void ReserveShadowMemoryRange(uptr beg, uptr end); // Platform-specific options. #if SANITIZER_MAC bool PlatformHasDifferentMemcpyAndMemmove(); # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \ (PlatformHasDifferentMemcpyAndMemmove()) #else # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true #endif // SANITIZER_MAC // Add convenient macro for interface functions that may be represented as // weak hooks. #define ASAN_MALLOC_HOOK(ptr, size) \ if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size) #define ASAN_FREE_HOOK(ptr) \ if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr) #define ASAN_ON_ERROR() \ if (&__asan_on_error) __asan_on_error() extern int asan_inited; // Used to avoid infinite recursion in __asan_init(). extern bool asan_init_is_running; extern void (*death_callback)(void); // These magic values are written to shadow for better error reporting. const int kAsanHeapLeftRedzoneMagic = 0xfa; const int kAsanHeapRightRedzoneMagic = 0xfb; const int kAsanHeapFreeMagic = 0xfd; const int kAsanStackLeftRedzoneMagic = 0xf1; const int kAsanStackMidRedzoneMagic = 0xf2; const int kAsanStackRightRedzoneMagic = 0xf3; const int kAsanStackPartialRedzoneMagic = 0xf4; const int kAsanStackAfterReturnMagic = 0xf5; const int kAsanInitializationOrderMagic = 0xf6; const int kAsanUserPoisonedMemoryMagic = 0xf7; const int kAsanContiguousContainerOOBMagic = 0xfc; const int kAsanStackUseAfterScopeMagic = 0xf8; const int kAsanGlobalRedzoneMagic = 0xf9; const int kAsanInternalHeapMagic = 0xfe; const int kAsanArrayCookieMagic = 0xac; const int kAsanIntraObjectRedzone = 0xbb; const int kAsanAllocaLeftMagic = 0xca; const int kAsanAllocaRightMagic = 0xcb; static const uptr kCurrentStackFrameMagic = 0x41B58AB3; static const uptr kRetiredStackFrameMagic = 0x45E0360E; } // namespace __asan #endif // ASAN_INTERNAL_H Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_linux.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_linux.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_linux.cc (revision 279191) @@ -1,249 +1,245 @@ //===-- asan_linux.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Linux-specific details. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_freebsd.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" #include #include #include #include #include #include #include #include #include #include #include #if SANITIZER_FREEBSD #include #endif #if SANITIZER_ANDROID || SANITIZER_FREEBSD #include extern "C" void* _DYNAMIC; #else #include #include #endif // x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in // 32-bit mode. #if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \ __FreeBSD_version <= 902001 // v9.2 #define ucontext_t xucontext_t #endif typedef enum { ASAN_RT_VERSION_UNDEFINED = 0, ASAN_RT_VERSION_DYNAMIC, ASAN_RT_VERSION_STATIC, } asan_rt_version_t; // FIXME: perhaps also store abi version here? extern "C" { SANITIZER_INTERFACE_ATTRIBUTE asan_rt_version_t __asan_rt_version; } namespace __asan { void MaybeReexec() { // No need to re-exec on Linux. } void *AsanDoesNotSupportStaticLinkage() { // This will fail to link with -static. return &_DYNAMIC; // defined in link.h } #if SANITIZER_ANDROID // FIXME: should we do anything for Android? void AsanCheckDynamicRTPrereqs() {} void AsanCheckIncompatibleRT() {} #else static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size, void *data) { // Continue until the first dynamic library is found if (!info->dlpi_name || info->dlpi_name[0] == 0) return 0; // Ignore vDSO if (internal_strncmp(info->dlpi_name, "linux-", sizeof("linux-") - 1) == 0) return 0; *(const char **)data = info->dlpi_name; return 1; } static bool IsDynamicRTName(const char *libname) { return internal_strstr(libname, "libclang_rt.asan") || internal_strstr(libname, "libasan.so"); } static void ReportIncompatibleRT() { Report("Your application is linked against incompatible ASan runtimes.\n"); Die(); } void AsanCheckDynamicRTPrereqs() { // Ensure that dynamic RT is the first DSO in the list const char *first_dso_name = 0; dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name); if (first_dso_name && !IsDynamicRTName(first_dso_name)) { Report("ASan runtime does not come first in initial library list; " "you should either link runtime to your application or " "manually preload it with LD_PRELOAD.\n"); Die(); } } void AsanCheckIncompatibleRT() { if (ASAN_DYNAMIC) { if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { __asan_rt_version = ASAN_RT_VERSION_DYNAMIC; } else if (__asan_rt_version != ASAN_RT_VERSION_DYNAMIC) { ReportIncompatibleRT(); } } else { if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { // Ensure that dynamic runtime is not present. We should detect it // as early as possible, otherwise ASan interceptors could bind to // the functions in dynamic ASan runtime instead of the functions in // system libraries, causing crashes later in ASan initialization. MemoryMappingLayout proc_maps(/*cache_enabled*/true); char filename[128]; while (proc_maps.Next(0, 0, 0, filename, sizeof(filename), 0)) { if (IsDynamicRTName(filename)) { Report("Your application is linked against " "incompatible ASan runtimes.\n"); Die(); } } __asan_rt_version = ASAN_RT_VERSION_STATIC; } else if (__asan_rt_version != ASAN_RT_VERSION_STATIC) { ReportIncompatibleRT(); } } } #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 } -bool AsanInterceptsSignal(int signum) { - return signum == SIGSEGV && common_flags()->handle_segv; -} - void AsanPlatformThreadInit() { // Nothing here for now. } #if !SANITIZER_ANDROID void ReadContextStack(void *context, uptr *stack, uptr *ssize) { ucontext_t *ucp = (ucontext_t*)context; *stack = (uptr)ucp->uc_stack.ss_sp; *ssize = ucp->uc_stack.ss_size; } #else void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } #endif void *AsanDlSymNext(const char *sym) { return dlsym(RTLD_NEXT, sym); } } // namespace __asan #endif // SANITIZER_FREEBSD || SANITIZER_LINUX Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_mac.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_mac.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_mac.cc (revision 279191) @@ -1,419 +1,428 @@ //===-- asan_mac.cc -------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Mac-specific details. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_MAC #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_stack.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mac.h" #include // for _NSGetArgv #include // for dladdr() #include #include #include #include #include #include #include #include #include // for free() #include #include 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 } bool PlatformHasDifferentMemcpyAndMemmove() { // On OS X 10.7 memcpy() and memmove() are both resolved // into memmove$VARIANT$sse42. // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34. // TODO(glider): need to check dynamically that memcpy() and memmove() are // actually the same function. return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD; } extern "C" void __asan_init(); static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; LowLevelAllocator allocator_for_env; // Change the value of the env var |name|, leaking the original value. // If |name_value| is NULL, the variable is deleted from the environment, // 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); uptr name_len = internal_strlen(name); while (*environ != 0) { uptr len = internal_strlen(*environ); if (len > name_len) { const char *p = *environ; 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(name_value); } else { // Shift the subsequent pointers back. char **del = environ; do { del[0] = del[1]; } while (*del++); } } } environ++; } } void MaybeReexec() { - if (!flags()->allow_reexec) return; // Make sure the dynamic ASan runtime library is preloaded so that the // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec // ourselves. Dl_info info; CHECK(dladdr((void*)((uptr)__asan_init), &info)); char *dyld_insert_libraries = const_cast(GetEnv(kDyldInsertLibraries)); uptr old_env_len = dyld_insert_libraries ? internal_strlen(dyld_insert_libraries) : 0; 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, StripModuleName(info.dli_fname))) { + !REAL(strstr)(dyld_insert_libraries, dylib_name)) { // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime // library. char program_name[1024]; uint32_t buf_size = sizeof(program_name); _NSGetExecutablePath(program_name, &buf_size); char *new_env = const_cast(info.dli_fname); if (dyld_insert_libraries) { // Append the runtime dylib name to the existing value of // DYLD_INSERT_LIBRARIES. new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2); internal_strncpy(new_env, dyld_insert_libraries, old_env_len); new_env[old_env_len] = ':'; // Copy fname_len and add a trailing zero. internal_strncpy(new_env + old_env_len + 1, info.dli_fname, fname_len + 1); // Ok to use setenv() since the wrappers don't depend on the value of // asan_inited. setenv(kDyldInsertLibraries, new_env, /*overwrite*/1); } else { // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); } VReport(1, "exec()-ing the program with\n"); VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env); VReport(1, "to enable ASan wrappers.\n"); - VReport(1, "Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n"); execv(program_name, *_NSGetArgv()); - } else { - // DYLD_INSERT_LIBRARIES is set and contains the runtime library. - if (old_env_len == fname_len) { - // It's just the runtime library name - fine to unset the variable. - LeakyResetEnv(kDyldInsertLibraries, NULL); - } else { - uptr env_name_len = internal_strlen(kDyldInsertLibraries); - // Allocate memory to hold the previous env var name, its value, the '=' - // sign and the '\0' char. - char *new_env = (char*)allocator_for_env.Allocate( - old_env_len + 2 + env_name_len); - CHECK(new_env); - internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); - internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); - new_env[env_name_len] = '='; - char *new_env_pos = new_env + env_name_len + 1; - // Iterate over colon-separated pieces of |dyld_insert_libraries|. - char *piece_start = dyld_insert_libraries; - char *piece_end = NULL; - char *old_env_end = dyld_insert_libraries + old_env_len; - do { - if (piece_start[0] == ':') piece_start++; - piece_end = REAL(strchr)(piece_start, ':'); - if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; - if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; - uptr piece_len = piece_end - piece_start; + // We get here only if execv() failed. + Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, " + "which is required for ASan to work. ASan tried to set the " + "environment variable and re-execute itself, but execv() failed, " + "possibly because of sandbox restrictions. Make sure to launch the " + "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env); + CHECK("execv failed" && 0); + } - // If the current piece isn't the runtime library name, - // append it to new_env. - if ((piece_len != fname_len) || - (internal_strncmp(piece_start, info.dli_fname, fname_len) != 0)) { - if (new_env_pos != new_env + env_name_len + 1) { - new_env_pos[0] = ':'; - new_env_pos++; - } - internal_strncpy(new_env_pos, piece_start, piece_len); - } - // Move on to the next piece. - new_env_pos += piece_len; - piece_start = piece_end; - } while (piece_start < old_env_end); + // 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. - // Can't use setenv() here, because it requires the allocator to be - // initialized. - // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in - // a separate function called after InitializeAllocator(). - LeakyResetEnv(kDyldInsertLibraries, new_env); + uptr env_name_len = internal_strlen(kDyldInsertLibraries); + // Allocate memory to hold the previous env var name, its value, the '=' + // sign and the '\0' char. + char *new_env = (char*)allocator_for_env.Allocate( + old_env_len + 2 + env_name_len); + CHECK(new_env); + internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); + internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); + new_env[env_name_len] = '='; + char *new_env_pos = new_env + env_name_len + 1; + + // Iterate over colon-separated pieces of |dyld_insert_libraries|. + char *piece_start = dyld_insert_libraries; + char *piece_end = NULL; + char *old_env_end = dyld_insert_libraries + old_env_len; + do { + if (piece_start[0] == ':') piece_start++; + piece_end = REAL(strchr)(piece_start, ':'); + if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; + if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; + uptr piece_len = piece_end - piece_start; + + char *filename_start = + (char *)internal_memrchr(piece_start, '/', piece_len); + uptr filename_len = piece_len; + if (filename_start) { + filename_start += 1; + filename_len = piece_len - (filename_start - piece_start); + } else { + filename_start = piece_start; } - } + + // If the current piece isn't the runtime library name, + // append it to new_env. + if ((dylib_name_len != filename_len) || + (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) { + if (new_env_pos != new_env + env_name_len + 1) { + new_env_pos[0] = ':'; + new_env_pos++; + } + internal_strncpy(new_env_pos, piece_start, piece_len); + new_env_pos += piece_len; + } + // Move on to the next piece. + piece_start = piece_end; + } while (piece_start < old_env_end); + + // Can't use setenv() here, because it requires the allocator to be + // initialized. + // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in + // a separate function called after InitializeAllocator(). + if (new_env_pos == new_env + env_name_len + 1) new_env = NULL; + LeakyResetEnv(kDyldInsertLibraries, new_env); } // No-op. Mac does not support static linkage anyway. void *AsanDoesNotSupportStaticLinkage() { return 0; } // No-op. Mac does not support static linkage anyway. void AsanCheckDynamicRTPrereqs() {} // No-op. Mac does not support static linkage anyway. void AsanCheckIncompatibleRT() {} -bool AsanInterceptsSignal(int signum) { - return (signum == SIGSEGV || signum == SIGBUS) && - common_flags()->handle_segv; -} - void AsanPlatformThreadInit() { } void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } // Support for the following functions from libdispatch on Mac OS: // dispatch_async_f() // dispatch_async() // dispatch_sync_f() // dispatch_sync() // dispatch_after_f() // dispatch_after() // dispatch_group_async_f() // dispatch_group_async() // TODO(glider): libdispatch API contains other functions that we don't support // yet. // // dispatch_sync() and dispatch_sync_f() are synchronous, although chances are // they can cause jobs to run on a thread different from the current one. // TODO(glider): if so, we need a test for this (otherwise we should remove // them). // // The following functions use dispatch_barrier_async_f() (which isn't a library // function but is exported) and are thus supported: // dispatch_source_set_cancel_handler_f() // dispatch_source_set_cancel_handler() // dispatch_source_set_event_handler_f() // dispatch_source_set_event_handler() // // The reference manual for Grand Central Dispatch is available at // http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html // The implementation details are at // http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c typedef void* dispatch_group_t; typedef void* dispatch_queue_t; typedef void* dispatch_source_t; typedef u64 dispatch_time_t; typedef void (*dispatch_function_t)(void *block); typedef void* (*worker_t)(void *block); // A wrapper for the ObjC blocks used to support libdispatch. typedef struct { void *block; dispatch_function_t func; u32 parent_tid; } asan_block_context_t; ALWAYS_INLINE void asan_register_worker_thread(int parent_tid, StackTrace *stack) { AsanThread *t = GetCurrentThread(); if (!t) { t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr, parent_tid, stack, /* detached */ true); t->Init(); asanThreadRegistry().StartThread(t->tid(), 0, 0); SetCurrentThread(t); } } // For use by only those functions that allocated the context via // alloc_asan_context(). extern "C" void asan_dispatch_call_block_and_release(void *block) { GET_STACK_TRACE_THREAD; asan_block_context_t *context = (asan_block_context_t*)block; VReport(2, "asan_dispatch_call_block_and_release(): " "context: %p, pthread_self: %p\n", block, pthread_self()); asan_register_worker_thread(context->parent_tid, &stack); // Call the original dispatcher for the block. context->func(context->block); asan_free(context, &stack, FROM_MALLOC); } } // namespace __asan using namespace __asan; // NOLINT // Wrap |ctxt| and |func| into an asan_block_context_t. // The caller retains control of the allocated context. extern "C" asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, BufferedStackTrace *stack) { asan_block_context_t *asan_ctxt = (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); asan_ctxt->block = ctxt; asan_ctxt->func = func; asan_ctxt->parent_tid = GetCurrentTidOrInvalid(); return asan_ctxt; } // Define interceptor for dispatch_*_f function with the three most common // parameters: dispatch_queue_t, context, dispatch_function_t. #define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \ INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \ dispatch_function_t func) { \ GET_STACK_TRACE_THREAD; \ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ - if (common_flags()->verbosity >= 2) { \ + if (Verbosity() >= 2) { \ Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ asan_ctxt, pthread_self()); \ PRINT_CURRENT_STACK(); \ } \ return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \ asan_dispatch_call_block_and_release); \ } INTERCEPT_DISPATCH_X_F_3(dispatch_async_f) INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f) INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f) INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { GET_STACK_TRACE_THREAD; asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (common_flags()->verbosity >= 2) { + if (Verbosity() >= 2) { Report("dispatch_after_f: %p\n", asan_ctxt); PRINT_CURRENT_STACK(); } return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt, asan_dispatch_call_block_and_release); } INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, dispatch_queue_t dq, void *ctxt, dispatch_function_t func) { GET_STACK_TRACE_THREAD; asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (common_flags()->verbosity >= 2) { + if (Verbosity() >= 2) { Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", asan_ctxt, pthread_self()); PRINT_CURRENT_STACK(); } REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt, asan_dispatch_call_block_and_release); } #if !defined(MISSING_BLOCKS_SUPPORT) extern "C" { void dispatch_async(dispatch_queue_t dq, void(^work)(void)); void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)); void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)); void dispatch_source_set_cancel_handler(dispatch_source_t ds, void(^work)(void)); void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); } #define GET_ASAN_BLOCK(work) \ void (^asan_block)(void); \ int parent_tid = GetCurrentTidOrInvalid(); \ asan_block = ^(void) { \ GET_STACK_TRACE_THREAD; \ asan_register_worker_thread(parent_tid, &stack); \ work(); \ } -// Forces the compiler to generate a frame pointer in the function. -#define ENABLE_FRAME_POINTER \ - do { \ - volatile uptr enable_fp; \ - enable_fp = GET_CURRENT_FRAME(); \ - } while (0) - INTERCEPTOR(void, dispatch_async, dispatch_queue_t dq, void(^work)(void)) { ENABLE_FRAME_POINTER; GET_ASAN_BLOCK(work); REAL(dispatch_async)(dq, asan_block); } INTERCEPTOR(void, dispatch_group_async, dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) { ENABLE_FRAME_POINTER; GET_ASAN_BLOCK(work); REAL(dispatch_group_async)(dg, dq, asan_block); } INTERCEPTOR(void, dispatch_after, dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) { ENABLE_FRAME_POINTER; GET_ASAN_BLOCK(work); REAL(dispatch_after)(when, queue, asan_block); } INTERCEPTOR(void, dispatch_source_set_cancel_handler, dispatch_source_t ds, void(^work)(void)) { + if (!work) { + REAL(dispatch_source_set_cancel_handler)(ds, work); + return; + } ENABLE_FRAME_POINTER; GET_ASAN_BLOCK(work); REAL(dispatch_source_set_cancel_handler)(ds, asan_block); } INTERCEPTOR(void, dispatch_source_set_event_handler, dispatch_source_t ds, void(^work)(void)) { ENABLE_FRAME_POINTER; GET_ASAN_BLOCK(work); REAL(dispatch_source_set_event_handler)(ds, asan_block); } #endif #endif // SANITIZER_MAC Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_malloc_mac.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_malloc_mac.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_malloc_mac.cc (revision 279191) @@ -1,359 +1,362 @@ //===-- asan_malloc_mac.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Mac-specific malloc interception. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_MAC #include #include #include #include #include #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "sanitizer_common/sanitizer_mac.h" // Similar code is used in Google Perftools, // http://code.google.com/p/google-perftools. // ---------------------- Replacement functions ---------------- {{{1 using namespace __asan; // NOLINT // TODO(glider): do we need both zones? static malloc_zone_t *system_malloc_zone = 0; static malloc_zone_t asan_zone; INTERCEPTOR(malloc_zone_t *, malloc_create_zone, vm_size_t start_size, unsigned zone_flags) { ENSURE_ASAN_INITED(); GET_STACK_TRACE_MALLOC; uptr page_size = GetPageSizeCached(); uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size); malloc_zone_t *new_zone = (malloc_zone_t*)asan_memalign(page_size, allocated_size, &stack, FROM_MALLOC); internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone)); new_zone->zone_name = NULL; // The name will be changed anyway. if (GetMacosVersion() >= MACOS_VERSION_LION) { // Prevent the client app from overwriting the zone contents. // Library functions that need to modify the zone will set PROT_WRITE on it. // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher. mprotect(new_zone, allocated_size, PROT_READ); } return new_zone; } INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) { ENSURE_ASAN_INITED(); return &asan_zone; } INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) { // FIXME: ASan should support purgeable allocations. // https://code.google.com/p/address-sanitizer/issues/detail?id=139 ENSURE_ASAN_INITED(); return &asan_zone; } INTERCEPTOR(void, malloc_make_purgeable, void *ptr) { // FIXME: ASan should support purgeable allocations. Ignoring them is fine // for now. ENSURE_ASAN_INITED(); } INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) { // FIXME: ASan should support purgeable allocations. Ignoring them is fine // for now. ENSURE_ASAN_INITED(); // Must return 0 if the contents were not purged since the last call to // malloc_make_purgeable(). return 0; } INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) { ENSURE_ASAN_INITED(); // Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes. size_t buflen = 6 + (name ? internal_strlen(name) : 0); InternalScopedString new_name(buflen); if (name && zone->introspect == asan_zone.introspect) { new_name.append("asan-%s", name); name = new_name.data(); } // Call the system malloc's implementation for both external and our zones, // since that appropriately changes VM region protections on the zone. REAL(malloc_set_zone_name)(zone, name); } INTERCEPTOR(void *, malloc, size_t size) { ENSURE_ASAN_INITED(); GET_STACK_TRACE_MALLOC; void *res = asan_malloc(size, &stack); return res; } INTERCEPTOR(void, free, void *ptr) { ENSURE_ASAN_INITED(); if (!ptr) return; GET_STACK_TRACE_FREE; asan_free(ptr, &stack, FROM_MALLOC); } INTERCEPTOR(void *, realloc, void *ptr, size_t size) { ENSURE_ASAN_INITED(); GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) { ENSURE_ASAN_INITED(); GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } INTERCEPTOR(void *, valloc, size_t size) { ENSURE_ASAN_INITED(); GET_STACK_TRACE_MALLOC; return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); } INTERCEPTOR(size_t, malloc_good_size, size_t size) { ENSURE_ASAN_INITED(); return asan_zone.introspect->good_size(&asan_zone, size); } INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { ENSURE_ASAN_INITED(); CHECK(memptr); GET_STACK_TRACE_MALLOC; void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC); if (result) { *memptr = result; return 0; } return -1; } namespace { -// TODO(glider): the mz_* functions should be united with the Linux wrappers, -// as they are basically copied from there. -size_t mz_size(malloc_zone_t* zone, const void* ptr) { +// TODO(glider): the __asan_mz_* functions should be united with the Linux +// wrappers, as they are basically copied from there. +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +size_t __asan_mz_size(malloc_zone_t* zone, const void* ptr) { return asan_mz_size(ptr); } -void *mz_malloc(malloc_zone_t *zone, size_t size) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_mz_malloc(malloc_zone_t *zone, uptr size) { if (UNLIKELY(!asan_inited)) { CHECK(system_malloc_zone); return malloc_zone_malloc(system_malloc_zone, size); } GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } -void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { if (UNLIKELY(!asan_inited)) { // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. const size_t kCallocPoolSize = 1024; static uptr calloc_memory_for_dlsym[kCallocPoolSize]; static size_t allocated; size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; void *mem = (void*)&calloc_memory_for_dlsym[allocated]; allocated += size_in_words; CHECK(allocated < kCallocPoolSize); return mem; } GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } -void *mz_valloc(malloc_zone_t *zone, size_t size) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_mz_valloc(malloc_zone_t *zone, size_t size) { if (UNLIKELY(!asan_inited)) { CHECK(system_malloc_zone); return malloc_zone_valloc(system_malloc_zone, size); } GET_STACK_TRACE_MALLOC; return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC); } #define GET_ZONE_FOR_PTR(ptr) \ malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \ const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name void ALWAYS_INLINE free_common(void *context, void *ptr) { if (!ptr) return; GET_STACK_TRACE_FREE; // FIXME: need to retire this flag. if (!flags()->mac_ignore_invalid_free) { asan_free(ptr, &stack, FROM_MALLOC); } else { GET_ZONE_FOR_PTR(ptr); WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); return; } } // TODO(glider): the allocation callbacks need to be refactored. -void mz_free(malloc_zone_t *zone, void *ptr) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_mz_free(malloc_zone_t *zone, void *ptr) { free_common(zone, ptr); } -void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { if (!ptr) { GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } else { if (asan_mz_size(ptr)) { GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } else { // We can't recover from reallocating an unknown address, because // this would require reading at most |size| bytes from // potentially unaccessible memory. GET_STACK_TRACE_FREE; GET_ZONE_FOR_PTR(ptr); ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); } } } -void mz_destroy(malloc_zone_t* zone) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_mz_destroy(malloc_zone_t* zone) { // A no-op -- we will not be destroyed! - Report("mz_destroy() called -- ignoring\n"); + Report("__asan_mz_destroy() called -- ignoring\n"); } - // from AvailabilityMacros.h -#if defined(MAC_OS_X_VERSION_10_6) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 -void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { if (UNLIKELY(!asan_inited)) { CHECK(system_malloc_zone); return malloc_zone_memalign(system_malloc_zone, align, size); } GET_STACK_TRACE_MALLOC; return asan_memalign(align, size, &stack, FROM_MALLOC); } // This function is currently unused, and we build with -Werror. #if 0 -void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) { +void __asan_mz_free_definite_size( + malloc_zone_t* zone, void *ptr, size_t size) { // TODO(glider): check that |size| is valid. UNIMPLEMENTED(); } #endif -#endif kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder) { // Should enumerate all the pointers we have. Seems like a lot of work. return KERN_FAILURE; } size_t mi_good_size(malloc_zone_t *zone, size_t size) { // I think it's always safe to return size, but we maybe could do better. return size; } boolean_t mi_check(malloc_zone_t *zone) { UNIMPLEMENTED(); } void mi_print(malloc_zone_t *zone, boolean_t verbose) { UNIMPLEMENTED(); } void mi_log(malloc_zone_t *zone, void *address) { // I don't think we support anything like this } void mi_force_lock(malloc_zone_t *zone) { asan_mz_force_lock(); } void mi_force_unlock(malloc_zone_t *zone) { asan_mz_force_unlock(); } void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { AsanMallocStats malloc_stats; FillMallocStatistics(&malloc_stats); CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); } -#if defined(MAC_OS_X_VERSION_10_6) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 boolean_t mi_zone_locked(malloc_zone_t *zone) { // UNIMPLEMENTED(); return false; } -#endif } // unnamed namespace namespace __asan { void ReplaceSystemMalloc() { static malloc_introspection_t asan_introspection; // Ok to use internal_memset, these places are not performance-critical. internal_memset(&asan_introspection, 0, sizeof(asan_introspection)); asan_introspection.enumerator = &mi_enumerator; asan_introspection.good_size = &mi_good_size; asan_introspection.check = &mi_check; asan_introspection.print = &mi_print; asan_introspection.log = &mi_log; asan_introspection.force_lock = &mi_force_lock; asan_introspection.force_unlock = &mi_force_unlock; asan_introspection.statistics = &mi_statistics; + asan_introspection.zone_locked = &mi_zone_locked; internal_memset(&asan_zone, 0, sizeof(malloc_zone_t)); - // Start with a version 4 zone which is used for OS X 10.4 and 10.5. - asan_zone.version = 4; + // Use version 6 for OSX >= 10.6. + asan_zone.version = 6; asan_zone.zone_name = "asan"; - asan_zone.size = &mz_size; - asan_zone.malloc = &mz_malloc; - asan_zone.calloc = &mz_calloc; - asan_zone.valloc = &mz_valloc; - asan_zone.free = &mz_free; - asan_zone.realloc = &mz_realloc; - asan_zone.destroy = &mz_destroy; + asan_zone.size = &__asan_mz_size; + asan_zone.malloc = &__asan_mz_malloc; + asan_zone.calloc = &__asan_mz_calloc; + asan_zone.valloc = &__asan_mz_valloc; + asan_zone.free = &__asan_mz_free; + asan_zone.realloc = &__asan_mz_realloc; + asan_zone.destroy = &__asan_mz_destroy; asan_zone.batch_malloc = 0; asan_zone.batch_free = 0; - asan_zone.introspect = &asan_introspection; - - // from AvailabilityMacros.h -#if defined(MAC_OS_X_VERSION_10_6) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - // Switch to version 6 on OSX 10.6 to support memalign. - asan_zone.version = 6; asan_zone.free_definite_size = 0; - asan_zone.memalign = &mz_memalign; - asan_introspection.zone_locked = &mi_zone_locked; -#endif + asan_zone.memalign = &__asan_mz_memalign; + asan_zone.introspect = &asan_introspection; // Register the ASan zone. malloc_zone_register(&asan_zone); } } // namespace __asan #endif // SANITIZER_MAC Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_mapping.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_mapping.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_mapping.h (revision 279191) @@ -1,271 +1,288 @@ //===-- asan_mapping.h ------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Defines ASan memory mapping. //===----------------------------------------------------------------------===// #ifndef ASAN_MAPPING_H #define ASAN_MAPPING_H #include "asan_internal.h" // The full explanation of the memory mapping could be found here: // http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm // // Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000: // || `[0x10007fff8000, 0x7fffffffffff]` || HighMem || // || `[0x02008fff7000, 0x10007fff7fff]` || HighShadow || // || `[0x00008fff7000, 0x02008fff6fff]` || ShadowGap || // || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow || // || `[0x000000000000, 0x00007fff7fff]` || LowMem || // // When SHADOW_OFFSET is zero (-pie): // || `[0x100000000000, 0x7fffffffffff]` || HighMem || // || `[0x020000000000, 0x0fffffffffff]` || HighShadow || // || `[0x000000040000, 0x01ffffffffff]` || ShadowGap || // // Special case when something is already mapped between // 0x003000000000 and 0x005000000000 (e.g. when prelink is installed): // || `[0x10007fff8000, 0x7fffffffffff]` || HighMem || // || `[0x02008fff7000, 0x10007fff7fff]` || HighShadow || // || `[0x005000000000, 0x02008fff6fff]` || ShadowGap3 || // || `[0x003000000000, 0x004fffffffff]` || MidMem || // || `[0x000a7fff8000, 0x002fffffffff]` || ShadowGap2 || // || `[0x00067fff8000, 0x000a7fff7fff]` || MidShadow || // || `[0x00008fff7000, 0x00067fff7fff]` || ShadowGap || // || `[0x00007fff8000, 0x00008fff6fff]` || LowShadow || // || `[0x000000000000, 0x00007fff7fff]` || LowMem || // // Default Linux/i386 mapping on x86_64 machine: // || `[0x40000000, 0xffffffff]` || HighMem || // || `[0x28000000, 0x3fffffff]` || HighShadow || // || `[0x24000000, 0x27ffffff]` || ShadowGap || // || `[0x20000000, 0x23ffffff]` || LowShadow || // || `[0x00000000, 0x1fffffff]` || LowMem || // // Default Linux/i386 mapping on i386 machine // (addresses starting with 0xc0000000 are reserved // for kernel and thus not sanitized): // || `[0x38000000, 0xbfffffff]` || HighMem || // || `[0x27000000, 0x37ffffff]` || HighShadow || // || `[0x24000000, 0x26ffffff]` || ShadowGap || // || `[0x20000000, 0x23ffffff]` || LowShadow || // || `[0x00000000, 0x1fffffff]` || LowMem || // -// Default Linux/MIPS mapping: +// Default Linux/MIPS32 mapping: // || `[0x2aaa0000, 0xffffffff]` || HighMem || // || `[0x0fff4000, 0x2aa9ffff]` || HighShadow || // || `[0x0bff4000, 0x0fff3fff]` || ShadowGap || // || `[0x0aaa0000, 0x0bff3fff]` || LowShadow || // || `[0x00000000, 0x0aa9ffff]` || LowMem || // +// Default Linux/MIPS64 mapping: +// || `[0x4000000000, 0xffffffffff]` || HighMem || +// || `[0x2800000000, 0x3fffffffff]` || HighShadow || +// || `[0x2400000000, 0x27ffffffff]` || ShadowGap || +// || `[0x2000000000, 0x23ffffffff]` || LowShadow || +// || `[0x0000000000, 0x1fffffffff]` || LowMem || +// // Shadow mapping on FreeBSD/x86-64 with SHADOW_OFFSET == 0x400000000000: // || `[0x500000000000, 0x7fffffffffff]` || HighMem || // || `[0x4a0000000000, 0x4fffffffffff]` || HighShadow || // || `[0x480000000000, 0x49ffffffffff]` || ShadowGap || // || `[0x400000000000, 0x47ffffffffff]` || LowShadow || // || `[0x000000000000, 0x3fffffffffff]` || LowMem || // // Shadow mapping on FreeBSD/i386 with SHADOW_OFFSET == 0x40000000: // || `[0x60000000, 0xffffffff]` || HighMem || // || `[0x4c000000, 0x5fffffff]` || HighShadow || // || `[0x48000000, 0x4bffffff]` || ShadowGap || // || `[0x40000000, 0x47ffffff]` || LowShadow || // || `[0x00000000, 0x3fffffff]` || LowMem || +// +// Default Windows/i386 mapping: +// (the exact location of HighShadow/HighMem may vary depending +// on WoW64, /LARGEADDRESSAWARE, etc). +// || `[0x50000000, 0xffffffff]` || HighMem || +// || `[0x3a000000, 0x4fffffff]` || HighShadow || +// || `[0x36000000, 0x39ffffff]` || ShadowGap || +// || `[0x30000000, 0x35ffffff]` || LowShadow || +// || `[0x00000000, 0x2fffffff]` || LowMem || 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 kAArch64_ShadowOffset64 = 1ULL << 36; static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000; -static const u64 kMIPS64_ShadowOffset64 = 1ULL << 36; +static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37; static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000 static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000 +static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 #define SHADOW_SCALE kDefaultShadowScale #if SANITIZER_ANDROID # define SHADOW_OFFSET (0) #else # if SANITIZER_WORDSIZE == 32 # if defined(__mips__) # 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 # else -# if SANITIZER_IOS -# define SHADOW_OFFSET kIosShadowOffset32 -# else -# define SHADOW_OFFSET kDefaultShadowOffset32 -# endif +# define SHADOW_OFFSET kDefaultShadowOffset32 # endif # else # if defined(__aarch64__) # define SHADOW_OFFSET kAArch64_ShadowOffset64 # elif defined(__powerpc64__) # define SHADOW_OFFSET kPPC64_ShadowOffset64 # elif SANITIZER_FREEBSD # define SHADOW_OFFSET kFreeBSD_ShadowOffset64 # elif SANITIZER_MAC # define SHADOW_OFFSET kDefaultShadowOffset64 # elif defined(__mips64) # define SHADOW_OFFSET kMIPS64_ShadowOffset64 # else # define SHADOW_OFFSET kDefaultShort64bitShadowOffset # endif # endif #endif #define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE) #define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) + (SHADOW_OFFSET)) #define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE) #define kLowMemBeg 0 #define kLowMemEnd (SHADOW_OFFSET ? SHADOW_OFFSET - 1 : 0) #define kLowShadowBeg SHADOW_OFFSET #define kLowShadowEnd MEM_TO_SHADOW(kLowMemEnd) #define kHighMemBeg (MEM_TO_SHADOW(kHighMemEnd) + 1) #define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg) #define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd) # define kMidShadowBeg MEM_TO_SHADOW(kMidMemBeg) # define kMidShadowEnd MEM_TO_SHADOW(kMidMemEnd) // With the zero shadow base we can not actually map pages starting from 0. // This constant is somewhat arbitrary. #define kZeroBaseShadowStart (1 << 18) #define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 \ : kZeroBaseShadowStart) #define kShadowGapEnd ((kMidMemBeg ? kMidShadowBeg : kHighShadowBeg) - 1) #define kShadowGap2Beg (kMidMemBeg ? kMidShadowEnd + 1 : 0) #define kShadowGap2End (kMidMemBeg ? kMidMemBeg - 1 : 0) #define kShadowGap3Beg (kMidMemBeg ? kMidMemEnd + 1 : 0) #define kShadowGap3End (kMidMemBeg ? kHighShadowBeg - 1 : 0) #define DO_ASAN_MAPPING_PROFILE 0 // Set to 1 to profile the functions below. #if DO_ASAN_MAPPING_PROFILE # define PROFILE_ASAN_MAPPING() AsanMappingProfile[__LINE__]++; #else # define PROFILE_ASAN_MAPPING() #endif // If 1, all shadow boundaries are constants. // Don't set to 1 other than for testing. #define ASAN_FIXED_MAPPING 0 namespace __asan { extern uptr AsanMappingProfile[]; #if ASAN_FIXED_MAPPING // Fixed mapping for 64-bit Linux. Mostly used for performance comparison // with non-fixed mapping. As of r175253 (Feb 2013) the performance // difference between fixed and non-fixed mapping is below the noise level. static uptr kHighMemEnd = 0x7fffffffffffULL; static uptr kMidMemBeg = 0x3000000000ULL; static uptr kMidMemEnd = 0x4fffffffffULL; #else extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init. #endif static inline bool AddrIsInLowMem(uptr a) { PROFILE_ASAN_MAPPING(); return a < kLowMemEnd; } static inline bool AddrIsInLowShadow(uptr a) { PROFILE_ASAN_MAPPING(); return a >= kLowShadowBeg && a <= kLowShadowEnd; } static inline bool AddrIsInHighMem(uptr a) { PROFILE_ASAN_MAPPING(); return a >= kHighMemBeg && a <= kHighMemEnd; } static inline bool AddrIsInMidMem(uptr a) { PROFILE_ASAN_MAPPING(); return kMidMemBeg && a >= kMidMemBeg && a <= kMidMemEnd; } static inline bool AddrIsInMem(uptr a) { PROFILE_ASAN_MAPPING(); return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a); } static inline uptr MemToShadow(uptr p) { PROFILE_ASAN_MAPPING(); CHECK(AddrIsInMem(p)); return MEM_TO_SHADOW(p); } static inline bool AddrIsInHighShadow(uptr a) { PROFILE_ASAN_MAPPING(); return a >= kHighShadowBeg && a <= kHighMemEnd; } static inline bool AddrIsInMidShadow(uptr a) { PROFILE_ASAN_MAPPING(); return kMidMemBeg && a >= kMidShadowBeg && a <= kMidMemEnd; } static inline bool AddrIsInShadow(uptr a) { PROFILE_ASAN_MAPPING(); return AddrIsInLowShadow(a) || AddrIsInMidShadow(a) || AddrIsInHighShadow(a); } static inline bool AddrIsInShadowGap(uptr a) { PROFILE_ASAN_MAPPING(); if (kMidMemBeg) { if (a <= kShadowGapEnd) return SHADOW_OFFSET == 0 || a >= kShadowGapBeg; return (a >= kShadowGap2Beg && a <= kShadowGap2End) || (a >= kShadowGap3Beg && a <= kShadowGap3End); } // In zero-based shadow mode we treat addresses near zero as addresses // in shadow gap as well. if (SHADOW_OFFSET == 0) return a <= kShadowGapEnd; return a >= kShadowGapBeg && a <= kShadowGapEnd; } static inline bool AddrIsAlignedByGranularity(uptr a) { PROFILE_ASAN_MAPPING(); return (a & (SHADOW_GRANULARITY - 1)) == 0; } static inline bool AddressIsPoisoned(uptr a) { PROFILE_ASAN_MAPPING(); const uptr kAccessSize = 1; u8 *shadow_address = (u8*)MEM_TO_SHADOW(a); s8 shadow_value = *shadow_address; if (shadow_value) { u8 last_accessed_byte = (a & (SHADOW_GRANULARITY - 1)) + kAccessSize - 1; return (last_accessed_byte >= shadow_value); } return false; } // Must be after all calls to PROFILE_ASAN_MAPPING(). static const uptr kAsanMappingProfileSize = __LINE__; } // namespace __asan #endif // ASAN_MAPPING_H Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_poisoning.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_poisoning.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_poisoning.cc (revision 279191) @@ -1,416 +1,427 @@ //===-- asan_poisoning.cc -------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Shadow memory poisoning by ASan RTL and by user application. //===----------------------------------------------------------------------===// #include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" +#include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_flags.h" namespace __asan { +static atomic_uint8_t can_poison_memory; + +void SetCanPoisonMemory(bool value) { + atomic_store(&can_poison_memory, value, memory_order_release); +} + +bool CanPoisonMemory() { + return atomic_load(&can_poison_memory, memory_order_acquire); +} + void PoisonShadow(uptr addr, uptr size, u8 value) { - if (!flags()->poison_heap) return; + if (!CanPoisonMemory()) return; CHECK(AddrIsAlignedByGranularity(addr)); CHECK(AddrIsInMem(addr)); CHECK(AddrIsAlignedByGranularity(addr + size)); CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY)); CHECK(REAL(memset)); FastPoisonShadow(addr, size, value); } void PoisonShadowPartialRightRedzone(uptr addr, uptr size, uptr redzone_size, u8 value) { - if (!flags()->poison_heap) return; + if (!CanPoisonMemory()) return; CHECK(AddrIsAlignedByGranularity(addr)); CHECK(AddrIsInMem(addr)); FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value); } struct ShadowSegmentEndpoint { u8 *chunk; s8 offset; // in [0, SHADOW_GRANULARITY) s8 value; // = *chunk; explicit ShadowSegmentEndpoint(uptr address) { chunk = (u8*)MemToShadow(address); offset = address & (SHADOW_GRANULARITY - 1); value = *chunk; } }; void FlushUnneededASanShadowMemory(uptr p, uptr size) { // Since asan's mapping is compacting, the shadow chunk may be // not page-aligned, so we only flush the page-aligned portion. uptr page_size = GetPageSizeCached(); uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size); uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size); FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); } void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) { uptr end = ptr + size; - if (common_flags()->verbosity) { + if (Verbosity()) { Printf("__asan_%spoison_intra_object_redzone [%p,%p) %zd\n", poison ? "" : "un", ptr, end, size); - if (common_flags()->verbosity >= 2) + if (Verbosity() >= 2) PRINT_CURRENT_STACK(); } CHECK(size); CHECK_LE(size, 4096); CHECK(IsAligned(end, SHADOW_GRANULARITY)); if (!IsAligned(ptr, SHADOW_GRANULARITY)) { *(u8 *)MemToShadow(ptr) = poison ? static_cast(ptr % SHADOW_GRANULARITY) : 0; ptr |= SHADOW_GRANULARITY - 1; ptr++; } for (; ptr < end; ptr += SHADOW_GRANULARITY) *(u8*)MemToShadow(ptr) = poison ? kAsanIntraObjectRedzone : 0; } } // namespace __asan // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT // Current implementation of __asan_(un)poison_memory_region doesn't check // that user program (un)poisons the memory it owns. It poisons memory // conservatively, and unpoisons progressively to make sure asan shadow // mapping invariant is preserved (see detailed mapping description here: // http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm). // // * if user asks to poison region [left, right), the program poisons // at least [left, AlignDown(right)). // * if user asks to unpoison region [left, right), the program unpoisons // at most [AlignDown(left), right). 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, (void *)end_addr); ShadowSegmentEndpoint beg(beg_addr); ShadowSegmentEndpoint end(end_addr); if (beg.chunk == end.chunk) { CHECK(beg.offset < end.offset); s8 value = beg.value; CHECK(value == end.value); // We can only poison memory if the byte in end.offset is unaddressable. // No need to re-poison memory if it is poisoned already. if (value > 0 && value <= end.offset) { if (beg.offset > 0) { *beg.chunk = Min(value, beg.offset); } else { *beg.chunk = kAsanUserPoisonedMemoryMagic; } } return; } CHECK(beg.chunk < end.chunk); if (beg.offset > 0) { // Mark bytes from beg.offset as unaddressable. if (beg.value == 0) { *beg.chunk = beg.offset; } else { *beg.chunk = Min(beg.value, beg.offset); } beg.chunk++; } REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk); // Poison if byte in end.offset is unaddressable. if (end.value > 0 && end.value <= end.offset) { *end.chunk = kAsanUserPoisonedMemoryMagic; } } 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, (void *)end_addr); ShadowSegmentEndpoint beg(beg_addr); ShadowSegmentEndpoint end(end_addr); if (beg.chunk == end.chunk) { CHECK(beg.offset < end.offset); s8 value = beg.value; CHECK(value == end.value); // We unpoison memory bytes up to enbytes up to end.offset if it is not // unpoisoned already. if (value != 0) { *beg.chunk = Max(value, end.offset); } return; } CHECK(beg.chunk < end.chunk); if (beg.offset > 0) { *beg.chunk = 0; beg.chunk++; } REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk); if (end.offset > 0 && end.value != 0) { *end.chunk = Max(end.value, end.offset); } } int __asan_address_is_poisoned(void const volatile *addr) { return __asan::AddressIsPoisoned((uptr)addr); } uptr __asan_region_is_poisoned(uptr beg, uptr size) { if (!size) return 0; uptr end = beg + size; if (!AddrIsInMem(beg)) return beg; if (!AddrIsInMem(end)) return end; CHECK_LT(beg, end); uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY); uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY); uptr shadow_beg = MemToShadow(aligned_b); uptr shadow_end = MemToShadow(aligned_e); // First check the first and the last application bytes, // then check the SHADOW_GRANULARITY-aligned region by calling // mem_is_zero on the corresponding shadow. if (!__asan::AddressIsPoisoned(beg) && !__asan::AddressIsPoisoned(end - 1) && (shadow_end <= shadow_beg || __sanitizer::mem_is_zero((const char *)shadow_beg, shadow_end - shadow_beg))) return 0; // The fast check failed, so we have a poisoned byte somewhere. // Find it slowly. for (; beg < end; beg++) if (__asan::AddressIsPoisoned(beg)) return beg; UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found"); return 0; } #define CHECK_SMALL_REGION(p, size, isWrite) \ do { \ uptr __p = reinterpret_cast(p); \ uptr __size = size; \ if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \ __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);\ } \ } while (false); \ extern "C" SANITIZER_INTERFACE_ATTRIBUTE u16 __sanitizer_unaligned_load16(const uu16 *p) { CHECK_SMALL_REGION(p, sizeof(*p), false); return *p; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE u32 __sanitizer_unaligned_load32(const uu32 *p) { CHECK_SMALL_REGION(p, sizeof(*p), false); return *p; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE u64 __sanitizer_unaligned_load64(const uu64 *p) { CHECK_SMALL_REGION(p, sizeof(*p), false); return *p; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store16(uu16 *p, u16 x) { CHECK_SMALL_REGION(p, sizeof(*p), true); *p = x; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store32(uu32 *p, u32 x) { CHECK_SMALL_REGION(p, sizeof(*p), true); *p = x; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store64(uu64 *p, u64 x) { CHECK_SMALL_REGION(p, sizeof(*p), true); *p = x; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_poison_cxx_array_cookie(uptr p) { if (SANITIZER_WORDSIZE != 64) return; if (!flags()->poison_array_cookie) return; uptr s = MEM_TO_SHADOW(p); *reinterpret_cast(s) = kAsanArrayCookieMagic; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_load_cxx_array_cookie(uptr *p) { if (SANITIZER_WORDSIZE != 64) return *p; if (!flags()->poison_array_cookie) return *p; uptr s = MEM_TO_SHADOW(reinterpret_cast(p)); u8 sval = *reinterpret_cast(s); if (sval == kAsanArrayCookieMagic) return *p; // If sval is not kAsanArrayCookieMagic it can only be freed memory, // which means that we are going to get double-free. So, return 0 to avoid // infinite loop of destructors. We don't want to report a double-free here // though, so print a warning just in case. // CHECK_EQ(sval, kAsanHeapFreeMagic); if (sval == kAsanHeapFreeMagic) { Report("AddressSanitizer: loaded array cookie from free-d memory; " "expect a double-free report\n"); return 0; } // The cookie may remain unpoisoned if e.g. it comes from a custom // operator new defined inside a class. return *p; } // This is a simplified version of __asan_(un)poison_memory_region, which // assumes that left border of region to be poisoned is properly aligned. static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { if (size == 0) return; uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1); PoisonShadow(addr, aligned_size, do_poison ? kAsanStackUseAfterScopeMagic : 0); if (size == aligned_size) return; s8 end_offset = (s8)(size - aligned_size); s8* shadow_end = (s8*)MemToShadow(addr + aligned_size); s8 end_value = *shadow_end; if (do_poison) { // If possible, mark all the bytes mapping to last shadow byte as // unaddressable. if (end_value > 0 && end_value <= end_offset) *shadow_end = (s8)kAsanStackUseAfterScopeMagic; } else { // If necessary, mark few first bytes mapping to last shadow byte // as addressable if (end_value != 0) *shadow_end = Max(end_value, end_offset); } } void __asan_poison_stack_memory(uptr addr, uptr size) { VReport(1, "poisoning: %p %zx\n", (void *)addr, size); PoisonAlignedStackMemory(addr, size, true); } void __asan_unpoison_stack_memory(uptr addr, uptr size) { VReport(1, "unpoisoning: %p %zx\n", (void *)addr, size); PoisonAlignedStackMemory(addr, size, false); } void __sanitizer_annotate_contiguous_container(const void *beg_p, const void *end_p, const void *old_mid_p, const void *new_mid_p) { if (!flags()->detect_container_overflow) return; VPrintf(2, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p, new_mid_p); uptr beg = reinterpret_cast(beg_p); uptr end = reinterpret_cast(end_p); uptr old_mid = reinterpret_cast(old_mid_p); uptr new_mid = reinterpret_cast(new_mid_p); uptr granularity = SHADOW_GRANULARITY; if (!(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end && IsAligned(beg, granularity))) { GET_STACK_TRACE_FATAL_HERE; ReportBadParamsToAnnotateContiguousContainer(beg, end, old_mid, new_mid, &stack); } CHECK_LE(end - beg, FIRST_32_SECOND_64(1UL << 30, 1UL << 34)); // Sanity check. uptr a = RoundDownTo(Min(old_mid, new_mid), granularity); uptr c = RoundUpTo(Max(old_mid, new_mid), granularity); uptr d1 = RoundDownTo(old_mid, granularity); // uptr d2 = RoundUpTo(old_mid, granularity); // Currently we should be in this state: // [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good. // Make a quick sanity check that we are indeed in this state. // // FIXME: Two of these three checks are disabled until we fix // https://code.google.com/p/address-sanitizer/issues/detail?id=258. // if (d1 != d2) // CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1); if (a + granularity <= d1) CHECK_EQ(*(u8*)MemToShadow(a), 0); // if (d2 + granularity <= c && c <= end) // CHECK_EQ(*(u8 *)MemToShadow(c - granularity), // kAsanContiguousContainerOOBMagic); uptr b1 = RoundDownTo(new_mid, granularity); uptr b2 = RoundUpTo(new_mid, granularity); // New state: // [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good. PoisonShadow(a, b1 - a, 0); PoisonShadow(b2, c - b2, kAsanContiguousContainerOOBMagic); if (b1 != b2) { CHECK_EQ(b2 - b1, granularity); *(u8*)MemToShadow(b1) = static_cast(new_mid - b1); } } int __sanitizer_verify_contiguous_container(const void *beg_p, const void *mid_p, const void *end_p) { if (!flags()->detect_container_overflow) return 1; uptr beg = reinterpret_cast(beg_p); uptr end = reinterpret_cast(end_p); uptr mid = reinterpret_cast(mid_p); CHECK_LE(beg, mid); CHECK_LE(mid, end); // Check some bytes starting from beg, some bytes around mid, and some bytes // ending with end. uptr kMaxRangeToCheck = 32; uptr r1_beg = beg; uptr r1_end = Min(end + kMaxRangeToCheck, mid); uptr r2_beg = Max(beg, mid - kMaxRangeToCheck); uptr r2_end = Min(end, mid + kMaxRangeToCheck); uptr r3_beg = Max(end - kMaxRangeToCheck, mid); uptr r3_end = end; for (uptr i = r1_beg; i < r1_end; i++) if (AddressIsPoisoned(i)) return 0; for (uptr i = r2_beg; i < mid; i++) if (AddressIsPoisoned(i)) return 0; for (uptr i = mid; i < r2_end; i++) if (!AddressIsPoisoned(i)) return 0; for (uptr i = r3_beg; i < r3_end; i++) if (!AddressIsPoisoned(i)) return 0; return 1; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_poison_intra_object_redzone(uptr ptr, uptr size) { AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, true); } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_unpoison_intra_object_redzone(uptr ptr, uptr size) { AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, false); } // --- Implementation of LSan-specific functions --- {{{1 namespace __lsan { bool WordIsPoisoned(uptr addr) { return (__asan_region_is_poisoned(addr, sizeof(uptr)) != 0); } } Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_poisoning.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_poisoning.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_poisoning.h (revision 279191) @@ -1,90 +1,93 @@ //===-- asan_poisoning.h ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Shadow memory poisoning by ASan RTL and by user application. //===----------------------------------------------------------------------===// #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_mapping.h" #include "sanitizer_common/sanitizer_flags.h" namespace __asan { +// Enable/disable memory poisoning. +void SetCanPoisonMemory(bool value); +bool CanPoisonMemory(); + // Poisons the shadow memory for "size" bytes starting from "addr". void PoisonShadow(uptr addr, uptr size, u8 value); // Poisons the shadow memory for "redzone_size" bytes starting from // "addr + size". void PoisonShadowPartialRightRedzone(uptr addr, uptr size, uptr redzone_size, u8 value); // Fast versions of PoisonShadow and PoisonShadowPartialRightRedzone that // assume that memory addresses are properly aligned. Use in // performance-critical code with care. ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, u8 value) { - DCHECK(flags()->poison_heap); + DCHECK(CanPoisonMemory()); uptr shadow_beg = MEM_TO_SHADOW(aligned_beg); uptr shadow_end = MEM_TO_SHADOW( aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1; // FIXME: Page states are different on Windows, so using the same interface // for mapping shadow and zeroing out pages doesn't "just work", so we should // probably provide higher-level interface for these operations. // For now, just memset on Windows. if (value || SANITIZER_WINDOWS == 1 || shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); } else { uptr page_size = GetPageSizeCached(); uptr page_beg = RoundUpTo(shadow_beg, page_size); uptr page_end = RoundDownTo(shadow_end, page_size); if (page_beg >= page_end) { REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg); } else { if (page_beg != shadow_beg) { REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg); } if (page_end != shadow_end) { REAL(memset)((void *)page_end, 0, shadow_end - page_end); } - void *res = MmapFixedNoReserve(page_beg, page_end - page_beg); - CHECK_EQ(page_beg, res); + ReserveShadowMemoryRange(page_beg, page_end - 1); } } } ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( uptr aligned_addr, uptr size, uptr redzone_size, u8 value) { - DCHECK(flags()->poison_heap); + DCHECK(CanPoisonMemory()); bool poison_partial = flags()->poison_partial; u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr); for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) { if (i + SHADOW_GRANULARITY <= size) { *shadow = 0; // fully addressable } else if (i >= size) { *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable } else { // first size-i bytes are addressable *shadow = poison_partial ? static_cast(size - i) : 0; } } } // Calls __sanitizer::FlushUnneededShadowMemory() on // [MemToShadow(p), MemToShadow(p+size)] with proper rounding. void FlushUnneededASanShadowMemory(uptr p, uptr size); } // namespace __asan Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_report.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_report.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_report.cc (revision 279191) @@ -1,1088 +1,1090 @@ //===-- asan_report.cc ----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // This file contains error reporting code. //===----------------------------------------------------------------------===// #include "asan_flags.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" namespace __asan { // -------------------- User-specified callbacks ----------------- {{{1 static void (*error_report_callback)(const char*); static char *error_message_buffer = 0; static uptr error_message_buffer_pos = 0; static uptr error_message_buffer_size = 0; struct ReportData { uptr pc; uptr sp; uptr bp; uptr addr; bool is_write; uptr access_size; const char *description; }; static bool report_happened = false; static ReportData report_data = {}; void AppendToErrorMessageBuffer(const char *buffer) { if (error_message_buffer) { uptr length = internal_strlen(buffer); CHECK_GE(error_message_buffer_size, error_message_buffer_pos); uptr remaining = error_message_buffer_size - error_message_buffer_pos; internal_strncpy(error_message_buffer + error_message_buffer_pos, buffer, remaining); error_message_buffer[error_message_buffer_size - 1] = '\0'; // FIXME: reallocate the buffer instead of truncating the message. - error_message_buffer_pos += remaining > length ? length : remaining; + error_message_buffer_pos += Min(remaining, length); } } // ---------------------- Decorator ------------------------------ {{{1 class Decorator: public __sanitizer::SanitizerCommonDecorator { public: Decorator() : SanitizerCommonDecorator() { } const char *Access() { return Blue(); } const char *EndAccess() { return Default(); } const char *Location() { return Green(); } const char *EndLocation() { return Default(); } const char *Allocation() { return Magenta(); } const char *EndAllocation() { return Default(); } const char *ShadowByte(u8 byte) { switch (byte) { case kAsanHeapLeftRedzoneMagic: case kAsanHeapRightRedzoneMagic: case kAsanArrayCookieMagic: return Red(); case kAsanHeapFreeMagic: return Magenta(); case kAsanStackLeftRedzoneMagic: case kAsanStackMidRedzoneMagic: case kAsanStackRightRedzoneMagic: case kAsanStackPartialRedzoneMagic: return Red(); case kAsanStackAfterReturnMagic: return Magenta(); case kAsanInitializationOrderMagic: return Cyan(); case kAsanUserPoisonedMemoryMagic: case kAsanContiguousContainerOOBMagic: case kAsanAllocaLeftMagic: case kAsanAllocaRightMagic: return Blue(); case kAsanStackUseAfterScopeMagic: return Magenta(); case kAsanGlobalRedzoneMagic: return Red(); case kAsanInternalHeapMagic: return Yellow(); case kAsanIntraObjectRedzone: return Yellow(); default: return Default(); } } const char *EndShadowByte() { return Default(); } const char *MemoryByte() { return Magenta(); } const char *EndMemoryByte() { return Default(); } }; // ---------------------- Helper functions ----------------------- {{{1 static void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte, bool in_shadow, const char *after = "\n") { Decorator d; str->append("%s%s%x%x%s%s", before, in_shadow ? d.ShadowByte(byte) : d.MemoryByte(), byte >> 4, byte & 15, in_shadow ? d.EndShadowByte() : d.EndMemoryByte(), after); } static void PrintShadowByte(InternalScopedString *str, const char *before, u8 byte, const char *after = "\n") { PrintMemoryByte(str, before, byte, /*in_shadow*/true, after); } static void PrintShadowBytes(InternalScopedString *str, const char *before, u8 *bytes, u8 *guilty, uptr n) { Decorator d; if (before) str->append("%s%p:", before, bytes); for (uptr i = 0; i < n; i++) { u8 *p = bytes + i; const char *before = p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " "; const char *after = p == guilty ? "]" : ""; PrintShadowByte(str, before, *p, after); } str->append("\n"); } static void PrintLegend(InternalScopedString *str) { str->append( "Shadow byte legend (one shadow byte represents %d " "application bytes):\n", (int)SHADOW_GRANULARITY); PrintShadowByte(str, " Addressable: ", 0); str->append(" Partially addressable: "); for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " "); str->append("\n"); PrintShadowByte(str, " Heap left redzone: ", kAsanHeapLeftRedzoneMagic); PrintShadowByte(str, " Heap right redzone: ", kAsanHeapRightRedzoneMagic); PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic); PrintShadowByte(str, " Stack left redzone: ", kAsanStackLeftRedzoneMagic); PrintShadowByte(str, " Stack mid redzone: ", kAsanStackMidRedzoneMagic); PrintShadowByte(str, " Stack right redzone: ", kAsanStackRightRedzoneMagic); PrintShadowByte(str, " Stack partial redzone: ", kAsanStackPartialRedzoneMagic); PrintShadowByte(str, " Stack after return: ", kAsanStackAfterReturnMagic); PrintShadowByte(str, " Stack use after scope: ", kAsanStackUseAfterScopeMagic); PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic); PrintShadowByte(str, " Global init order: ", kAsanInitializationOrderMagic); PrintShadowByte(str, " Poisoned by user: ", kAsanUserPoisonedMemoryMagic); PrintShadowByte(str, " Container overflow: ", kAsanContiguousContainerOOBMagic); PrintShadowByte(str, " Array cookie: ", kAsanArrayCookieMagic); PrintShadowByte(str, " Intra object redzone: ", kAsanIntraObjectRedzone); PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic); PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic); PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic); } void MaybeDumpInstructionBytes(uptr pc) { if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) return; InternalScopedString str(1024); str.append("First 16 instruction bytes at pc: "); if (IsAccessibleMemoryRange(pc, 16)) { for (int i = 0; i < 16; ++i) { PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/false, " "); } str.append("\n"); } else { str.append("unaccessible\n"); } Report("%s", str.data()); } static void PrintShadowMemoryForAddress(uptr addr) { if (!AddrIsInMem(addr)) return; uptr shadow_addr = MemToShadow(addr); const uptr n_bytes_per_row = 16; uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); InternalScopedString str(4096 * 8); str.append("Shadow bytes around the buggy address:\n"); for (int i = -5; i <= 5; i++) { const char *prefix = (i == 0) ? "=>" : " "; PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row), (u8 *)shadow_addr, n_bytes_per_row); } if (flags()->print_legend) PrintLegend(&str); Printf("%s", str.data()); } static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, const char *zone_name) { if (zone_ptr) { if (zone_name) { Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n", ptr, zone_ptr, zone_name); } else { Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n", ptr, zone_ptr); } } else { Printf("malloc_zone_from_ptr(%p) = 0\n", ptr); } } static void DescribeThread(AsanThread *t) { if (t) DescribeThread(t->context()); } // ---------------------- Address Descriptions ------------------- {{{1 static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; } static const char *MaybeDemangleGlobalName(const char *name) { // We can spoil names of globals with C linkage, so use an heuristic // approach to check if the name should be demangled. bool should_demangle = false; if (name[0] == '_' && name[1] == 'Z') should_demangle = true; else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?') should_demangle = true; return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name; } // Check if the global is a zero-terminated ASCII string. If so, print it. static void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) { for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { unsigned char c = *(unsigned char*)p; if (c == '\0' || !IsASCII(c)) return; } if (*(char*)(g.beg + g.size - 1) != '\0') return; str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name), (char *)g.beg); } static const char *GlobalFilename(const __asan_global &g) { const char *res = g.module_name; // Prefer the filename from source location, if is available. if (g.location) res = g.location->filename; CHECK(res); return res; } static void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g) { str->append("%s", GlobalFilename(g)); if (!g.location) return; if (g.location->line_no) str->append(":%d", g.location->line_no); if (g.location->column_no) str->append(":%d", g.location->column_no); } bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, const __asan_global &g) { if (!IsAddressNearGlobal(addr, g)) return false; InternalScopedString str(4096); Decorator d; str.append("%s", d.Location()); if (addr < g.beg) { str.append("%p is located %zd bytes to the left", (void *)addr, g.beg - addr); } else if (addr + size > g.beg + g.size) { if (addr < g.beg + g.size) addr = g.beg + g.size; str.append("%p is located %zd bytes to the right", (void *)addr, addr - (g.beg + g.size)); } else { // Can it happen? str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg); } str.append(" of global variable '%s' defined in '", MaybeDemangleGlobalName(g.name)); PrintGlobalLocation(&str, g); str.append("' (0x%zx) of size %zu\n", g.beg, g.size); str.append("%s", d.EndLocation()); PrintGlobalNameIfASCII(&str, g); Printf("%s", str.data()); return true; } bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr, bool print) { if (AddrIsInMem(addr)) return false; const char *area_type = nullptr; if (AddrIsInShadowGap(addr)) area_type = "shadow gap"; else if (AddrIsInHighShadow(addr)) area_type = "high shadow"; else if (AddrIsInLowShadow(addr)) area_type = "low shadow"; if (area_type != nullptr) { if (print) { Printf("Address %p is located in the %s area.\n", addr, area_type); } else { CHECK(descr); descr->region_kind = area_type; } return true; } CHECK(0 && "Address is not in memory and not in shadow?"); return false; } // Return " (thread_name) " or an empty string if the name is empty. const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], uptr buff_len) { const char *name = t->name; if (name[0] == '\0') return ""; buff[0] = 0; internal_strncat(buff, " (", 3); internal_strncat(buff, name, buff_len - 4); internal_strncat(buff, ")", 2); return buff; } const char *ThreadNameWithParenthesis(u32 tid, char buff[], uptr buff_len) { if (tid == kInvalidTid) return ""; asanThreadRegistry().CheckLocked(); AsanThreadContext *t = GetThreadContextByTidLocked(tid); return ThreadNameWithParenthesis(t, buff, buff_len); } static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, uptr access_size, uptr prev_var_end, uptr next_var_beg) { uptr var_end = var.beg + var.size; uptr addr_end = addr + access_size; const char *pos_descr = 0; // If the variable [var.beg, var_end) is the nearest variable to the // current memory access, indicate it in the log. if (addr >= var.beg) { if (addr_end <= var_end) pos_descr = "is inside"; // May happen if this is a use-after-return. else if (addr < var_end) pos_descr = "partially overflows"; else if (addr_end <= next_var_beg && next_var_beg - addr_end >= addr - var_end) pos_descr = "overflows"; } else { if (addr_end > var.beg) pos_descr = "partially underflows"; else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end) pos_descr = "underflows"; } InternalScopedString str(1024); str.append(" [%zd, %zd)", var.beg, var_end); // Render variable name. str.append(" '"); for (uptr i = 0; i < var.name_len; ++i) { str.append("%c", var.name_pos[i]); } str.append("'"); if (pos_descr) { Decorator d; // FIXME: we may want to also print the size of the access here, // but in case of accesses generated by memset it may be confusing. str.append("%s <== Memory access at offset %zd %s this variable%s\n", d.Location(), addr, pos_descr, d.EndLocation()); } else { str.append("\n"); } Printf("%s", str.data()); } bool ParseFrameDescription(const char *frame_descr, InternalMmapVector *vars) { CHECK(frame_descr); char *p; // This string is created by the compiler and has the following form: // "n alloc_1 alloc_2 ... alloc_n" // where alloc_i looks like "offset size len ObjectName". uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10); if (n_objects == 0) return false; for (uptr i = 0; i < n_objects; i++) { uptr beg = (uptr)internal_simple_strtoll(p, &p, 10); uptr size = (uptr)internal_simple_strtoll(p, &p, 10); uptr len = (uptr)internal_simple_strtoll(p, &p, 10); if (beg == 0 || size == 0 || *p != ' ') { return false; } p++; StackVarDescr var = {beg, size, p, len}; vars->push_back(var); p += len; } return true; } bool DescribeAddressIfStack(uptr addr, uptr access_size) { AsanThread *t = FindThreadByStackAddress(addr); if (!t) return false; Decorator d; char tname[128]; Printf("%s", d.Location()); Printf("Address %p is located in stack of thread T%d%s", addr, t->tid(), ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname))); // Try to fetch precise stack frame for this access. AsanThread::StackFrameAccess access; if (!t->GetStackFrameAccessByAddr(addr, &access)) { Printf("%s\n", d.EndLocation()); return true; } Printf(" at offset %zu in frame%s\n", access.offset, d.EndLocation()); // Now we print the frame where the alloca has happened. // We print this frame as a stack trace with one element. // The symbolizer may print more than one frame if inlining was involved. // The frame numbers may be different than those in the stack trace printed // previously. That's unfortunate, but I have no better solution, // especially given that the alloca may be from entirely different place // (e.g. use-after-scope, or different thread's stack). #if defined(__powerpc64__) && defined(__BIG_ENDIAN__) // On PowerPC64 ELFv1, the address of a function actually points to a // three-doubleword data structure with the first field containing // the address of the function's code. access.frame_pc = *reinterpret_cast(access.frame_pc); #endif access.frame_pc += 16; Printf("%s", d.EndLocation()); StackTrace alloca_stack(&access.frame_pc, 1); alloca_stack.Print(); InternalMmapVector vars(16); if (!ParseFrameDescription(access.frame_descr, &vars)) { Printf("AddressSanitizer can't parse the stack frame " "descriptor: |%s|\n", access.frame_descr); // 'addr' is a stack address, so return true even if we can't parse frame return true; } uptr n_objects = vars.size(); // Report the number of stack objects. Printf(" This frame has %zu object(s):\n", n_objects); // Report all objects in this frame. for (uptr i = 0; i < n_objects; i++) { uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); PrintAccessAndVarIntersection(vars[i], access.offset, access_size, prev_var_end, next_var_beg); } Printf("HINT: this may be a false positive if your program uses " "some custom stack unwind mechanism or swapcontext\n"); if (SANITIZER_WINDOWS) Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n"); else Printf(" (longjmp and C++ exceptions *are* supported)\n"); DescribeThread(t); return true; } static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, uptr access_size) { sptr offset; Decorator d; InternalScopedString str(4096); str.append("%s", d.Location()); if (chunk.AddrIsAtLeft(addr, access_size, &offset)) { str.append("%p is located %zd bytes to the left of", (void *)addr, offset); } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) { if (offset < 0) { addr -= offset; offset = 0; } str.append("%p is located %zd bytes to the right of", (void *)addr, offset); } else if (chunk.AddrIsInside(addr, access_size, &offset)) { str.append("%p is located %zd bytes inside of", (void*)addr, offset); } else { str.append("%p is located somewhere around (this is AddressSanitizer bug!)", (void *)addr); } str.append(" %zu-byte region [%p,%p)\n", chunk.UsedSize(), (void *)(chunk.Beg()), (void *)(chunk.End())); str.append("%s", d.EndLocation()); Printf("%s", str.data()); } void DescribeHeapAddress(uptr addr, uptr access_size) { AsanChunkView chunk = FindHeapChunkByAddress(addr); if (!chunk.IsValid()) { Printf("AddressSanitizer can not describe address in more detail " "(wild memory access suspected).\n"); return; } DescribeAccessToHeapChunk(chunk, addr, access_size); CHECK(chunk.AllocTid() != kInvalidTid); asanThreadRegistry().CheckLocked(); AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(chunk.AllocTid()); StackTrace alloc_stack = chunk.GetAllocStack(); char tname[128]; Decorator d; AsanThreadContext *free_thread = 0; if (chunk.FreeTid() != kInvalidTid) { free_thread = GetThreadContextByTidLocked(chunk.FreeTid()); Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), free_thread->tid, ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), d.EndAllocation()); StackTrace free_stack = chunk.GetFreeStack(); free_stack.Print(); Printf("%spreviously allocated by thread T%d%s here:%s\n", d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); } else { Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); } alloc_stack.Print(); DescribeThread(GetCurrentThread()); if (free_thread) DescribeThread(free_thread); DescribeThread(alloc_thread); } void DescribeAddress(uptr addr, uptr access_size) { // Check if this is shadow or shadow gap. if (DescribeAddressIfShadow(addr)) return; CHECK(AddrIsInMem(addr)); if (DescribeAddressIfGlobal(addr, access_size)) return; if (DescribeAddressIfStack(addr, access_size)) return; // Assume it is a heap address. DescribeHeapAddress(addr, access_size); } // ------------------- Thread description -------------------- {{{1 void DescribeThread(AsanThreadContext *context) { CHECK(context); asanThreadRegistry().CheckLocked(); // No need to announce the main thread. if (context->tid == 0 || context->announced) { return; } context->announced = true; char tname[128]; InternalScopedString str(1024); str.append("Thread T%d%s", context->tid, ThreadNameWithParenthesis(context->tid, tname, sizeof(tname))); str.append( " created by T%d%s here:\n", context->parent_tid, ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); Printf("%s", str.data()); StackDepotGet(context->stack_id).Print(); // Recursively described parent thread if needed. if (flags()->print_full_thread_history) { AsanThreadContext *parent_context = GetThreadContextByTidLocked(context->parent_tid); DescribeThread(parent_context); } } // -------------------- Different kinds of reports ----------------- {{{1 // Use ScopedInErrorReport to run common actions just before and // immediately after printing error report. class ScopedInErrorReport { public: explicit ScopedInErrorReport(ReportData *report = nullptr) { static atomic_uint32_t num_calls; static u32 reporting_thread_tid; if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { // Do not print more than one report, otherwise they will mix up. // Error reporting functions shouldn't return at this situation, as // they are defined as no-return. Report("AddressSanitizer: while reporting a bug found another one. " "Ignoring.\n"); u32 current_tid = GetCurrentTidOrInvalid(); if (current_tid != reporting_thread_tid) { // ASan found two bugs in different threads simultaneously. Sleep // long enough to make sure that the thread which started to print // an error report will finish doing it. SleepForSeconds(Max(100, flags()->sleep_before_dying + 1)); } // If we're still not dead for some reason, use raw _exit() instead of // Die() to bypass any additional checks. internal__exit(flags()->exitcode); } if (report) report_data = *report; report_happened = true; ASAN_ON_ERROR(); // Make sure the registry and sanitizer report mutexes are locked while // we're printing an error report. // We can lock them only here to avoid self-deadlock in case of // recursive reports. asanThreadRegistry().Lock(); CommonSanitizerReportMutex.Lock(); reporting_thread_tid = GetCurrentTidOrInvalid(); Printf("====================================================" "=============\n"); } // Destructor is NORETURN, as functions that report errors are. NORETURN ~ScopedInErrorReport() { // Make sure the current thread is announced. DescribeThread(GetCurrentThread()); // We may want to grab this lock again when printing stats. asanThreadRegistry().Unlock(); // Print memory stats. if (flags()->print_stats) __asan_print_accumulated_stats(); if (error_report_callback) { error_report_callback(error_message_buffer); } Report("ABORTING\n"); Die(); } }; void ReportStackOverflow(const SignalContext &sig) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); Report( "ERROR: AddressSanitizer: stack-overflow on address %p" " (pc %p bp %p sp %p T%d)\n", (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, (void *)sig.sp, GetCurrentTidOrInvalid()); Printf("%s", d.EndWarning()); GET_STACK_TRACE_SIGNAL(sig); stack.Print(); ReportErrorSummary("stack-overflow", &stack); } void ReportSIGSEGV(const char *description, const SignalContext &sig) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); Report( "ERROR: AddressSanitizer: %s on unknown address %p" " (pc %p bp %p sp %p T%d)\n", description, (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, (void *)sig.sp, GetCurrentTidOrInvalid()); if (sig.pc < GetPageSizeCached()) { Report("Hint: pc points to the zero page.\n"); } Printf("%s", d.EndWarning()); GET_STACK_TRACE_SIGNAL(sig); stack.Print(); MaybeDumpInstructionBytes(sig.pc); Printf("AddressSanitizer can not provide additional info.\n"); ReportErrorSummary("SEGV", &stack); } void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); char tname[128]; u32 curr_tid = GetCurrentTidOrInvalid(); Report("ERROR: AddressSanitizer: attempting double-free on %p in " "thread T%d%s:\n", addr, curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); Printf("%s", d.EndWarning()); CHECK_GT(free_stack->size, 0); GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); stack.Print(); DescribeHeapAddress(addr, 1); ReportErrorSummary("double-free", &stack); } void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, BufferedStackTrace *free_stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); char tname[128]; u32 curr_tid = GetCurrentTidOrInvalid(); Report("ERROR: AddressSanitizer: new-delete-type-mismatch on %p in " "thread T%d%s:\n", addr, curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); Printf("%s object passed to delete has wrong type:\n", d.EndWarning()); Printf(" size of the allocated type: %zd bytes;\n" " size of the deallocated type: %zd bytes.\n", asan_mz_size(reinterpret_cast(addr)), delete_size); CHECK_GT(free_stack->size, 0); GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); stack.Print(); DescribeHeapAddress(addr, 1); ReportErrorSummary("new-delete-type-mismatch", &stack); Report("HINT: if you don't care about these warnings you may set " "ASAN_OPTIONS=new_delete_type_mismatch=0\n"); } void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); char tname[128]; u32 curr_tid = GetCurrentTidOrInvalid(); Report("ERROR: AddressSanitizer: attempting free on address " "which was not malloc()-ed: %p in thread T%d%s\n", addr, curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); Printf("%s", d.EndWarning()); CHECK_GT(free_stack->size, 0); GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); stack.Print(); DescribeHeapAddress(addr, 1); ReportErrorSummary("bad-free", &stack); } void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, AllocType alloc_type, AllocType dealloc_type) { static const char *alloc_names[] = {"INVALID", "malloc", "operator new", "operator new []"}; static const char *dealloc_names[] = {"INVALID", "free", "operator delete", "operator delete []"}; CHECK_NE(alloc_type, dealloc_type); ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", alloc_names[alloc_type], dealloc_names[dealloc_type], addr); Printf("%s", d.EndWarning()); CHECK_GT(free_stack->size, 0); GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); stack.Print(); DescribeHeapAddress(addr, 1); ReportErrorSummary("alloc-dealloc-mismatch", &stack); Report("HINT: if you don't care about these warnings you may set " "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); } void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: attempting to call " "malloc_usable_size() for pointer which is " "not owned: %p\n", addr); Printf("%s", d.EndWarning()); stack->Print(); DescribeHeapAddress(addr, 1); ReportErrorSummary("bad-malloc_usable_size", stack); } void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, BufferedStackTrace *stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: attempting to call " "__sanitizer_get_allocated_size() for pointer which is " "not owned: %p\n", addr); Printf("%s", d.EndWarning()); stack->Print(); DescribeHeapAddress(addr, 1); ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack); } void ReportStringFunctionMemoryRangesOverlap(const char *function, const char *offset1, uptr length1, const char *offset2, uptr length2, BufferedStackTrace *stack) { ScopedInErrorReport in_report; Decorator d; char bug_type[100]; internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: %s: " "memory ranges [%p,%p) and [%p, %p) overlap\n", \ bug_type, offset1, offset1 + length1, offset2, offset2 + length2); Printf("%s", d.EndWarning()); stack->Print(); DescribeAddress((uptr)offset1, length1); DescribeAddress((uptr)offset2, length2); ReportErrorSummary(bug_type, stack); } void ReportStringFunctionSizeOverflow(uptr offset, uptr size, BufferedStackTrace *stack) { ScopedInErrorReport in_report; Decorator d; const char *bug_type = "negative-size-param"; Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size); Printf("%s", d.EndWarning()); stack->Print(); DescribeAddress(offset, size); ReportErrorSummary(bug_type, stack); } void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, uptr old_mid, uptr new_mid, BufferedStackTrace *stack) { ScopedInErrorReport in_report; Report("ERROR: AddressSanitizer: bad parameters to " "__sanitizer_annotate_contiguous_container:\n" " beg : %p\n" " end : %p\n" " old_mid : %p\n" " new_mid : %p\n", beg, end, old_mid, new_mid); stack->Print(); ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack); } void ReportODRViolation(const __asan_global *g1, u32 stack_id1, const __asan_global *g2, u32 stack_id2) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg); Printf("%s", d.EndWarning()); InternalScopedString g1_loc(256), g2_loc(256); PrintGlobalLocation(&g1_loc, *g1); PrintGlobalLocation(&g2_loc, *g2); Printf(" [1] size=%zd '%s' %s\n", g1->size, MaybeDemangleGlobalName(g1->name), g1_loc.data()); Printf(" [2] size=%zd '%s' %s\n", g2->size, MaybeDemangleGlobalName(g2->name), g2_loc.data()); if (stack_id1 && stack_id2) { Printf("These globals were registered at these points:\n"); Printf(" [1]:\n"); StackDepotGet(stack_id1).Print(); Printf(" [2]:\n"); StackDepotGet(stack_id2).Print(); } Report("HINT: if you don't care about these warnings you may set " "ASAN_OPTIONS=detect_odr_violation=0\n"); InternalScopedString error_msg(256); error_msg.append("odr-violation: global '%s' at %s", MaybeDemangleGlobalName(g1->name), g1_loc.data()); ReportErrorSummary(error_msg.data()); } // ----------------------- CheckForInvalidPointerPair ----------- {{{1 static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, uptr a1, uptr a2) { ScopedInErrorReport in_report; 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); } static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { if (!flags()->detect_invalid_pointer_pairs) return; uptr a1 = reinterpret_cast(p1); uptr a2 = reinterpret_cast(p2); AsanChunkView chunk1 = FindHeapChunkByAddress(a1); AsanChunkView chunk2 = FindHeapChunkByAddress(a2); bool valid1 = chunk1.IsValid(); bool valid2 = chunk2.IsValid(); if ((valid1 != valid2) || (valid1 && valid2 && !chunk1.Eq(chunk2))) { GET_CALLER_PC_BP_SP; \ return ReportInvalidPointerPair(pc, bp, sp, a1, a2); } } // ----------------------- Mac-specific reports ----------------- {{{1 void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name, BufferedStackTrace *stack) { // Just print a warning here. Printf("free_common(%p) -- attempting to free unallocated memory.\n" "AddressSanitizer is ignoring this error on Mac OS now.\n", addr); PrintZoneForPointer(addr, zone_ptr, zone_name); stack->Print(); DescribeHeapAddress(addr, 1); } void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, BufferedStackTrace *stack) { ScopedInErrorReport in_report; Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n" "This is an unrecoverable problem, exiting now.\n", addr); PrintZoneForPointer(addr, zone_ptr, zone_name); stack->Print(); DescribeHeapAddress(addr, 1); } void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, BufferedStackTrace *stack) { ScopedInErrorReport in_report; Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n" "This is an unrecoverable problem, exiting now.\n", addr); PrintZoneForPointer(addr, zone_ptr, zone_name); stack->Print(); DescribeHeapAddress(addr, 1); } } // namespace __asan // --------------------------- Interface --------------------- {{{1 using namespace __asan; // NOLINT void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, uptr access_size) { + ENABLE_FRAME_POINTER; + // Determine the error type. const char *bug_descr = "unknown-crash"; if (AddrIsInMem(addr)) { u8 *shadow_addr = (u8*)MemToShadow(addr); // If we are accessing 16 bytes, look at the second shadow byte. if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) shadow_addr++; // If we are in the partial right redzone, look at the next shadow byte. if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++; switch (*shadow_addr) { case kAsanHeapLeftRedzoneMagic: case kAsanHeapRightRedzoneMagic: case kAsanArrayCookieMagic: bug_descr = "heap-buffer-overflow"; break; case kAsanHeapFreeMagic: bug_descr = "heap-use-after-free"; break; case kAsanStackLeftRedzoneMagic: bug_descr = "stack-buffer-underflow"; break; case kAsanInitializationOrderMagic: bug_descr = "initialization-order-fiasco"; break; case kAsanStackMidRedzoneMagic: case kAsanStackRightRedzoneMagic: case kAsanStackPartialRedzoneMagic: bug_descr = "stack-buffer-overflow"; break; case kAsanStackAfterReturnMagic: bug_descr = "stack-use-after-return"; break; case kAsanUserPoisonedMemoryMagic: bug_descr = "use-after-poison"; break; case kAsanContiguousContainerOOBMagic: bug_descr = "container-overflow"; break; case kAsanStackUseAfterScopeMagic: bug_descr = "stack-use-after-scope"; break; case kAsanGlobalRedzoneMagic: bug_descr = "global-buffer-overflow"; break; case kAsanIntraObjectRedzone: bug_descr = "intra-object-overflow"; break; case kAsanAllocaLeftMagic: case kAsanAllocaRightMagic: bug_descr = "dynamic-stack-buffer-overflow"; break; } } ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size, bug_descr }; ScopedInErrorReport in_report(&report); Decorator d; Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: %s on address " "%p at pc %p bp %p sp %p\n", bug_descr, (void*)addr, pc, bp, sp); Printf("%s", d.EndWarning()); u32 curr_tid = GetCurrentTidOrInvalid(); char tname[128]; Printf("%s%s of size %zu at %p thread T%d%s%s\n", d.Access(), access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size, (void*)addr, curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)), d.EndAccess()); GET_STACK_TRACE_FATAL(pc, bp); stack.Print(); DescribeAddress(addr, access_size); ReportErrorSummary(bug_descr, &stack); PrintShadowMemoryForAddress(addr); } void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { error_report_callback = callback; if (callback) { error_message_buffer_size = 1 << 16; error_message_buffer = (char*)MmapOrDie(error_message_buffer_size, __func__); error_message_buffer_pos = 0; } } void __asan_describe_address(uptr addr) { // Thread registry must be locked while we're describing an address. asanThreadRegistry().Lock(); DescribeAddress(addr, 1); asanThreadRegistry().Unlock(); } int __asan_report_present() { return report_happened ? 1 : 0; } uptr __asan_get_report_pc() { return report_data.pc; } uptr __asan_get_report_bp() { return report_data.bp; } uptr __asan_get_report_sp() { return report_data.sp; } uptr __asan_get_report_address() { return report_data.addr; } int __asan_get_report_access_type() { return report_data.is_write ? 1 : 0; } uptr __asan_get_report_access_size() { return report_data.access_size; } const char *__asan_get_report_description() { return report_data.description; } extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_ptr_sub(void *a, void *b) { CheckForInvalidPointerPair(a, b); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_ptr_cmp(void *a, void *b) { CheckForInvalidPointerPair(a, b); } } // extern "C" #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default implementation of __asan_on_error that does nothing // and may be overriden by user. SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE void __asan_on_error() {} #endif Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_rtl.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_rtl.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_rtl.cc (revision 279191) @@ -1,776 +1,523 @@ //===-- asan_rtl.cc -------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Main file of the ASan run-time library. //===----------------------------------------------------------------------===// #include "asan_activation.h" #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_interface_internal.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_suppressions.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "lsan/lsan_common.h" int __asan_option_detect_stack_use_after_return; // Global interface symbol. uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan. namespace __asan { uptr AsanMappingProfile[kAsanMappingProfileSize]; static void AsanDie() { static atomic_uint32_t num_calls; if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) { // Don't die twice - run a busy loop. while (1) { } } if (flags()->sleep_before_dying) { Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying); SleepForSeconds(flags()->sleep_before_dying); } if (flags()->unmap_shadow_on_exit) { if (kMidMemBeg) { UnmapOrDie((void*)kLowShadowBeg, kMidMemBeg - kLowShadowBeg); UnmapOrDie((void*)kMidMemEnd, kHighShadowEnd - kMidMemEnd); } else { UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); } } if (common_flags()->coverage) __sanitizer_cov_dump(); - if (death_callback) - death_callback(); if (flags()->abort_on_error) Abort(); internal__exit(flags()->exitcode); } static void AsanCheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file, line, cond, (uptr)v1, (uptr)v2); // FIXME: check for infinite recursion without a thread-local counter here. PRINT_CURRENT_STACK_CHECK(); Die(); } -// -------------------------- Flags ------------------------- {{{1 -static const int kDefaultMallocContextSize = 30; - -Flags asan_flags_dont_use_directly; // use via flags(). - -static const char *MaybeCallAsanDefaultOptions() { - return (&__asan_default_options) ? __asan_default_options() : ""; -} - -static const char *MaybeUseAsanDefaultOptionsCompileDefinition() { -#ifdef ASAN_DEFAULT_OPTIONS -// Stringize the macro value. -# define ASAN_STRINGIZE(x) #x -# define ASAN_STRINGIZE_OPTIONS(options) ASAN_STRINGIZE(options) - return ASAN_STRINGIZE_OPTIONS(ASAN_DEFAULT_OPTIONS); -#else - return ""; -#endif -} - -static void ParseFlagsFromString(Flags *f, const char *str) { - CommonFlags *cf = common_flags(); - ParseCommonFlagsFromString(cf, str); - CHECK((uptr)cf->malloc_context_size <= kStackTraceMax); - // Please write meaningful flag descriptions when adding new flags. - ParseFlag(str, &f->quarantine_size, "quarantine_size", - "Size (in bytes) of quarantine used to detect use-after-free " - "errors. Lower value may reduce memory usage but increase the " - "chance of false negatives."); - ParseFlag(str, &f->redzone, "redzone", - "Minimal size (in bytes) of redzones around heap objects. " - "Requirement: redzone >= 16, is a power of two."); - ParseFlag(str, &f->max_redzone, "max_redzone", - "Maximal size (in bytes) of redzones around heap objects."); - CHECK_GE(f->redzone, 16); - CHECK_GE(f->max_redzone, f->redzone); - CHECK_LE(f->max_redzone, 2048); - CHECK(IsPowerOfTwo(f->redzone)); - CHECK(IsPowerOfTwo(f->max_redzone)); - - ParseFlag(str, &f->debug, "debug", - "If set, prints some debugging information and does additional checks."); - ParseFlag(str, &f->report_globals, "report_globals", - "Controls the way to handle globals (0 - don't detect buffer overflow on " - "globals, 1 - detect buffer overflow, 2 - print data about registered " - "globals)."); - - ParseFlag(str, &f->check_initialization_order, - "check_initialization_order", - "If set, attempts to catch initialization order issues."); - - ParseFlag(str, &f->replace_str, "replace_str", - "If set, uses custom wrappers and replacements for libc string functions " - "to find more errors."); - - ParseFlag(str, &f->replace_intrin, "replace_intrin", - "If set, uses custom wrappers for memset/memcpy/memmove intinsics."); - ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free", - "Ignore invalid free() calls to work around some bugs. Used on OS X " - "only."); - ParseFlag(str, &f->detect_stack_use_after_return, - "detect_stack_use_after_return", - "Enables stack-use-after-return checking at run-time."); - ParseFlag(str, &f->min_uar_stack_size_log, "min_uar_stack_size_log", - "Minimum fake stack size log."); - ParseFlag(str, &f->max_uar_stack_size_log, "max_uar_stack_size_log", - "Maximum fake stack size log."); - ParseFlag(str, &f->uar_noreserve, "uar_noreserve", - "Use mmap with 'norserve' flag to allocate fake stack."); - ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size", - "ASan allocator flag. max_malloc_fill_size is the maximal amount of " - "bytes that will be filled with malloc_fill_byte on malloc."); - ParseFlag(str, &f->malloc_fill_byte, "malloc_fill_byte", - "Value used to fill the newly allocated memory."); - ParseFlag(str, &f->exitcode, "exitcode", - "Override the program exit status if the tool found an error."); - ParseFlag(str, &f->allow_user_poisoning, "allow_user_poisoning", - "If set, user may manually mark memory regions as poisoned or " - "unpoisoned."); - ParseFlag(str, &f->sleep_before_dying, "sleep_before_dying", - "Number of seconds to sleep between printing an error report and " - "terminating the program. Useful for debugging purposes (e.g. when one " - "needs to attach gdb)."); - - ParseFlag(str, &f->check_malloc_usable_size, "check_malloc_usable_size", - "Allows the users to work around the bug in Nvidia drivers prior to " - "295.*."); - - ParseFlag(str, &f->unmap_shadow_on_exit, "unmap_shadow_on_exit", - "If set, explicitly unmaps the (huge) shadow at exit."); - ParseFlag(str, &f->abort_on_error, "abort_on_error", - "If set, the tool calls abort() instead of _exit() after printing the " - "error report."); - ParseFlag(str, &f->print_stats, "print_stats", - "Print various statistics after printing an error message or if " - "atexit=1."); - ParseFlag(str, &f->print_legend, "print_legend", - "Print the legend for the shadow bytes."); - ParseFlag(str, &f->atexit, "atexit", - "If set, prints ASan exit stats even after program terminates " - "successfully."); - - ParseFlag(str, &f->allow_reexec, "allow_reexec", - "Allow the tool to re-exec the program. This may interfere badly with " - "the debugger."); - - ParseFlag(str, &f->print_full_thread_history, - "print_full_thread_history", - "If set, prints thread creation stacks for the threads involved in the " - "report and their ancestors up to the main thread."); - - ParseFlag(str, &f->poison_heap, "poison_heap", - "Poison (or not) the heap memory on [de]allocation. Zero value is useful " - "for benchmarking the allocator or instrumentator."); - - ParseFlag(str, &f->poison_array_cookie, "poison_array_cookie", - "Poison (or not) the array cookie after operator new[]."); - - ParseFlag(str, &f->poison_partial, "poison_partial", - "If true, poison partially addressable 8-byte aligned words " - "(default=true). This flag affects heap and global buffers, but not " - "stack buffers."); - - ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch", - "Report errors on malloc/delete, new/free, new/delete[], etc."); - - ParseFlag(str, &f->new_delete_type_mismatch, "new_delete_type_mismatch", - "Report errors on mismatch betwen size of new and delete."); - - ParseFlag(str, &f->strict_memcmp, "strict_memcmp", - "If true, assume that memcmp(p1, p2, n) always reads n bytes before " - "comparing p1 and p2."); - - ParseFlag(str, &f->strict_init_order, "strict_init_order", - "If true, assume that dynamic initializers can never access globals from " - "other modules, even if the latter are already initialized."); - - ParseFlag(str, &f->start_deactivated, "start_deactivated", - "If true, ASan tweaks a bunch of other flags (quarantine, redzone, heap " - "poisoning) to reduce memory consumption as much as possible, and " - "restores them to original values when the first instrumented module is " - "loaded into the process. This is mainly intended to be used on " - "Android. "); - - ParseFlag(str, &f->detect_invalid_pointer_pairs, - "detect_invalid_pointer_pairs", - "If non-zero, try to detect operations like <, <=, >, >= and - on " - "invalid pointer pairs (e.g. when pointers belong to different objects). " - "The bigger the value the harder we try."); - - ParseFlag(str, &f->detect_container_overflow, - "detect_container_overflow", - "If true, honor the container overflow annotations. " - "See https://code.google.com/p/address-sanitizer/wiki/ContainerOverflow"); - - ParseFlag(str, &f->detect_odr_violation, "detect_odr_violation", - "If >=2, detect violation of One-Definition-Rule (ODR); " - "If ==1, detect ODR-violation only if the two variables " - "have different sizes"); - - ParseFlag(str, &f->dump_instruction_bytes, "dump_instruction_bytes", - "If true, dump 16 bytes starting at the instruction that caused SEGV"); -} - -void InitializeFlags(Flags *f, const char *env) { - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - cf->detect_leaks = CAN_SANITIZE_LEAKS; - cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); - cf->malloc_context_size = kDefaultMallocContextSize; - cf->intercept_tls_get_addr = true; - cf->coverage = false; - - internal_memset(f, 0, sizeof(*f)); - f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; - f->redzone = 16; - f->max_redzone = 2048; - f->debug = false; - f->report_globals = 1; - f->check_initialization_order = false; - f->replace_str = true; - f->replace_intrin = true; - f->mac_ignore_invalid_free = false; - f->detect_stack_use_after_return = false; // Also needs the compiler flag. - f->min_uar_stack_size_log = 16; // We can't do smaller anyway. - f->max_uar_stack_size_log = 20; // 1Mb per size class, i.e. ~11Mb per thread. - f->uar_noreserve = false; - f->max_malloc_fill_size = 0x1000; // By default, fill only the first 4K. - f->malloc_fill_byte = 0xbe; - f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE; - f->allow_user_poisoning = true; - f->sleep_before_dying = 0; - f->check_malloc_usable_size = true; - f->unmap_shadow_on_exit = false; - f->abort_on_error = false; - f->print_stats = false; - f->print_legend = true; - f->atexit = false; - f->allow_reexec = true; - f->print_full_thread_history = true; - f->poison_heap = true; - f->poison_array_cookie = true; - f->poison_partial = true; - // Turn off alloc/dealloc mismatch checker on Mac and Windows for now. - // https://code.google.com/p/address-sanitizer/issues/detail?id=131 - // https://code.google.com/p/address-sanitizer/issues/detail?id=309 - // TODO(glider,timurrrr): Fix known issues and enable this back. - f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0); - f->new_delete_type_mismatch = true; - f->strict_memcmp = true; - f->strict_init_order = false; - f->start_deactivated = false; - f->detect_invalid_pointer_pairs = 0; - f->detect_container_overflow = true; - f->detect_odr_violation = 2; - f->dump_instruction_bytes = false; - - // Override from compile definition. - ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefinition()); - - // Override from user-specified string. - ParseFlagsFromString(f, MaybeCallAsanDefaultOptions()); - VReport(1, "Using the defaults from __asan_default_options: %s\n", - MaybeCallAsanDefaultOptions()); - - // Override from command line. - ParseFlagsFromString(f, env); - if (common_flags()->help) { - PrintFlagDescriptions(); - } - - if (!CAN_SANITIZE_LEAKS && cf->detect_leaks) { - Report("%s: detect_leaks is not supported on this platform.\n", - SanitizerToolName); - cf->detect_leaks = false; - } - - // Make "strict_init_order" imply "check_initialization_order". - // TODO(samsonov): Use a single runtime flag for an init-order checker. - if (f->strict_init_order) { - f->check_initialization_order = true; - } -} - -// Parse flags that may change between startup and activation. -// On Android they come from a system property. -// On other platforms this is no-op. -void ParseExtraActivationFlags() { - char buf[100]; - GetExtraActivationFlags(buf, sizeof(buf)); - ParseFlagsFromString(flags(), buf); - if (buf[0] != '\0') - VReport(1, "Extra activation flags: %s\n", buf); -} - // -------------------------- Globals --------------------- {{{1 int asan_inited; bool asan_init_is_running; -void (*death_callback)(void); #if !ASAN_FIXED_MAPPING uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; #endif // -------------------------- Misc ---------------- {{{1 void ShowStatsAndAbort() { __asan_print_accumulated_stats(); Die(); } // ---------------------- mmap -------------------- {{{1 // Reserve memory range [beg, end]. -static void ReserveShadowMemoryRange(uptr beg, uptr end) { +// We need to use inclusive range because end+1 may not be representable. +void ReserveShadowMemoryRange(uptr beg, uptr end) { 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); if (res != (void*)beg) { Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " "Perhaps you're using ulimit -v\n", size); Abort(); } + if (common_flags()->no_huge_pages_for_shadow) + NoHugePagesInRegion(beg, size); + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(beg, size); } // --------------- LowLevelAllocateCallbac ---------- {{{1 static void OnLowLevelAllocate(uptr ptr, uptr size) { PoisonShadow(ptr, size, kAsanInternalHeapMagic); } // -------------------------- 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) { \ GET_CALLER_PC_BP_SP; \ __asan_report_error(pc, bp, sp, addr, is_write, size); \ } ASAN_REPORT_ERROR(load, false, 1) ASAN_REPORT_ERROR(load, false, 2) ASAN_REPORT_ERROR(load, false, 4) ASAN_REPORT_ERROR(load, false, 8) ASAN_REPORT_ERROR(load, false, 16) ASAN_REPORT_ERROR(store, true, 1) ASAN_REPORT_ERROR(store, true, 2) ASAN_REPORT_ERROR(store, true, 4) ASAN_REPORT_ERROR(store, true, 8) 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_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) { \ uptr sp = MEM_TO_SHADOW(addr); \ uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast(sp) \ : *reinterpret_cast(sp); \ if (UNLIKELY(s)) { \ if (UNLIKELY(size >= SHADOW_GRANULARITY || \ ((s8)((addr & (SHADOW_GRANULARITY - 1)) + size - 1)) >= \ (s8)s)) { \ if (__asan_test_only_reported_buggy_pointer) { \ *__asan_test_only_reported_buggy_pointer = addr; \ } else { \ GET_CALLER_PC_BP_SP; \ __asan_report_error(pc, bp, sp, addr, is_write, size); \ } \ } \ } \ } ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1) ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2) ASAN_MEMORY_ACCESS_CALLBACK(load, false, 4) ASAN_MEMORY_ACCESS_CALLBACK(load, false, 8) ASAN_MEMORY_ACCESS_CALLBACK(load, false, 16) ASAN_MEMORY_ACCESS_CALLBACK(store, true, 1) ASAN_MEMORY_ACCESS_CALLBACK(store, true, 2) ASAN_MEMORY_ACCESS_CALLBACK(store, true, 4) 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) { if (__asan_region_is_poisoned(addr, size)) { GET_CALLER_PC_BP_SP; __asan_report_error(pc, bp, sp, addr, false, size); } } 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); } } // Force the linker to keep the symbols for various ASan interface functions. // We want to keep those in the executable in order to let the instrumented // dynamic libraries access the symbol even if it is not used by the executable // itself. This should help if the build system is removing dead code at link // time. static NOINLINE void force_interface_symbols() { volatile int fake_condition = 0; // prevent dead condition elimination. // __asan_report_* functions are noreturn, so we need a switch to prevent // the compiler from removing any of them. switch (fake_condition) { case 1: __asan_report_load1(0); break; case 2: __asan_report_load2(0); break; 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; } } static void asan_atexit() { Printf("AddressSanitizer exit stats:\n"); __asan_print_accumulated_stats(); // Print AsanMappingProfile. for (uptr i = 0; i < kAsanMappingProfileSize; i++) { if (AsanMappingProfile[i] == 0) continue; Printf("asan_mapping.h:%zd -- %zd\n", i, AsanMappingProfile[i]); } } static void InitializeHighMemEnd() { #if !ASAN_FIXED_MAPPING kHighMemEnd = GetMaxVirtualAddress(); // Increase kHighMemEnd to make sure it's properly // aligned together with kHighMemBeg: kHighMemEnd |= SHADOW_GRANULARITY * GetPageSizeCached() - 1; #endif // !ASAN_FIXED_MAPPING CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0); } static void ProtectGap(uptr a, uptr size) { - CHECK_EQ(a, (uptr)Mprotect(a, size)); + void *res = Mprotect(a, size); + if (a == (uptr)res) + return; + Report("ERROR: Failed to protect the shadow gap. " + "ASan cannot proceed correctly. ABORTING.\n"); + DumpProcessMap(); + Die(); } static void PrintAddressSpaceLayout() { Printf("|| `[%p, %p]` || HighMem ||\n", (void*)kHighMemBeg, (void*)kHighMemEnd); Printf("|| `[%p, %p]` || HighShadow ||\n", (void*)kHighShadowBeg, (void*)kHighShadowEnd); if (kMidMemBeg) { Printf("|| `[%p, %p]` || ShadowGap3 ||\n", (void*)kShadowGap3Beg, (void*)kShadowGap3End); Printf("|| `[%p, %p]` || MidMem ||\n", (void*)kMidMemBeg, (void*)kMidMemEnd); Printf("|| `[%p, %p]` || ShadowGap2 ||\n", (void*)kShadowGap2Beg, (void*)kShadowGap2End); Printf("|| `[%p, %p]` || MidShadow ||\n", (void*)kMidShadowBeg, (void*)kMidShadowEnd); } Printf("|| `[%p, %p]` || ShadowGap ||\n", (void*)kShadowGapBeg, (void*)kShadowGapEnd); if (kLowShadowBeg) { Printf("|| `[%p, %p]` || LowShadow ||\n", (void*)kLowShadowBeg, (void*)kLowShadowEnd); Printf("|| `[%p, %p]` || LowMem ||\n", (void*)kLowMemBeg, (void*)kLowMemEnd); } Printf("MemToShadow(shadow): %p %p %p %p", (void*)MEM_TO_SHADOW(kLowShadowBeg), (void*)MEM_TO_SHADOW(kLowShadowEnd), (void*)MEM_TO_SHADOW(kHighShadowBeg), (void*)MEM_TO_SHADOW(kHighShadowEnd)); if (kMidMemBeg) { Printf(" %p %p", (void*)MEM_TO_SHADOW(kMidShadowBeg), (void*)MEM_TO_SHADOW(kMidShadowEnd)); } Printf("\n"); Printf("redzone=%zu\n", (uptr)flags()->redzone); Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone); - Printf("quarantine_size=%zuM\n", (uptr)flags()->quarantine_size >> 20); + Printf("quarantine_size_mb=%zuM\n", (uptr)flags()->quarantine_size_mb); 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); CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7); if (kMidMemBeg) CHECK(kMidShadowBeg > kLowShadowEnd && kMidMemBeg > kMidShadowEnd && kHighShadowBeg > kMidMemEnd); } static void AsanInitInternal() { if (LIKELY(asan_inited)) return; SanitizerToolName = "AddressSanitizer"; CHECK(!asan_init_is_running && "ASan init calls itself!"); asan_init_is_running = true; // Initialize flags. This must be done early, because most of the // initialization steps look at flags(). - const char *options = GetEnv("ASAN_OPTIONS"); - InitializeFlags(flags(), options); + InitializeFlags(flags()); + SetCanPoisonMemory(flags()->poison_heap); + SetMallocContextSize(common_flags()->malloc_context_size); + InitializeHighMemEnd(); // Make sure we are not statically linked. AsanDoesNotSupportStaticLinkage(); // Install tool-specific callbacks in sanitizer_common. SetDieCallback(AsanDie); SetCheckFailedCallback(AsanCheckFailed); SetPrintfAndReportCallback(AppendToErrorMessageBuffer); - if (!flags()->start_deactivated) - ParseExtraActivationFlags(); - __sanitizer_set_report_path(common_flags()->log_path); + + // Enable UAR detection, if required. __asan_option_detect_stack_use_after_return = flags()->detect_stack_use_after_return; - CHECK_LE(flags()->min_uar_stack_size_log, flags()->max_uar_stack_size_log); - if (options) { - VReport(1, "Parsed ASAN_OPTIONS: %s\n", options); - } - - if (flags()->start_deactivated) - AsanStartDeactivated(); - // Re-exec ourselves if we need to set additional env or command line args. MaybeReexec(); // Setup internal allocator callback. SetLowLevelAllocateCallback(OnLowLevelAllocate); InitializeAsanInterceptors(); // Enable system log ("adb logcat") on Android. // Doing this before interceptors are initialized crashes in: // AsanInitInternal -> android_log_write -> __interceptor_strcmp AndroidLogInit(); ReplaceSystemMalloc(); uptr shadow_start = kLowShadowBeg; if (kLowShadowBeg) shadow_start -= GetMmapGranularity(); bool full_shadow_is_available = MemoryRangeIsAvailable(shadow_start, kHighShadowEnd); #if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \ !ASAN_FIXED_MAPPING if (!full_shadow_is_available) { kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0; kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0; } #endif - if (common_flags()->verbosity) - PrintAddressSpaceLayout(); + if (Verbosity()) PrintAddressSpaceLayout(); DisableCoreDumperIfNecessary(); if (full_shadow_is_available) { // mmap the low shadow plus at least one page at the left. if (kLowShadowBeg) ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); // mmap the high shadow. ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); // protect the gap. ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); } else if (kMidMemBeg && MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { CHECK(kLowShadowBeg != kLowShadowEnd); // mmap the low shadow plus at least one page at the left. ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); // mmap the mid shadow. ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd); // mmap the high shadow. ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); // protect the gaps. ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1); ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1); } else { Report("Shadow memory range interleaves with an existing memory mapping. " "ASan cannot proceed correctly. ABORTING.\n"); + Report("ASan shadow was supposed to be located in the [%p-%p] range.\n", + shadow_start, kHighShadowEnd); DumpProcessMap(); Die(); } AsanTSDInit(PlatformTSDDtor); InstallDeadlySignalHandlers(AsanOnSIGSEGV); - InitializeAllocator(); + AllocatorOptions allocator_options; + allocator_options.SetFrom(flags(), common_flags()); + InitializeAllocator(allocator_options); + MaybeStartBackgroudThread(); + SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback); + // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited // should be set to 1 prior to initializing the threads. asan_inited = 1; asan_init_is_running = false; if (flags()->atexit) Atexit(asan_atexit); - if (common_flags()->coverage) { - __sanitizer_cov_init(); - Atexit(__sanitizer_cov_dump); - } + InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); + // Now that ASan runtime is (mostly) initialized, deactivate it if + // necessary, so that it can be re-activated when requested. + if (flags()->start_deactivated) + AsanDeactivate(); + // interceptors InitTlsSize(); // Create main thread. AsanThread *main_thread = AsanThread::Create( /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ 0, /* stack */ nullptr, /* detached */ true); CHECK_EQ(0, main_thread->tid()); SetCurrentThread(main_thread); main_thread->ThreadStart(internal_getpid(), /* signal_thread_is_registered */ nullptr); force_interface_symbols(); // no-op. SanitizerInitializeUnwinder(); #if CAN_SANITIZE_LEAKS __lsan::InitCommonLsan(false); if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { Atexit(__lsan::DoLeakCheck); } #endif // CAN_SANITIZE_LEAKS InitializeSuppressions(); VReport(1, "AddressSanitizer Init done\n"); } // Initialize as requested from some part of ASan runtime library (interceptors, // allocator, etc). void AsanInitFromRtl() { AsanInitInternal(); } #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). class AsanInitializer { public: // NOLINT AsanInitializer() { AsanCheckIncompatibleRT(); AsanCheckDynamicRTPrereqs(); AsanInitFromRtl(); } }; static AsanInitializer asan_initializer; #endif // ASAN_DYNAMIC } // namespace __asan // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT -#if !SANITIZER_SUPPORTS_WEAK_HOOKS -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -const char* __asan_default_options() { return ""; } -} // extern "C" -#endif - int NOINLINE __asan_set_error_exit_code(int exit_code) { int old = flags()->exitcode; flags()->exitcode = exit_code; return old; } void NOINLINE __asan_handle_no_return() { int local_stack; AsanThread *curr_thread = GetCurrentThread(); CHECK(curr_thread); uptr PageSize = GetPageSizeCached(); uptr top = curr_thread->stack_top(); uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1); static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M if (top - bottom > kMaxExpectedCleanupSize) { static bool reported_warning = false; if (reported_warning) return; reported_warning = true; Report("WARNING: ASan is ignoring requested __asan_handle_no_return: " "stack top: %p; bottom %p; size: %p (%zd)\n" "False positive error reports may follow\n" "For details see " "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n", top, bottom, top - bottom, top - bottom); return; } PoisonShadow(bottom, top - bottom, 0); if (curr_thread->has_fake_stack()) curr_thread->fake_stack()->HandleNoReturn(); } void NOINLINE __asan_set_death_callback(void (*callback)(void)) { - death_callback = callback; + SetUserDieCallback(callback); } // 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(); } Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_stack.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_stack.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_stack.cc (revision 279191) @@ -1,25 +1,40 @@ //===-- asan_stack.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Code for ASan stack trace. //===----------------------------------------------------------------------===// #include "asan_internal.h" #include "asan_stack.h" +#include "sanitizer_common/sanitizer_atomic.h" + +namespace __asan { + +static atomic_uint32_t malloc_context_size; + +void SetMallocContextSize(u32 size) { + atomic_store(&malloc_context_size, size, memory_order_release); +} + +u32 GetMallocContextSize() { + return atomic_load(&malloc_context_size, memory_order_acquire); +} + +} // namespace __asan // ------------------ Interface -------------- {{{1 extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_print_stack_trace() { using namespace __asan; PRINT_CURRENT_STACK(); } } // extern "C" Index: projects/clang360-import/contrib/compiler-rt/lib/asan/asan_stack.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/asan_stack.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/asan_stack.h (revision 279191) @@ -1,114 +1,118 @@ //===-- asan_stack.h --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // ASan-private header for asan_stack.cc. //===----------------------------------------------------------------------===// #ifndef ASAN_STACK_H #define ASAN_STACK_H #include "asan_flags.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_stacktrace.h" namespace __asan { +static const u32 kDefaultMallocContextSize = 30; + +void SetMallocContextSize(u32 size); +u32 GetMallocContextSize(); + // Get the stack trace with the given pc and bp. // The pc will be in the position 0 of the resulting stack trace. // The bp may refer to the current frame or to the caller's frame. ALWAYS_INLINE void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth, uptr pc, uptr bp, void *context, bool fast) { #if SANITIZER_WINDOWS stack->Unwind(max_depth, pc, bp, context, 0, 0, fast); #else AsanThread *t; stack->size = 0; if (LIKELY(asan_inited)) { if ((t = GetCurrentThread()) && !t->isUnwinding()) { // On FreeBSD the slow unwinding that leverages _Unwind_Backtrace() // yields the call stack of the signal's handler and not of the code // that raised the signal (as it does on Linux). if (SANITIZER_FREEBSD && t->isInDeadlySignal()) fast = true; uptr stack_top = t->stack_top(); uptr stack_bottom = t->stack_bottom(); ScopedUnwinding unwind_scope(t); stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom, fast); } else if (t == 0 && !fast) { /* If GetCurrentThread() has failed, try to do slow unwind anyways. */ stack->Unwind(max_depth, pc, bp, context, 0, 0, false); } } #endif // SANITIZER_WINDOWS } } // namespace __asan // NOTE: A Rule of thumb is to retrieve stack trace in the interceptors // as early as possible (in functions exposed to the user), as we generally // don't want stack trace to contain functions from ASan internals. #define GET_STACK_TRACE(max_size, fast) \ BufferedStackTrace stack; \ if (max_size <= 2) { \ stack.size = max_size; \ if (max_size > 0) { \ stack.top_frame_bp = GET_CURRENT_FRAME(); \ stack.trace_buffer[0] = StackTrace::GetCurrentPc(); \ if (max_size > 1) \ stack.trace_buffer[1] = GET_CALLER_PC(); \ } \ } else { \ GetStackTraceWithPcBpAndContext(&stack, max_size, \ StackTrace::GetCurrentPc(), \ GET_CURRENT_FRAME(), 0, fast); \ } #define GET_STACK_TRACE_FATAL(pc, bp) \ BufferedStackTrace stack; \ GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, 0, \ common_flags()->fast_unwind_on_fatal) #define GET_STACK_TRACE_SIGNAL(sig) \ BufferedStackTrace stack; \ GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, \ (sig).pc, (sig).bp, (sig).context, \ common_flags()->fast_unwind_on_fatal) #define GET_STACK_TRACE_FATAL_HERE \ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal) #define GET_STACK_TRACE_CHECK_HERE \ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_check) #define GET_STACK_TRACE_THREAD \ GET_STACK_TRACE(kStackTraceMax, true) -#define GET_STACK_TRACE_MALLOC \ - GET_STACK_TRACE(common_flags()->malloc_context_size, \ - common_flags()->fast_unwind_on_malloc) +#define GET_STACK_TRACE_MALLOC \ + GET_STACK_TRACE(GetMallocContextSize(), common_flags()->fast_unwind_on_malloc) #define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC #define PRINT_CURRENT_STACK() \ { \ GET_STACK_TRACE_FATAL_HERE; \ stack.Print(); \ } #define PRINT_CURRENT_STACK_CHECK() \ { \ GET_STACK_TRACE_CHECK_HERE; \ stack.Print(); \ } #endif // ASAN_STACK_H Index: projects/clang360-import/contrib/compiler-rt/lib/asan/scripts/asan_device_setup =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/scripts/asan_device_setup (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/scripts/asan_device_setup (revision 279191) @@ -1,267 +1,268 @@ #!/bin/bash #===- lib/asan/scripts/asan_device_setup -----------------------------------===# # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # # Prepare Android device to run ASan applications. # #===------------------------------------------------------------------------===# set -e HERE="$(cd "$(dirname "$0")" && pwd)" revert=no extra_options= device= lib= function usage { echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]" echo " --revert: Uninstall ASan from the device." echo " --lib: Path to ASan runtime library." echo " --extra-options: Extra ASAN_OPTIONS." echo " --device: Install to the given device. Use 'adb devices' to find" echo " device-id." echo exit 1 } function get_device_arch { # OUTVAR local _outvar=$1 local _ABI=$($ADB shell getprop ro.product.cpu.abi) local _ARCH= if [[ $_ABI == x86* ]]; then _ARCH=i686 elif [[ $_ABI == armeabi* ]]; then _ARCH=arm else echo "Unrecognized device ABI: $_ABI" exit 1 fi eval $_outvar=\$_ARCH } while [[ $# > 0 ]]; do case $1 in --revert) revert=yes ;; --extra-options) shift if [[ $# == 0 ]]; then echo "--extra-options requires an argument." exit 1 fi extra_options="$1" ;; --lib) shift if [[ $# == 0 ]]; then echo "--lib requires an argument." exit 1 fi lib="$1" ;; --device) shift if [[ $# == 0 ]]; then echo "--device requires an argument." exit 1 fi device="$1" ;; *) usage ;; esac shift done ADB=${ADB:-adb} if [[ x$device != x ]]; then ADB="$ADB -s $device" fi echo '>> Remounting /system rw' +$ADB wait-for-device $ADB root $ADB wait-for-device $ADB remount $ADB wait-for-device get_device_arch ARCH echo "Target architecture: $ARCH" ASAN_RT="libclang_rt.asan-$ARCH-android.so" if [[ x$revert == xyes ]]; then echo '>> Uninstalling ASan' if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then echo '>> Pre-L device detected.' $ADB shell mv /system/bin/app_process.real /system/bin/app_process $ADB shell rm /system/bin/asanwrapper $ADB shell rm /system/lib/$ASAN_RT else $ADB shell rm /system/bin/app_process.wrap $ADB shell rm /system/bin/asanwrapper $ADB shell rm /system/lib/$ASAN_RT $ADB shell rm /system/bin/app_process $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process fi echo '>> Restarting shell' $ADB shell stop $ADB shell start echo '>> Done' exit 0 fi if [[ -d "$lib" ]]; then ASAN_RT_PATH="$lib" elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then ASAN_RT_PATH=$(dirname "$lib") elif [[ -f "$HERE/$ASAN_RT" ]]; then ASAN_RT_PATH="$HERE" elif [[ $(basename "$HERE") == "bin" ]]; then # We could be in the toolchain's base directory. # Consider ../lib, ../lib/asan, ../lib/linux and ../lib/clang/$VERSION/lib/linux. P=$(ls "$HERE"/../lib/"$ASAN_RT" "$HERE"/../lib/asan/"$ASAN_RT" "$HERE"/../lib/linux/"$ASAN_RT" "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1) if [[ -n "$P" ]]; then ASAN_RT_PATH="$(dirname "$P")" fi fi if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then echo ">> ASan runtime library not found" exit 1 fi TMPDIRBASE=$(mktemp -d) TMPDIROLD="$TMPDIRBASE/old" TMPDIR="$TMPDIRBASE/new" mkdir "$TMPDIROLD" RELEASE=$($ADB shell getprop ro.build.version.release) PRE_L=0 if echo "$RELEASE" | grep '^4\.' >&/dev/null; then PRE_L=1 fi if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then if $ADB pull /system/bin/app_process.real /dev/null >&/dev/null; then echo '>> Old-style ASan installation detected. Reverting.' $ADB shell mv /system/bin/app_process.real /system/bin/app_process fi echo '>> Pre-L device detected. Setting up app_process symlink.' $ADB shell mv /system/bin/app_process /system/bin/app_process32 $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process fi echo '>> Copying files from the device' $ADB pull /system/bin/app_process.wrap "$TMPDIROLD" || true $ADB pull /system/bin/asanwrapper "$TMPDIROLD" || true $ADB pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true cp -r "$TMPDIROLD" "$TMPDIR" if [[ -f "$TMPDIR/app_process.wrap" ]]; then echo ">> Previous installation detected" else echo ">> New installation" fi echo '>> Generating wrappers' cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/" # FIXME: alloc_dealloc_mismatch=0 prevents a failure in libdvm startup, # which may or may not be a real bug (probably not). ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0 # On Android-L not allowing user segv handler breaks some applications. -if $ADB shell 'echo $LD_PRELOAD' | grep libsigchain.so >&/dev/null; then +if [[ PRE_L -eq 0 ]]; then ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1" fi if [[ x$extra_options != x ]] ; then ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options" fi # Zygote wrapper. cat <"$TMPDIR/app_process.wrap" #!/system/bin/sh-from-zygote ASAN_OPTIONS=$ASAN_OPTIONS \\ LD_PRELOAD=\$LD_PRELOAD:$ASAN_RT \\ exec /system/bin/app_process32 \$@ EOF # General command-line tool wrapper (use for anything that's not started as # zygote). cat <"$TMPDIR/asanwrapper" #!/system/bin/sh LD_PRELOAD=$ASAN_RT \\ exec \$@ EOF if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then echo '>> Pushing files to the device' $ADB push "$TMPDIR/$ASAN_RT" /system/lib/ $ADB push "$TMPDIR/app_process.wrap" /system/bin/app_process.wrap $ADB push "$TMPDIR/asanwrapper" /system/bin/asanwrapper $ADB shell rm /system/bin/app_process $ADB shell ln -s /system/bin/app_process.wrap /system/bin/app_process $ADB shell chown root.shell \ /system/lib/"$ASAN_RT" \ /system/bin/app_process.wrap \ /system/bin/asanwrapper $ADB shell chmod 644 \ /system/lib/"$ASAN_RT" $ADB shell chmod 755 \ /system/bin/app_process.wrap \ /system/bin/asanwrapper # Make SELinux happy by keeping app_process wrapper and the shell # it runs on in zygote domain. ENFORCING=0 if $ADB shell getenforce | grep Enforcing >/dev/null; then # Sometimes shell is not allowed to change file contexts. # Temporarily switch to permissive. ENFORCING=1 $ADB shell setenforce 0 fi $ADB shell cp /system/bin/sh /system/bin/sh-from-zygote if [[ PRE_L -eq 1 ]]; then CTX=u:object_r:system_file:s0 else CTX=u:object_r:zygote_exec:s0 fi $ADB shell chcon $CTX \ /system/bin/sh-from-zygote \ /system/bin/app_process.wrap \ /system/bin/app_process32 if [ $ENFORCING == 1 ]; then $ADB shell setenforce 1 fi echo '>> Restarting shell (asynchronous)' $ADB shell stop $ADB shell start echo '>> Please wait until the device restarts' else echo '>> Device is up to date' fi rm -r "$TMPDIRBASE" Index: projects/clang360-import/contrib/compiler-rt/lib/asan/scripts/asan_symbolize.py =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/scripts/asan_symbolize.py (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/scripts/asan_symbolize.py (revision 279191) @@ -1,469 +1,479 @@ #!/usr/bin/env python #===- lib/asan/scripts/asan_symbolize.py -----------------------------------===# # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # #===------------------------------------------------------------------------===# import argparse import bisect import getopt import os -import pty import re import subprocess import sys -import termios symbolizers = {} DEBUG = False demangle = False binutils_prefix = None sysroot_path = None binary_name_filter = None fix_filename_patterns = None logfile = sys.stdin # FIXME: merge the code that calls fix_filename(). def fix_filename(file_name): if fix_filename_patterns: for path_to_cut in fix_filename_patterns: file_name = re.sub('.*' + path_to_cut, '', file_name) file_name = re.sub('.*asan_[a-z_]*.cc:[0-9]*', '_asan_rtl_', file_name) file_name = re.sub('.*crtstuff.c:0', '???:0', file_name) return file_name def sysroot_path_filter(binary_name): return sysroot_path + binary_name def guess_arch(addr): # Guess which arch we're running. 10 = len('0x') + 8 hex digits. if len(addr) > 10: return 'x86_64' else: return 'i386' class Symbolizer(object): def __init__(self): pass def symbolize(self, addr, binary, offset): """Symbolize the given address (pair of binary and offset). Overriden in subclasses. Args: addr: virtual address of an instruction. binary: path to executable/shared object containing this instruction. offset: instruction offset in the @binary. Returns: list of strings (one string for each inlined frame) describing the code locations for this instruction (that is, function name, file name, line and column numbers). """ return None class LLVMSymbolizer(Symbolizer): def __init__(self, symbolizer_path, default_arch, system, dsym_hints=[]): super(LLVMSymbolizer, self).__init__() self.symbolizer_path = symbolizer_path self.default_arch = default_arch self.system = system self.dsym_hints = dsym_hints self.pipe = self.open_llvm_symbolizer() def open_llvm_symbolizer(self): cmd = [self.symbolizer_path, '--use-symbol-table=true', '--demangle=%s' % demangle, '--functions=short', '--inlining=true', '--default-arch=%s' % self.default_arch] if self.system == 'Darwin': for hint in self.dsym_hints: cmd.append('--dsym-hint=%s' % hint) if DEBUG: print ' '.join(cmd) try: result = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) except OSError: result = None return result def symbolize(self, addr, binary, offset): """Overrides Symbolizer.symbolize.""" if not self.pipe: return None result = [] try: symbolizer_input = '"%s" %s' % (binary, offset) if DEBUG: print symbolizer_input print >> self.pipe.stdin, symbolizer_input while True: function_name = self.pipe.stdout.readline().rstrip() if not function_name: break file_name = self.pipe.stdout.readline().rstrip() file_name = fix_filename(file_name) if (not function_name.startswith('??') or not file_name.startswith('??')): # Append only non-trivial frames. result.append('%s in %s %s' % (addr, function_name, file_name)) except Exception: result = [] if not result: result = None return result def LLVMSymbolizerFactory(system, default_arch, dsym_hints=[]): symbolizer_path = os.getenv('LLVM_SYMBOLIZER_PATH') if not symbolizer_path: symbolizer_path = os.getenv('ASAN_SYMBOLIZER_PATH') if not symbolizer_path: # Assume llvm-symbolizer is in PATH. symbolizer_path = 'llvm-symbolizer' return LLVMSymbolizer(symbolizer_path, default_arch, system, dsym_hints) class Addr2LineSymbolizer(Symbolizer): def __init__(self, binary): super(Addr2LineSymbolizer, self).__init__() self.binary = binary self.pipe = self.open_addr2line() def open_addr2line(self): addr2line_tool = 'addr2line' if binutils_prefix: addr2line_tool = binutils_prefix + addr2line_tool cmd = [addr2line_tool, '-f'] if demangle: cmd += ['--demangle'] cmd += ['-e', self.binary] if DEBUG: print ' '.join(cmd) return subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) def symbolize(self, addr, binary, offset): """Overrides Symbolizer.symbolize.""" if self.binary != binary: return None try: print >> self.pipe.stdin, offset function_name = self.pipe.stdout.readline().rstrip() file_name = self.pipe.stdout.readline().rstrip() except Exception: function_name = '' file_name = '' file_name = fix_filename(file_name) return ['%s in %s %s' % (addr, function_name, file_name)] class UnbufferedLineConverter(object): """ Wrap a child process that responds to each line of input with one line of output. Uses pty to trick the child into providing unbuffered output. """ def __init__(self, args, close_stderr=False): + # Local imports so that the script can start on Windows. + import pty + import termios pid, fd = pty.fork() if pid == 0: # We're the child. Transfer control to command. if close_stderr: dev_null = os.open('/dev/null', 0) os.dup2(dev_null, 2) os.execvp(args[0], args) else: # Disable echoing. attr = termios.tcgetattr(fd) attr[3] = attr[3] & ~termios.ECHO termios.tcsetattr(fd, termios.TCSANOW, attr) # Set up a file()-like interface to the child process self.r = os.fdopen(fd, "r", 1) self.w = os.fdopen(os.dup(fd), "w", 1) def convert(self, line): self.w.write(line + "\n") return self.readline() def readline(self): return self.r.readline().rstrip() class DarwinSymbolizer(Symbolizer): def __init__(self, addr, binary): super(DarwinSymbolizer, self).__init__() self.binary = binary self.arch = guess_arch(addr) self.open_atos() def open_atos(self): if DEBUG: print 'atos -o %s -arch %s' % (self.binary, self.arch) cmdline = ['atos', '-o', self.binary, '-arch', self.arch] self.atos = UnbufferedLineConverter(cmdline, close_stderr=True) def symbolize(self, addr, binary, offset): """Overrides Symbolizer.symbolize.""" if self.binary != binary: return None atos_line = self.atos.convert('0x%x' % int(offset, 16)) while "got symbolicator for" in atos_line: atos_line = self.atos.readline() # A well-formed atos response looks like this: # foo(type1, type2) (in object.name) (filename.cc:80) match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line) if DEBUG: print 'atos_line: ', atos_line if match: function_name = match.group(1) function_name = re.sub('\(.*?\)', '', function_name) file_name = fix_filename(match.group(3)) return ['%s in %s %s' % (addr, function_name, file_name)] else: return ['%s in %s' % (addr, atos_line)] # Chain several symbolizers so that if one symbolizer fails, we fall back # to the next symbolizer in chain. class ChainSymbolizer(Symbolizer): def __init__(self, symbolizer_list): super(ChainSymbolizer, self).__init__() self.symbolizer_list = symbolizer_list def symbolize(self, addr, binary, offset): """Overrides Symbolizer.symbolize.""" for symbolizer in self.symbolizer_list: if symbolizer: result = symbolizer.symbolize(addr, binary, offset) if result: return result return None def append_symbolizer(self, symbolizer): self.symbolizer_list.append(symbolizer) def BreakpadSymbolizerFactory(binary): suffix = os.getenv('BREAKPAD_SUFFIX') if suffix: filename = binary + suffix if os.access(filename, os.F_OK): return BreakpadSymbolizer(filename) return None def SystemSymbolizerFactory(system, addr, binary): if system == 'Darwin': return DarwinSymbolizer(addr, binary) elif system == 'Linux': return Addr2LineSymbolizer(binary) class BreakpadSymbolizer(Symbolizer): def __init__(self, filename): super(BreakpadSymbolizer, self).__init__() self.filename = filename lines = file(filename).readlines() self.files = [] self.symbols = {} self.address_list = [] self.addresses = {} # MODULE mac x86_64 A7001116478B33F18FF9BEDE9F615F190 t fragments = lines[0].rstrip().split() self.arch = fragments[2] self.debug_id = fragments[3] self.binary = ' '.join(fragments[4:]) self.parse_lines(lines[1:]) def parse_lines(self, lines): cur_function_addr = '' for line in lines: fragments = line.split() if fragments[0] == 'FILE': assert int(fragments[1]) == len(self.files) self.files.append(' '.join(fragments[2:])) elif fragments[0] == 'PUBLIC': self.symbols[int(fragments[1], 16)] = ' '.join(fragments[3:]) elif fragments[0] in ['CFI', 'STACK']: pass elif fragments[0] == 'FUNC': cur_function_addr = int(fragments[1], 16) if not cur_function_addr in self.symbols.keys(): self.symbols[cur_function_addr] = ' '.join(fragments[4:]) else: # Line starting with an address. addr = int(fragments[0], 16) self.address_list.append(addr) # Tuple of symbol address, size, line, file number. self.addresses[addr] = (cur_function_addr, int(fragments[1], 16), int(fragments[2]), int(fragments[3])) self.address_list.sort() def get_sym_file_line(self, addr): key = None if addr in self.addresses.keys(): key = addr else: index = bisect.bisect_left(self.address_list, addr) if index == 0: return None else: key = self.address_list[index - 1] sym_id, size, line_no, file_no = self.addresses[key] symbol = self.symbols[sym_id] filename = self.files[file_no] if addr < key + size: return symbol, filename, line_no else: return None def symbolize(self, addr, binary, offset): if self.binary != binary: return None res = self.get_sym_file_line(int(offset, 16)) if res: function_name, file_name, line_no = res result = ['%s in %s %s:%d' % ( addr, function_name, file_name, line_no)] print result return result else: return None class SymbolizationLoop(object): def __init__(self, binary_name_filter=None, dsym_hint_producer=None): - # Used by clients who may want to supply a different binary name. - # E.g. in Chrome several binaries may share a single .dSYM. - self.binary_name_filter = binary_name_filter - self.dsym_hint_producer = dsym_hint_producer - self.system = os.uname()[0] - if self.system not in ['Linux', 'Darwin', 'FreeBSD']: - raise Exception('Unknown system') - self.llvm_symbolizers = {} - self.last_llvm_symbolizer = None - self.dsym_hints = set([]) - self.frame_no = 0 + if sys.platform == 'win32': + # ASan on Windows uses dbghelp.dll to symbolize in-process, which works + # even in sandboxed processes. Nothing needs to be done here. + self.process_line = self.process_line_echo + else: + # Used by clients who may want to supply a different binary name. + # E.g. in Chrome several binaries may share a single .dSYM. + self.binary_name_filter = binary_name_filter + self.dsym_hint_producer = dsym_hint_producer + self.system = os.uname()[0] + if self.system not in ['Linux', 'Darwin', 'FreeBSD']: + raise Exception('Unknown system') + self.llvm_symbolizers = {} + self.last_llvm_symbolizer = None + self.dsym_hints = set([]) + self.frame_no = 0 + self.process_line = self.process_line_posix def symbolize_address(self, addr, binary, offset): # On non-Darwin (i.e. on platforms without .dSYM debug info) always use # a single symbolizer binary. # On Darwin, if the dsym hint producer is present: # 1. check whether we've seen this binary already; if so, # use |llvm_symbolizers[binary]|, which has already loaded the debug # info for this binary (might not be the case for # |last_llvm_symbolizer|); # 2. otherwise check if we've seen all the hints for this binary already; # if so, reuse |last_llvm_symbolizer| which has the full set of hints; # 3. otherwise create a new symbolizer and pass all currently known # .dSYM hints to it. if not binary in self.llvm_symbolizers: - use_last_symbolizer = True + use_new_symbolizer = True if self.system == 'Darwin' and self.dsym_hint_producer: dsym_hints_for_binary = set(self.dsym_hint_producer(binary)) - use_last_symbolizer = bool(dsym_hints_for_binary - self.dsym_hints) + use_new_symbolizer = bool(dsym_hints_for_binary - self.dsym_hints) self.dsym_hints |= dsym_hints_for_binary - if self.last_llvm_symbolizer and use_last_symbolizer: + if self.last_llvm_symbolizer and not use_new_symbolizer: self.llvm_symbolizers[binary] = self.last_llvm_symbolizer else: self.last_llvm_symbolizer = LLVMSymbolizerFactory( self.system, guess_arch(addr), self.dsym_hints) self.llvm_symbolizers[binary] = self.last_llvm_symbolizer # Use the chain of symbolizers: # Breakpad symbolizer -> LLVM symbolizer -> addr2line/atos # (fall back to next symbolizer if the previous one fails). if not binary in symbolizers: symbolizers[binary] = ChainSymbolizer( [BreakpadSymbolizerFactory(binary), self.llvm_symbolizers[binary]]) result = symbolizers[binary].symbolize(addr, binary, offset) if result is None: # Initialize system symbolizer only if other symbolizers failed. symbolizers[binary].append_symbolizer( SystemSymbolizerFactory(self.system, addr, binary)) result = symbolizers[binary].symbolize(addr, binary, offset) # The system symbolizer must produce some result. assert result return result def get_symbolized_lines(self, symbolized_lines): if not symbolized_lines: return [self.current_line] else: result = [] for symbolized_frame in symbolized_lines: result.append(' #%s %s' % (str(self.frame_no), symbolized_frame.rstrip())) self.frame_no += 1 return result def process_logfile(self): self.frame_no = 0 - while True: - line = logfile.readline() - if not line: - break + for line in logfile: processed = self.process_line(line) print '\n'.join(processed) - def process_line(self, line): + def process_line_echo(self, line): + return [line.rstrip()] + + def process_line_posix(self, line): self.current_line = line.rstrip() #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) stack_trace_line_format = ( '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)') match = re.match(stack_trace_line_format, line) if not match: return [self.current_line] if DEBUG: print line _, frameno_str, addr, binary, offset = match.groups() if frameno_str == '0': # Assume that frame #0 is the first frame of new stack trace. self.frame_no = 0 original_binary = binary if self.binary_name_filter: binary = self.binary_name_filter(binary) symbolized_line = self.symbolize_address(addr, binary, offset) if not symbolized_line: if original_binary != binary: symbolized_line = self.symbolize_address(addr, binary, offset) return self.get_symbolized_lines(symbolized_line) if __name__ == '__main__': - parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, - description='ASan symbolization script', - epilog='''Example of use: - asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" -s "$HOME/SymbolFiles" < asan.log''') + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description='ASan symbolization script', + epilog='Example of use:\n' + 'asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" ' + '-s "$HOME/SymbolFiles" < asan.log') parser.add_argument('path_to_cut', nargs='*', - help='pattern to be cut from the result file path ') + help='pattern to be cut from the result file path ') parser.add_argument('-d','--demangle', action='store_true', - help='demangle function names') + help='demangle function names') parser.add_argument('-s', metavar='SYSROOT', - help='set path to sysroot for sanitized binaries') + help='set path to sysroot for sanitized binaries') parser.add_argument('-c', metavar='CROSS_COMPILE', - help='set prefix for binutils') - parser.add_argument('-l','--logfile', default=sys.stdin, type=argparse.FileType('r'), - help='set log file name to parse, default is stdin') + help='set prefix for binutils') + parser.add_argument('-l','--logfile', default=sys.stdin, + type=argparse.FileType('r'), + help='set log file name to parse, default is stdin') args = parser.parse_args() if args.path_to_cut: fix_filename_patterns = args.path_to_cut if args.demangle: demangle = True if args.s: binary_name_filter = sysroot_path_filter sysroot_path = args.s if args.c: binutils_prefix = args.c if args.logfile: logfile = args.logfile else: logfile = sys.stdin loop = SymbolizationLoop(binary_name_filter) loop.process_logfile() Index: projects/clang360-import/contrib/compiler-rt/lib/asan/tests/asan_interface_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/tests/asan_interface_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/tests/asan_interface_test.cc (revision 279191) @@ -1,433 +1,433 @@ //===-- asan_interface_test.cc --------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // //===----------------------------------------------------------------------===// #include "asan_test_utils.h" #include #include TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) { EXPECT_EQ(0U, __sanitizer_get_estimated_allocated_size(0)); const size_t sizes[] = { 1, 30, 1<<30 }; for (size_t i = 0; i < 3; i++) { EXPECT_EQ(sizes[i], __sanitizer_get_estimated_allocated_size(sizes[i])); } } static const char* kGetAllocatedSizeErrorMsg = "attempting to call __sanitizer_get_allocated_size"; TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) { const size_t kArraySize = 100; char *array = Ident((char*)malloc(kArraySize)); int *int_ptr = Ident(new int); // Allocated memory is owned by allocator. Allocated size should be // equal to requested size. EXPECT_EQ(true, __sanitizer_get_ownership(array)); EXPECT_EQ(kArraySize, __sanitizer_get_allocated_size(array)); EXPECT_EQ(true, __sanitizer_get_ownership(int_ptr)); EXPECT_EQ(sizeof(int), __sanitizer_get_allocated_size(int_ptr)); // We cannot call GetAllocatedSize from the memory we didn't map, // and from the interior pointers (not returned by previous malloc). void *wild_addr = (void*)0x1; EXPECT_FALSE(__sanitizer_get_ownership(wild_addr)); EXPECT_DEATH(__sanitizer_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg); EXPECT_FALSE(__sanitizer_get_ownership(array + kArraySize / 2)); EXPECT_DEATH(__sanitizer_get_allocated_size(array + kArraySize / 2), kGetAllocatedSizeErrorMsg); // NULL is not owned, but is a valid argument for // __sanitizer_get_allocated_size(). EXPECT_FALSE(__sanitizer_get_ownership(NULL)); EXPECT_EQ(0U, __sanitizer_get_allocated_size(NULL)); // When memory is freed, it's not owned, and call to GetAllocatedSize // is forbidden. free(array); EXPECT_FALSE(__sanitizer_get_ownership(array)); EXPECT_DEATH(__sanitizer_get_allocated_size(array), kGetAllocatedSizeErrorMsg); delete int_ptr; void *zero_alloc = Ident(malloc(0)); if (zero_alloc != 0) { // If malloc(0) is not null, this pointer is owned and should have valid // allocated size. EXPECT_TRUE(__sanitizer_get_ownership(zero_alloc)); // Allocated size is 0 or 1 depending on the allocator used. EXPECT_LT(__sanitizer_get_allocated_size(zero_alloc), 2U); } free(zero_alloc); } TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) { size_t before_malloc, after_malloc, after_free; char *array; const size_t kMallocSize = 100; before_malloc = __sanitizer_get_current_allocated_bytes(); array = Ident((char*)malloc(kMallocSize)); after_malloc = __sanitizer_get_current_allocated_bytes(); EXPECT_EQ(before_malloc + kMallocSize, after_malloc); free(array); after_free = __sanitizer_get_current_allocated_bytes(); EXPECT_EQ(before_malloc, after_free); } TEST(AddressSanitizerInterface, GetHeapSizeTest) { - // asan_allocator2 does not keep huge chunks in free list, but unmaps them. + // ASan allocator does not keep huge chunks in free list, but unmaps them. // The chunk should be greater than the quarantine size, // otherwise it will be stuck in quarantine instead of being unmaped. static const size_t kLargeMallocSize = (1 << 28) + 1; // 256M free(Ident(malloc(kLargeMallocSize))); // Drain quarantine. size_t old_heap_size = __sanitizer_get_heap_size(); for (int i = 0; i < 3; i++) { // fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize); free(Ident(malloc(kLargeMallocSize))); EXPECT_EQ(old_heap_size, __sanitizer_get_heap_size()); } } static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357}; static const size_t kManyThreadsIterations = 250; static const size_t kManyThreadsNumThreads = (SANITIZER_WORDSIZE == 32) ? 40 : 200; static void *ManyThreadsWithStatsWorker(void *arg) { (void)arg; for (size_t iter = 0; iter < kManyThreadsIterations; iter++) { for (size_t size_index = 0; size_index < 4; size_index++) { free(Ident(malloc(kManyThreadsMallocSizes[size_index]))); } } // Just one large allocation. free(Ident(malloc(1 << 20))); return 0; } TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) { size_t before_test, after_test, i; pthread_t threads[kManyThreadsNumThreads]; before_test = __sanitizer_get_current_allocated_bytes(); for (i = 0; i < kManyThreadsNumThreads; i++) { PTHREAD_CREATE(&threads[i], 0, (void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i); } for (i = 0; i < kManyThreadsNumThreads; i++) { PTHREAD_JOIN(threads[i], 0); } after_test = __sanitizer_get_current_allocated_bytes(); // ASan stats also reflect memory usage of internal ASan RTL structs, // so we can't check for equality here. EXPECT_LT(after_test, before_test + (1UL<<20)); } static void DoDoubleFree() { int *x = Ident(new int); delete Ident(x); delete Ident(x); } TEST(AddressSanitizerInterface, ExitCode) { int original_exit_code = __asan_set_error_exit_code(7); EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), ""); EXPECT_EQ(7, __asan_set_error_exit_code(8)); EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), ""); EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code)); EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(original_exit_code), ""); } static void MyDeathCallback() { fprintf(stderr, "MyDeathCallback\n"); fflush(0); // On Windows, stderr doesn't flush on crash. } TEST(AddressSanitizerInterface, DeathCallbackTest) { __asan_set_death_callback(MyDeathCallback); EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback"); __asan_set_death_callback(NULL); } static const char* kUseAfterPoisonErrorMessage = "use-after-poison"; #define GOOD_ACCESS(ptr, offset) \ EXPECT_FALSE(__asan_address_is_poisoned(ptr + offset)) #define BAD_ACCESS(ptr, offset) \ EXPECT_TRUE(__asan_address_is_poisoned(ptr + offset)) TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) { char *array = Ident((char*)malloc(120)); // poison array[40..80) __asan_poison_memory_region(array + 40, 40); GOOD_ACCESS(array, 39); GOOD_ACCESS(array, 80); BAD_ACCESS(array, 40); BAD_ACCESS(array, 60); BAD_ACCESS(array, 79); char value; EXPECT_DEATH(value = Ident(array[40]), kUseAfterPoisonErrorMessage); __asan_unpoison_memory_region(array + 40, 40); // access previously poisoned memory. GOOD_ACCESS(array, 40); GOOD_ACCESS(array, 79); free(array); } TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) { char *array = Ident((char*)malloc(120)); // Poison [0..40) and [80..120) __asan_poison_memory_region(array, 40); __asan_poison_memory_region(array + 80, 40); BAD_ACCESS(array, 20); GOOD_ACCESS(array, 60); BAD_ACCESS(array, 100); // Poison whole array - [0..120) __asan_poison_memory_region(array, 120); BAD_ACCESS(array, 60); // Unpoison [24..96) __asan_unpoison_memory_region(array + 24, 72); BAD_ACCESS(array, 23); GOOD_ACCESS(array, 24); GOOD_ACCESS(array, 60); GOOD_ACCESS(array, 95); BAD_ACCESS(array, 96); free(array); } TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) { // Vector of capacity 20 char *vec = Ident((char*)malloc(20)); __asan_poison_memory_region(vec, 20); for (size_t i = 0; i < 7; i++) { // Simulate push_back. __asan_unpoison_memory_region(vec + i, 1); GOOD_ACCESS(vec, i); BAD_ACCESS(vec, i + 1); } for (size_t i = 7; i > 0; i--) { // Simulate pop_back. __asan_poison_memory_region(vec + i - 1, 1); BAD_ACCESS(vec, i - 1); if (i > 1) GOOD_ACCESS(vec, i - 2); } free(vec); } // Make sure that each aligned block of size "2^granularity" doesn't have // "true" value before "false" value. static void MakeShadowValid(bool *shadow, int length, int granularity) { bool can_be_poisoned = true; for (int i = length - 1; i >= 0; i--) { if (!shadow[i]) can_be_poisoned = false; if (!can_be_poisoned) shadow[i] = false; if (i % (1 << granularity) == 0) { can_be_poisoned = true; } } } TEST(AddressSanitizerInterface, PoisoningStressTest) { const size_t kSize = 24; bool expected[kSize]; char *arr = Ident((char*)malloc(kSize)); for (size_t l1 = 0; l1 < kSize; l1++) { for (size_t s1 = 1; l1 + s1 <= kSize; s1++) { for (size_t l2 = 0; l2 < kSize; l2++) { for (size_t s2 = 1; l2 + s2 <= kSize; s2++) { // Poison [l1, l1+s1), [l2, l2+s2) and check result. __asan_unpoison_memory_region(arr, kSize); __asan_poison_memory_region(arr + l1, s1); __asan_poison_memory_region(arr + l2, s2); memset(expected, false, kSize); memset(expected + l1, true, s1); MakeShadowValid(expected, kSize, /*granularity*/ 3); memset(expected + l2, true, s2); MakeShadowValid(expected, kSize, /*granularity*/ 3); for (size_t i = 0; i < kSize; i++) { ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); } // Unpoison [l1, l1+s1) and [l2, l2+s2) and check result. __asan_poison_memory_region(arr, kSize); __asan_unpoison_memory_region(arr + l1, s1); __asan_unpoison_memory_region(arr + l2, s2); memset(expected, true, kSize); memset(expected + l1, false, s1); MakeShadowValid(expected, kSize, /*granularity*/ 3); memset(expected + l2, false, s2); MakeShadowValid(expected, kSize, /*granularity*/ 3); for (size_t i = 0; i < kSize; i++) { ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i)); } } } } } free(arr); } TEST(AddressSanitizerInterface, GlobalRedzones) { GOOD_ACCESS(glob1, 1 - 1); GOOD_ACCESS(glob2, 2 - 1); GOOD_ACCESS(glob3, 3 - 1); GOOD_ACCESS(glob4, 4 - 1); GOOD_ACCESS(glob5, 5 - 1); GOOD_ACCESS(glob6, 6 - 1); GOOD_ACCESS(glob7, 7 - 1); GOOD_ACCESS(glob8, 8 - 1); GOOD_ACCESS(glob9, 9 - 1); GOOD_ACCESS(glob10, 10 - 1); GOOD_ACCESS(glob11, 11 - 1); GOOD_ACCESS(glob12, 12 - 1); GOOD_ACCESS(glob13, 13 - 1); GOOD_ACCESS(glob14, 14 - 1); GOOD_ACCESS(glob15, 15 - 1); GOOD_ACCESS(glob16, 16 - 1); GOOD_ACCESS(glob17, 17 - 1); GOOD_ACCESS(glob1000, 1000 - 1); GOOD_ACCESS(glob10000, 10000 - 1); GOOD_ACCESS(glob100000, 100000 - 1); BAD_ACCESS(glob1, 1); BAD_ACCESS(glob2, 2); BAD_ACCESS(glob3, 3); BAD_ACCESS(glob4, 4); BAD_ACCESS(glob5, 5); BAD_ACCESS(glob6, 6); BAD_ACCESS(glob7, 7); BAD_ACCESS(glob8, 8); BAD_ACCESS(glob9, 9); BAD_ACCESS(glob10, 10); BAD_ACCESS(glob11, 11); BAD_ACCESS(glob12, 12); BAD_ACCESS(glob13, 13); BAD_ACCESS(glob14, 14); BAD_ACCESS(glob15, 15); BAD_ACCESS(glob16, 16); BAD_ACCESS(glob17, 17); BAD_ACCESS(glob1000, 1000); BAD_ACCESS(glob1000, 1100); // Redzone is at least 101 bytes. BAD_ACCESS(glob10000, 10000); BAD_ACCESS(glob10000, 11000); // Redzone is at least 1001 bytes. BAD_ACCESS(glob100000, 100000); BAD_ACCESS(glob100000, 110000); // Redzone is at least 10001 bytes. } TEST(AddressSanitizerInterface, PoisonedRegion) { size_t rz = 16; for (size_t size = 1; size <= 64; size++) { char *p = new char[size]; for (size_t beg = 0; beg < size + rz; beg++) { for (size_t end = beg; end < size + rz; end++) { void *first_poisoned = __asan_region_is_poisoned(p + beg, end - beg); if (beg == end) { EXPECT_FALSE(first_poisoned); } else if (beg < size && end <= size) { EXPECT_FALSE(first_poisoned); } else if (beg >= size) { EXPECT_EQ(p + beg, first_poisoned); } else { EXPECT_GT(end, size); EXPECT_EQ(p + size, first_poisoned); } } } delete [] p; } } // This is a performance benchmark for manual runs. // asan's memset interceptor calls mem_is_zero for the entire shadow region. // the profile should look like this: // 89.10% [.] __memset_sse2 // 10.50% [.] __sanitizer::mem_is_zero // I.e. mem_is_zero should consume ~ SHADOW_GRANULARITY less CPU cycles // than memset itself. TEST(AddressSanitizerInterface, DISABLED_StressLargeMemset) { size_t size = 1 << 20; char *x = new char[size]; for (int i = 0; i < 100000; i++) Ident(memset)(x, 0, size); delete [] x; } // Same here, but we run memset with small sizes. TEST(AddressSanitizerInterface, DISABLED_StressSmallMemset) { size_t size = 32; char *x = new char[size]; for (int i = 0; i < 100000000; i++) Ident(memset)(x, 0, size); delete [] x; } static const char *kInvalidPoisonMessage = "invalid-poison-memory-range"; static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range"; TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) { char *array = Ident((char*)malloc(120)); __asan_unpoison_memory_region(array, 120); // Try to unpoison not owned memory EXPECT_DEATH(__asan_unpoison_memory_region(array, 121), kInvalidUnpoisonMessage); EXPECT_DEATH(__asan_unpoison_memory_region(array - 1, 120), kInvalidUnpoisonMessage); __asan_poison_memory_region(array, 120); // Try to poison not owned memory. EXPECT_DEATH(__asan_poison_memory_region(array, 121), kInvalidPoisonMessage); EXPECT_DEATH(__asan_poison_memory_region(array - 1, 120), kInvalidPoisonMessage); free(array); } #if !defined(_WIN32) // FIXME: This should really be a lit test. static void ErrorReportCallbackOneToZ(const char *report) { int report_len = strlen(report); ASSERT_EQ(6, write(2, "ABCDEF", 6)); ASSERT_EQ(report_len, write(2, report, report_len)); ASSERT_EQ(6, write(2, "ABCDEF", 6)); _exit(1); } TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) { __asan_set_error_report_callback(ErrorReportCallbackOneToZ); EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1), ASAN_PCRE_DOTALL "ABCDEF.*AddressSanitizer.*WRITE.*ABCDEF"); __asan_set_error_report_callback(NULL); } #endif TEST(AddressSanitizerInterface, GetOwnershipStressTest) { std::vector pointers; std::vector sizes; const size_t kNumMallocs = 1 << 9; for (size_t i = 0; i < kNumMallocs; i++) { size_t size = i * 100 + 1; pointers.push_back((char*)malloc(size)); sizes.push_back(size); } for (size_t i = 0; i < 4000000; i++) { EXPECT_FALSE(__sanitizer_get_ownership(&pointers)); EXPECT_FALSE(__sanitizer_get_ownership((void*)0x1234)); size_t idx = i % kNumMallocs; EXPECT_TRUE(__sanitizer_get_ownership(pointers[idx])); EXPECT_EQ(sizes[idx], __sanitizer_get_allocated_size(pointers[idx])); } for (size_t i = 0, n = pointers.size(); i < n; i++) free(pointers[i]); } Index: projects/clang360-import/contrib/compiler-rt/lib/asan/tests/asan_noinst_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/tests/asan_noinst_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/tests/asan_noinst_test.cc (revision 279191) @@ -1,269 +1,260 @@ //===-- asan_noinst_test.cc -----------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // This test file should be compiled w/o asan instrumentation. //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_test_utils.h" #include #include #include #include #include // for memset() #include #include #include // ATTENTION! // Please don't call intercepted functions (including malloc() and friends) // in this test. The static runtime library is linked explicitly (without // -fsanitize=address), thus the interceptors do not work correctly on OS X. -#if !defined(_WIN32) -extern "C" { -// Set specific ASan options for uninstrumented unittest. -const char* __asan_default_options() { - return "allow_reexec=0"; -} -} // extern "C" -#endif - // Make sure __asan_init is called before any test case is run. struct AsanInitCaller { AsanInitCaller() { __asan_init(); } }; static AsanInitCaller asan_init_caller; TEST(AddressSanitizer, InternalSimpleDeathTest) { EXPECT_DEATH(exit(1), ""); } static void MallocStress(size_t n) { u32 seed = my_rand(); BufferedStackTrace stack1; stack1.trace_buffer[0] = 0xa123; stack1.trace_buffer[1] = 0xa456; stack1.size = 2; BufferedStackTrace stack2; stack2.trace_buffer[0] = 0xb123; stack2.trace_buffer[1] = 0xb456; stack2.size = 2; BufferedStackTrace stack3; stack3.trace_buffer[0] = 0xc123; stack3.trace_buffer[1] = 0xc456; stack3.size = 2; std::vector vec; for (size_t i = 0; i < n; i++) { if ((i % 3) == 0) { if (vec.empty()) continue; size_t idx = my_rand_r(&seed) % vec.size(); void *ptr = vec[idx]; vec[idx] = vec.back(); vec.pop_back(); __asan::asan_free(ptr, &stack1, __asan::FROM_MALLOC); } else { size_t size = my_rand_r(&seed) % 1000 + 1; switch ((my_rand_r(&seed) % 128)) { case 0: size += 1024; break; case 1: size += 2048; break; case 2: size += 4096; break; } size_t alignment = 1 << (my_rand_r(&seed) % 10 + 1); char *ptr = (char*)__asan::asan_memalign(alignment, size, &stack2, __asan::FROM_MALLOC); EXPECT_EQ(size, __asan::asan_malloc_usable_size(ptr, 0, 0)); vec.push_back(ptr); ptr[0] = 0; ptr[size-1] = 0; ptr[size/2] = 0; } } for (size_t i = 0; i < vec.size(); i++) __asan::asan_free(vec[i], &stack3, __asan::FROM_MALLOC); } TEST(AddressSanitizer, NoInstMallocTest) { MallocStress(ASAN_LOW_MEMORY ? 300000 : 1000000); } TEST(AddressSanitizer, ThreadedMallocStressTest) { const int kNumThreads = 4; const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000; pthread_t t[kNumThreads]; for (int i = 0; i < kNumThreads; i++) { PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress, (void*)kNumIterations); } for (int i = 0; i < kNumThreads; i++) { PTHREAD_JOIN(t[i], 0); } } static void PrintShadow(const char *tag, uptr ptr, size_t size) { fprintf(stderr, "%s shadow: %lx size % 3ld: ", tag, (long)ptr, (long)size); uptr prev_shadow = 0; for (sptr i = -32; i < (sptr)size + 32; i++) { uptr shadow = __asan::MemToShadow(ptr + i); if (i == 0 || i == (sptr)size) fprintf(stderr, "."); if (shadow != prev_shadow) { prev_shadow = shadow; fprintf(stderr, "%02x", (int)*(u8*)shadow); } } fprintf(stderr, "\n"); } TEST(AddressSanitizer, DISABLED_InternalPrintShadow) { for (size_t size = 1; size <= 513; size++) { char *ptr = new char[size]; PrintShadow("m", (uptr)ptr, size); delete [] ptr; PrintShadow("f", (uptr)ptr, size); } } TEST(AddressSanitizer, QuarantineTest) { BufferedStackTrace stack; stack.trace_buffer[0] = 0x890; stack.size = 1; const int size = 1024; void *p = __asan::asan_malloc(size, &stack); __asan::asan_free(p, &stack, __asan::FROM_MALLOC); size_t i; size_t max_i = 1 << 30; for (i = 0; i < max_i; i++) { void *p1 = __asan::asan_malloc(size, &stack); __asan::asan_free(p1, &stack, __asan::FROM_MALLOC); if (p1 == p) break; } EXPECT_GE(i, 10000U); EXPECT_LT(i, max_i); } void *ThreadedQuarantineTestWorker(void *unused) { (void)unused; u32 seed = my_rand(); BufferedStackTrace stack; stack.trace_buffer[0] = 0x890; stack.size = 1; for (size_t i = 0; i < 1000; i++) { void *p = __asan::asan_malloc(1 + (my_rand_r(&seed) % 4000), &stack); __asan::asan_free(p, &stack, __asan::FROM_MALLOC); } return NULL; } // Check that the thread local allocators are flushed when threads are // destroyed. TEST(AddressSanitizer, ThreadedQuarantineTest) { const int n_threads = 3000; size_t mmaped1 = __sanitizer_get_heap_size(); for (int i = 0; i < n_threads; i++) { pthread_t t; PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0); PTHREAD_JOIN(t, 0); size_t mmaped2 = __sanitizer_get_heap_size(); EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20)); } } void *ThreadedOneSizeMallocStress(void *unused) { (void)unused; BufferedStackTrace stack; stack.trace_buffer[0] = 0x890; stack.size = 1; const size_t kNumMallocs = 1000; for (int iter = 0; iter < 1000; iter++) { void *p[kNumMallocs]; for (size_t i = 0; i < kNumMallocs; i++) { p[i] = __asan::asan_malloc(32, &stack); } for (size_t i = 0; i < kNumMallocs; i++) { __asan::asan_free(p[i], &stack, __asan::FROM_MALLOC); } } return NULL; } TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) { const int kNumThreads = 4; pthread_t t[kNumThreads]; for (int i = 0; i < kNumThreads; i++) { PTHREAD_CREATE(&t[i], 0, ThreadedOneSizeMallocStress, 0); } for (int i = 0; i < kNumThreads; i++) { PTHREAD_JOIN(t[i], 0); } } TEST(AddressSanitizer, ShadowRegionIsPoisonedTest) { using __asan::kHighMemEnd; // Check that __asan_region_is_poisoned works for shadow regions. uptr ptr = kLowShadowBeg + 200; EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); ptr = kShadowGapBeg + 200; EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); ptr = kHighShadowBeg + 200; EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); } // Test __asan_load1 & friends. TEST(AddressSanitizer, LoadStoreCallbacks) { typedef void (*CB)(uptr p); CB cb[2][5] = { { __asan_load1, __asan_load2, __asan_load4, __asan_load8, __asan_load16, }, { __asan_store1, __asan_store2, __asan_store4, __asan_store8, __asan_store16, } }; uptr buggy_ptr; __asan_test_only_reported_buggy_pointer = &buggy_ptr; BufferedStackTrace stack; stack.trace_buffer[0] = 0x890; stack.size = 1; for (uptr len = 16; len <= 32; len++) { char *ptr = (char*) __asan::asan_malloc(len, &stack); uptr p = reinterpret_cast(ptr); for (uptr is_write = 0; is_write <= 1; is_write++) { for (uptr size_log = 0; size_log <= 4; size_log++) { uptr size = 1 << size_log; CB call = cb[is_write][size_log]; // Iterate only size-aligned offsets. for (uptr offset = 0; offset <= len; offset += size) { buggy_ptr = 0; call(p + offset); if (offset + size <= len) EXPECT_EQ(buggy_ptr, 0U); else EXPECT_EQ(buggy_ptr, p + offset); } } } __asan::asan_free(ptr, &stack, __asan::FROM_MALLOC); } __asan_test_only_reported_buggy_pointer = 0; } Index: projects/clang360-import/contrib/compiler-rt/lib/asan/tests/asan_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/asan/tests/asan_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/asan/tests/asan_test.cc (revision 279191) @@ -1,1286 +1,1317 @@ //===-- asan_test.cc ------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // //===----------------------------------------------------------------------===// #include "asan_test_utils.h" NOINLINE void *malloc_fff(size_t size) { void *res = malloc/**/(size); break_optimization(0); return res;} NOINLINE void *malloc_eee(size_t size) { void *res = malloc_fff(size); break_optimization(0); return res;} NOINLINE void *malloc_ddd(size_t size) { void *res = malloc_eee(size); break_optimization(0); return res;} NOINLINE void *malloc_ccc(size_t size) { void *res = malloc_ddd(size); break_optimization(0); return res;} NOINLINE void *malloc_bbb(size_t size) { void *res = malloc_ccc(size); break_optimization(0); return res;} NOINLINE void *malloc_aaa(size_t size) { void *res = malloc_bbb(size); break_optimization(0); return res;} NOINLINE void free_ccc(void *p) { free(p); break_optimization(0);} NOINLINE void free_bbb(void *p) { free_ccc(p); break_optimization(0);} NOINLINE void free_aaa(void *p) { free_bbb(p); break_optimization(0);} template NOINLINE void uaf_test(int size, int off) { char *p = (char *)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)); } TEST(AddressSanitizer, HasFeatureAddressSanitizerTest) { #if defined(__has_feature) && __has_feature(address_sanitizer) bool asan = 1; #elif defined(__SANITIZE_ADDRESS__) bool asan = 1; #else bool asan = 0; #endif EXPECT_EQ(true, asan); } TEST(AddressSanitizer, SimpleDeathTest) { EXPECT_DEATH(exit(1), ""); } TEST(AddressSanitizer, VariousMallocsTest) { int *a = (int*)malloc(100 * sizeof(int)); a[50] = 0; free(a); int *r = (int*)malloc(10); r = (int*)realloc(r, 2000 * sizeof(int)); r[1000] = 0; free(r); int *b = new int[100]; b[50] = 0; delete [] b; int *c = new int; *c = 0; delete c; #if SANITIZER_TEST_HAS_POSIX_MEMALIGN int *pm; int pm_res = posix_memalign((void**)&pm, kPageSize, kPageSize); EXPECT_EQ(0, pm_res); free(pm); #endif // SANITIZER_TEST_HAS_POSIX_MEMALIGN #if SANITIZER_TEST_HAS_MEMALIGN int *ma = (int*)memalign(kPageSize, kPageSize); EXPECT_EQ(0U, (uintptr_t)ma % kPageSize); ma[123] = 0; free(ma); #endif // SANITIZER_TEST_HAS_MEMALIGN } TEST(AddressSanitizer, CallocTest) { int *a = (int*)calloc(100, sizeof(int)); EXPECT_EQ(0, a[10]); free(a); } TEST(AddressSanitizer, CallocReturnsZeroMem) { size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; for (size_t s = 0; s < sizeof(sizes)/sizeof(sizes[0]); s++) { size_t size = sizes[s]; for (size_t iter = 0; iter < 5; iter++) { char *x = Ident((char*)calloc(1, size)); EXPECT_EQ(x[0], 0); EXPECT_EQ(x[size - 1], 0); EXPECT_EQ(x[size / 2], 0); EXPECT_EQ(x[size / 3], 0); EXPECT_EQ(x[size / 4], 0); memset(x, 0x42, size); free(Ident(x)); #if !defined(_WIN32) // FIXME: OOM on Windows. We should just make this a lit test // with quarantine size set to 1. free(Ident(malloc(Ident(1 << 27)))); // Try to drain the quarantine. #endif } } } // No valloc on Windows or Android. #if !defined(_WIN32) && !defined(__ANDROID__) TEST(AddressSanitizer, VallocTest) { void *a = valloc(100); EXPECT_EQ(0U, (uintptr_t)a % kPageSize); free(a); } #endif #if SANITIZER_TEST_HAS_PVALLOC TEST(AddressSanitizer, PvallocTest) { char *a = (char*)pvalloc(kPageSize + 100); EXPECT_EQ(0U, (uintptr_t)a % kPageSize); a[kPageSize + 101] = 1; // we should not report an error here. free(a); a = (char*)pvalloc(0); // pvalloc(0) should allocate at least one page. EXPECT_EQ(0U, (uintptr_t)a % kPageSize); a[101] = 1; // we should not report an error here. free(a); } #endif // SANITIZER_TEST_HAS_PVALLOC #if !defined(_WIN32) // FIXME: Use an equivalent of pthread_setspecific on Windows. void *TSDWorker(void *test_key) { if (test_key) { pthread_setspecific(*(pthread_key_t*)test_key, (void*)0xfeedface); } return NULL; } void TSDDestructor(void *tsd) { // Spawning a thread will check that the current thread id is not -1. pthread_t th; PTHREAD_CREATE(&th, NULL, TSDWorker, NULL); PTHREAD_JOIN(th, NULL); } // This tests triggers the thread-specific data destruction fiasco which occurs // if we don't manage the TSD destructors ourselves. We create a new pthread // key with a non-NULL destructor which is likely to be put after the destructor // of AsanThread in the list of destructors. // In this case the TSD for AsanThread will be destroyed before TSDDestructor // is called for the child thread, and a CHECK will fail when we call // pthread_create() to spawn the grandchild. TEST(AddressSanitizer, DISABLED_TSDTest) { pthread_t th; pthread_key_t test_key; pthread_key_create(&test_key, TSDDestructor); PTHREAD_CREATE(&th, NULL, TSDWorker, &test_key); PTHREAD_JOIN(th, NULL); pthread_key_delete(test_key); } #endif TEST(AddressSanitizer, UAF_char) { const char *uaf_string = "AddressSanitizer:.*heap-use-after-free"; EXPECT_DEATH(uaf_test(1, 0), uaf_string); EXPECT_DEATH(uaf_test(10, 0), uaf_string); EXPECT_DEATH(uaf_test(10, 10), uaf_string); EXPECT_DEATH(uaf_test(kLargeMalloc, 0), uaf_string); EXPECT_DEATH(uaf_test(kLargeMalloc, kLargeMalloc / 2), uaf_string); } TEST(AddressSanitizer, UAF_long_double) { if (sizeof(long double) == sizeof(double)) return; long double *p = Ident(new long double[10]); EXPECT_DEATH(Ident(p)[12] = 0, "WRITE of size 1[026]"); EXPECT_DEATH(Ident(p)[0] = Ident(p)[12], "READ of size 1[026]"); delete [] Ident(p); } #if !defined(_WIN32) struct Packed5 { int x; char c; } __attribute__((packed)); #else # pragma pack(push, 1) struct Packed5 { int x; char c; }; # pragma pack(pop) #endif TEST(AddressSanitizer, UAF_Packed5) { static_assert(sizeof(Packed5) == 5, "Please check the keywords used"); Packed5 *p = Ident(new Packed5[2]); EXPECT_DEATH(p[0] = p[3], "READ of size 5"); EXPECT_DEATH(p[3] = p[0], "WRITE of size 5"); delete [] Ident(p); } #if ASAN_HAS_BLACKLIST TEST(AddressSanitizer, IgnoreTest) { int *x = Ident(new int); delete Ident(x); *x = 0; } #endif // ASAN_HAS_BLACKLIST struct StructWithBitField { int bf1:1; int bf2:1; int bf3:1; int bf4:29; }; TEST(AddressSanitizer, BitFieldPositiveTest) { StructWithBitField *x = new StructWithBitField; delete Ident(x); EXPECT_DEATH(x->bf1 = 0, "use-after-free"); EXPECT_DEATH(x->bf2 = 0, "use-after-free"); EXPECT_DEATH(x->bf3 = 0, "use-after-free"); EXPECT_DEATH(x->bf4 = 0, "use-after-free"); } struct StructWithBitFields_8_24 { int a:8; int b:24; }; TEST(AddressSanitizer, BitFieldNegativeTest) { StructWithBitFields_8_24 *x = Ident(new StructWithBitFields_8_24); x->a = 0; x->b = 0; delete Ident(x); } #if ASAN_NEEDS_SEGV namespace { const char kUnknownCrash[] = "AddressSanitizer: SEGV on unknown address"; const char kOverriddenHandler[] = "ASan signal handler has been overridden\n"; TEST(AddressSanitizer, WildAddressTest) { char *c = (char*)0x123; EXPECT_DEATH(*c = 0, kUnknownCrash); } void my_sigaction_sighandler(int, siginfo_t*, void*) { fprintf(stderr, kOverriddenHandler); exit(1); } void my_signal_sighandler(int signum) { fprintf(stderr, kOverriddenHandler); exit(1); } TEST(AddressSanitizer, SignalTest) { struct sigaction sigact; memset(&sigact, 0, sizeof(sigact)); sigact.sa_sigaction = my_sigaction_sighandler; sigact.sa_flags = SA_SIGINFO; // ASan should silently ignore sigaction()... EXPECT_EQ(0, sigaction(SIGSEGV, &sigact, 0)); #ifdef __APPLE__ EXPECT_EQ(0, sigaction(SIGBUS, &sigact, 0)); #endif char *c = (char*)0x123; EXPECT_DEATH(*c = 0, kUnknownCrash); // ... and signal(). EXPECT_EQ(0, signal(SIGSEGV, my_signal_sighandler)); EXPECT_DEATH(*c = 0, kUnknownCrash); } } // namespace #endif static void TestLargeMalloc(size_t size) { char buff[1024]; sprintf(buff, "is located 1 bytes to the left of %lu-byte", (long)size); EXPECT_DEATH(Ident((char*)malloc(size))[-1] = 0, buff); } TEST(AddressSanitizer, LargeMallocTest) { const int max_size = (SANITIZER_WORDSIZE == 32) ? 1 << 26 : 1 << 28; for (int i = 113; i < max_size; i = i * 2 + 13) { TestLargeMalloc(i); } } TEST(AddressSanitizer, HugeMallocTest) { if (SANITIZER_WORDSIZE != 64 || ASAN_AVOID_EXPENSIVE_TESTS) return; size_t n_megs = 4100; EXPECT_DEATH(Ident((char*)malloc(n_megs << 20))[-1] = 0, "is located 1 bytes to the left|" "AddressSanitizer failed to allocate"); } #if SANITIZER_TEST_HAS_MEMALIGN void MemalignRun(size_t align, size_t size, int idx) { char *p = (char *)memalign(align, size); Ident(p)[idx] = 0; free(p); } TEST(AddressSanitizer, memalign) { for (int align = 16; align <= (1 << 23); align *= 2) { size_t size = align * 5; EXPECT_DEATH(MemalignRun(align, size, -1), "is located 1 bytes to the left"); EXPECT_DEATH(MemalignRun(align, size, size + 1), "is located 1 bytes to the right"); } } #endif // SANITIZER_TEST_HAS_MEMALIGN void *ManyThreadsWorker(void *a) { for (int iter = 0; iter < 100; iter++) { for (size_t size = 100; size < 2000; size *= 2) { free(Ident(malloc(size))); } } return 0; } TEST(AddressSanitizer, ManyThreadsTest) { const size_t kNumThreads = (SANITIZER_WORDSIZE == 32 || ASAN_AVOID_EXPENSIVE_TESTS) ? 30 : 1000; pthread_t t[kNumThreads]; for (size_t i = 0; i < kNumThreads; i++) { PTHREAD_CREATE(&t[i], 0, ManyThreadsWorker, (void*)i); } for (size_t i = 0; i < kNumThreads; i++) { PTHREAD_JOIN(t[i], 0); } } TEST(AddressSanitizer, ReallocTest) { const int kMinElem = 5; int *ptr = (int*)malloc(sizeof(int) * kMinElem); ptr[3] = 3; for (int i = 0; i < 10000; i++) { ptr = (int*)realloc(ptr, (my_rand() % 1000 + kMinElem) * sizeof(int)); EXPECT_EQ(3, ptr[3]); } free(ptr); // Realloc pointer returned by malloc(0). int *ptr2 = Ident((int*)malloc(0)); ptr2 = Ident((int*)realloc(ptr2, sizeof(*ptr2))); *ptr2 = 42; EXPECT_EQ(42, *ptr2); free(ptr2); } TEST(AddressSanitizer, ReallocFreedPointerTest) { void *ptr = Ident(malloc(42)); ASSERT_TRUE(NULL != ptr); free(ptr); EXPECT_DEATH(ptr = realloc(ptr, 77), "attempting double-free"); } TEST(AddressSanitizer, ReallocInvalidPointerTest) { void *ptr = Ident(malloc(42)); EXPECT_DEATH(ptr = realloc((int*)ptr + 1, 77), "attempting free.*not malloc"); free(ptr); } TEST(AddressSanitizer, ZeroSizeMallocTest) { // Test that malloc(0) and similar functions don't return NULL. void *ptr = Ident(malloc(0)); EXPECT_TRUE(NULL != ptr); free(ptr); #if SANITIZER_TEST_HAS_POSIX_MEMALIGN int pm_res = posix_memalign(&ptr, 1<<20, 0); EXPECT_EQ(0, pm_res); EXPECT_TRUE(NULL != ptr); free(ptr); #endif // SANITIZER_TEST_HAS_POSIX_MEMALIGN int *int_ptr = new int[0]; int *int_ptr2 = new int[0]; EXPECT_TRUE(NULL != int_ptr); EXPECT_TRUE(NULL != int_ptr2); EXPECT_NE(int_ptr, int_ptr2); delete[] int_ptr; delete[] int_ptr2; } #if SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE static const char *kMallocUsableSizeErrorMsg = "AddressSanitizer: attempting to call malloc_usable_size()"; TEST(AddressSanitizer, MallocUsableSizeTest) { const size_t kArraySize = 100; char *array = Ident((char*)malloc(kArraySize)); int *int_ptr = Ident(new int); EXPECT_EQ(0U, malloc_usable_size(NULL)); EXPECT_EQ(kArraySize, malloc_usable_size(array)); EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr)); EXPECT_DEATH(malloc_usable_size((void*)0x123), kMallocUsableSizeErrorMsg); EXPECT_DEATH(malloc_usable_size(array + kArraySize / 2), kMallocUsableSizeErrorMsg); free(array); EXPECT_DEATH(malloc_usable_size(array), kMallocUsableSizeErrorMsg); delete int_ptr; } #endif // SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE void WrongFree() { int *x = (int*)malloc(100 * sizeof(int)); // Use the allocated memory, otherwise Clang will optimize it out. Ident(x); free(x + 1); } #if !defined(_WIN32) // FIXME: This should be a lit test. TEST(AddressSanitizer, WrongFreeTest) { EXPECT_DEATH(WrongFree(), ASAN_PCRE_DOTALL "ERROR: AddressSanitizer: attempting free.*not malloc" ".*is located 4 bytes inside of 400-byte region" ".*allocated by thread"); } #endif void DoubleFree() { int *x = (int*)malloc(100 * sizeof(int)); fprintf(stderr, "DoubleFree: x=%p\n", x); free(x); free(x); fprintf(stderr, "should have failed in the second free(%p)\n", x); abort(); } #if !defined(_WIN32) // FIXME: This should be a lit test. TEST(AddressSanitizer, DoubleFreeTest) { EXPECT_DEATH(DoubleFree(), ASAN_PCRE_DOTALL "ERROR: AddressSanitizer: attempting double-free" ".*is located 0 bytes inside of 400-byte region" ".*freed by thread T0 here" ".*previously allocated by thread T0 here"); } #endif template NOINLINE void SizedStackTest() { char a[kSize]; char *A = Ident((char*)&a); const char *expected_death = "AddressSanitizer: stack-buffer-"; for (size_t i = 0; i < kSize; i++) A[i] = i; EXPECT_DEATH(A[-1] = 0, expected_death); EXPECT_DEATH(A[-5] = 0, expected_death); EXPECT_DEATH(A[kSize] = 0, expected_death); EXPECT_DEATH(A[kSize + 1] = 0, expected_death); EXPECT_DEATH(A[kSize + 5] = 0, expected_death); if (kSize > 16) EXPECT_DEATH(A[kSize + 31] = 0, expected_death); } TEST(AddressSanitizer, SimpleStackTest) { SizedStackTest<1>(); SizedStackTest<2>(); SizedStackTest<3>(); SizedStackTest<4>(); SizedStackTest<5>(); SizedStackTest<6>(); SizedStackTest<7>(); SizedStackTest<16>(); SizedStackTest<25>(); SizedStackTest<34>(); SizedStackTest<43>(); SizedStackTest<51>(); SizedStackTest<62>(); SizedStackTest<64>(); SizedStackTest<128>(); } #if !defined(_WIN32) // FIXME: It's a bit hard to write multi-line death test expectations // in a portable way. Anyways, this should just be turned into a lit test. TEST(AddressSanitizer, ManyStackObjectsTest) { char XXX[10]; char YYY[20]; char ZZZ[30]; Ident(XXX); Ident(YYY); EXPECT_DEATH(Ident(ZZZ)[-1] = 0, ASAN_PCRE_DOTALL "XXX.*YYY.*ZZZ"); } #endif #if 0 // This test requires online symbolizer. // Moved to lit_tests/stack-oob-frames.cc. // Reenable here once we have online symbolizer by default. NOINLINE static void Frame0(int frame, char *a, char *b, char *c) { char d[4] = {0}; char *D = Ident(d); switch (frame) { case 3: a[5]++; break; case 2: b[5]++; break; case 1: c[5]++; break; case 0: D[5]++; break; } } NOINLINE static void Frame1(int frame, char *a, char *b) { char c[4] = {0}; Frame0(frame, a, b, c); break_optimization(0); } NOINLINE static void Frame2(int frame, char *a) { char b[4] = {0}; Frame1(frame, a, b); break_optimization(0); } NOINLINE static void Frame3(int frame) { char a[4] = {0}; Frame2(frame, a); break_optimization(0); } TEST(AddressSanitizer, GuiltyStackFrame0Test) { EXPECT_DEATH(Frame3(0), "located .*in frame <.*Frame0"); } TEST(AddressSanitizer, GuiltyStackFrame1Test) { EXPECT_DEATH(Frame3(1), "located .*in frame <.*Frame1"); } TEST(AddressSanitizer, GuiltyStackFrame2Test) { EXPECT_DEATH(Frame3(2), "located .*in frame <.*Frame2"); } TEST(AddressSanitizer, GuiltyStackFrame3Test) { EXPECT_DEATH(Frame3(3), "located .*in frame <.*Frame3"); } #endif NOINLINE void LongJmpFunc1(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; longjmp(buf, 1); } NOINLINE void TouchStackFunc() { int a[100]; // long array will intersect with redzones from LongJmpFunc1. int *A = Ident(a); for (int i = 0; i < 100; i++) A[i] = i*i; } // Test that we handle longjmp and do not report false positives on stack. TEST(AddressSanitizer, LongJmpTest) { static jmp_buf buf; if (!setjmp(buf)) { LongJmpFunc1(buf); } else { TouchStackFunc(); } } #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; int b; int *A = Ident(&a); int *B = Ident(&b); *A = *B; _longjmp(buf, 1); } NOINLINE void SigLongJmpFunc1(sigjmp_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; siglongjmp(buf, 1); } #if !defined(__ANDROID__) && !defined(__arm__) && \ - !defined(__powerpc64__) && !defined(__powerpc__) + !defined(__powerpc64__) && !defined(__powerpc__) && \ + !defined(__aarch64__) // Does not work on Power and ARM: // https://code.google.com/p/address-sanitizer/issues/detail?id=185 TEST(AddressSanitizer, BuiltinLongJmpTest) { static jmp_buf buf; if (!__builtin_setjmp((void**)buf)) { BuiltinLongJmpFunc1(buf); } else { TouchStackFunc(); } } #endif // !defined(__ANDROID__) && !defined(__powerpc64__) && // !defined(__powerpc__) && !defined(__arm__) TEST(AddressSanitizer, UnderscopeLongJmpTest) { static jmp_buf buf; if (!_setjmp(buf)) { UnderscopeLongJmpFunc1(buf); } else { TouchStackFunc(); } } TEST(AddressSanitizer, SigLongJmpTest) { static sigjmp_buf buf; if (!sigsetjmp(buf, 1)) { SigLongJmpFunc1(buf); } else { TouchStackFunc(); } } #endif // FIXME: Why does clang-cl define __EXCEPTIONS? #if defined(__EXCEPTIONS) && !defined(_WIN32) NOINLINE void ThrowFunc() { // create three red zones for these two stack objects. int a; int b; int *A = Ident(&a); int *B = Ident(&b); *A = *B; ASAN_THROW(1); } TEST(AddressSanitizer, CxxExceptionTest) { if (ASAN_UAR) return; // TODO(kcc): this test crashes on 32-bit for some reason... if (SANITIZER_WORDSIZE == 32) return; try { ThrowFunc(); } catch(...) {} TouchStackFunc(); } #endif void *ThreadStackReuseFunc1(void *unused) { // create three red zones for these two stack objects. int a; int b; int *A = Ident(&a); int *B = Ident(&b); *A = *B; pthread_exit(0); return 0; } void *ThreadStackReuseFunc2(void *unused) { TouchStackFunc(); return 0; } TEST(AddressSanitizer, ThreadStackReuseTest) { pthread_t t; PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc1, 0); PTHREAD_JOIN(t, 0); PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc2, 0); PTHREAD_JOIN(t, 0); } #if defined(__i686__) || defined(__x86_64__) #include TEST(AddressSanitizer, Store128Test) { char *a = Ident((char*)malloc(Ident(12))); char *p = a; if (((uintptr_t)a % 16) != 0) p = a + 8; assert(((uintptr_t)p % 16) == 0); __m128i value_wide = _mm_set1_epi16(0x1234); EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), "AddressSanitizer: heap-buffer-overflow"); EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), "WRITE of size 16"); EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide), "located 0 bytes to the right of 12-byte"); free(a); } #endif // FIXME: All tests that use this function should be turned into lit tests. string RightOOBErrorMessage(int oob_distance, bool is_write) { assert(oob_distance >= 0); char expected_str[100]; sprintf(expected_str, ASAN_PCRE_DOTALL #if !GTEST_USES_SIMPLE_RE "buffer-overflow.*%s.*" #endif "located %d bytes to the right", #if !GTEST_USES_SIMPLE_RE is_write ? "WRITE" : "READ", #endif oob_distance); return string(expected_str); } string RightOOBWriteMessage(int oob_distance) { return RightOOBErrorMessage(oob_distance, /*is_write*/true); } string RightOOBReadMessage(int oob_distance) { return RightOOBErrorMessage(oob_distance, /*is_write*/false); } // FIXME: All tests that use this function should be turned into lit tests. string LeftOOBErrorMessage(int oob_distance, bool is_write) { assert(oob_distance > 0); char expected_str[100]; sprintf(expected_str, #if !GTEST_USES_SIMPLE_RE ASAN_PCRE_DOTALL "%s.*" #endif "located %d bytes to the left", #if !GTEST_USES_SIMPLE_RE is_write ? "WRITE" : "READ", #endif oob_distance); return string(expected_str); } string LeftOOBWriteMessage(int oob_distance) { return LeftOOBErrorMessage(oob_distance, /*is_write*/true); } string LeftOOBReadMessage(int oob_distance) { return LeftOOBErrorMessage(oob_distance, /*is_write*/false); } string LeftOOBAccessMessage(int oob_distance) { assert(oob_distance > 0); char expected_str[100]; sprintf(expected_str, "located %d bytes to the left", oob_distance); return string(expected_str); } char* MallocAndMemsetString(size_t size, char ch) { char *s = Ident((char*)malloc(size)); memset(s, ch, size); return s; } char* MallocAndMemsetString(size_t size) { return MallocAndMemsetString(size, 'z'); } #if defined(__linux__) && !defined(__ANDROID__) #define READ_TEST(READ_N_BYTES) \ char *x = new char[10]; \ int fd = open("/proc/self/stat", O_RDONLY); \ ASSERT_GT(fd, 0); \ EXPECT_DEATH(READ_N_BYTES, \ ASAN_PCRE_DOTALL \ "AddressSanitizer: heap-buffer-overflow" \ ".* is located 0 bytes to the right of 10-byte region"); \ close(fd); \ delete [] x; \ TEST(AddressSanitizer, pread) { READ_TEST(pread(fd, x, 15, 0)); } TEST(AddressSanitizer, pread64) { READ_TEST(pread64(fd, x, 15, 0)); } TEST(AddressSanitizer, read) { READ_TEST(read(fd, x, 15)); } #endif // defined(__linux__) && !defined(__ANDROID__) // This test case fails // Clang optimizes memcpy/memset calls which lead to unaligned access TEST(AddressSanitizer, DISABLED_MemIntrinsicUnalignedAccessTest) { int size = Ident(4096); char *s = Ident((char*)malloc(size)); EXPECT_DEATH(memset(s + size - 1, 0, 2), RightOOBWriteMessage(0)); free(s); } // TODO(samsonov): Add a test with malloc(0) // TODO(samsonov): Add tests for str* and mem* functions. NOINLINE static int LargeFunction(bool do_bad_access) { int *x = new int[100]; x[0]++; x[1]++; x[2]++; x[3]++; x[4]++; x[5]++; x[6]++; x[7]++; x[8]++; x[9]++; x[do_bad_access ? 100 : 0]++; int res = __LINE__; x[10]++; x[11]++; x[12]++; x[13]++; x[14]++; x[15]++; x[16]++; x[17]++; x[18]++; x[19]++; delete[] x; return res; } // Test the we have correct debug info for the failing instruction. // This test requires the in-process symbolizer to be enabled by default. TEST(AddressSanitizer, DISABLED_LargeFunctionSymbolizeTest) { int failing_line = LargeFunction(false); char expected_warning[128]; sprintf(expected_warning, "LargeFunction.*asan_test.*:%d", failing_line); EXPECT_DEATH(LargeFunction(true), expected_warning); } // Check that we unwind and symbolize correctly. TEST(AddressSanitizer, DISABLED_MallocFreeUnwindAndSymbolizeTest) { int *a = (int*)malloc_aaa(sizeof(int)); *a = 1; free_aaa(a); EXPECT_DEATH(*a = 1, "free_ccc.*free_bbb.*free_aaa.*" "malloc_fff.*malloc_eee.*malloc_ddd"); } static bool TryToSetThreadName(const char *name) { #if defined(__linux__) && defined(PR_SET_NAME) return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); #else return false; #endif } void *ThreadedTestAlloc(void *a) { EXPECT_EQ(true, TryToSetThreadName("AllocThr")); int **p = (int**)a; *p = new int; return 0; } void *ThreadedTestFree(void *a) { EXPECT_EQ(true, TryToSetThreadName("FreeThr")); int **p = (int**)a; delete *p; return 0; } void *ThreadedTestUse(void *a) { EXPECT_EQ(true, TryToSetThreadName("UseThr")); int **p = (int**)a; **p = 1; return 0; } void ThreadedTestSpawn() { pthread_t t; int *x; PTHREAD_CREATE(&t, 0, ThreadedTestAlloc, &x); PTHREAD_JOIN(t, 0); PTHREAD_CREATE(&t, 0, ThreadedTestFree, &x); PTHREAD_JOIN(t, 0); PTHREAD_CREATE(&t, 0, ThreadedTestUse, &x); PTHREAD_JOIN(t, 0); } #if !defined(_WIN32) // FIXME: This should be a lit test. TEST(AddressSanitizer, ThreadedTest) { EXPECT_DEATH(ThreadedTestSpawn(), ASAN_PCRE_DOTALL "Thread T.*created" ".*Thread T.*created" ".*Thread T.*created"); } #endif void *ThreadedTestFunc(void *unused) { // Check if prctl(PR_SET_NAME) is supported. Return if not. if (!TryToSetThreadName("TestFunc")) return 0; EXPECT_DEATH(ThreadedTestSpawn(), ASAN_PCRE_DOTALL "WRITE .*thread T. .UseThr." ".*freed by thread T. .FreeThr. here:" ".*previously allocated by thread T. .AllocThr. here:" ".*Thread T. .UseThr. created by T.*TestFunc" ".*Thread T. .FreeThr. created by T" ".*Thread T. .AllocThr. created by T" ""); return 0; } TEST(AddressSanitizer, ThreadNamesTest) { // Run ThreadedTestFunc in a separate thread because it tries to set a // thread name and we don't want to change the main thread's name. pthread_t t; PTHREAD_CREATE(&t, 0, ThreadedTestFunc, 0); PTHREAD_JOIN(t, 0); } #if ASAN_NEEDS_SEGV TEST(AddressSanitizer, ShadowGapTest) { #if SANITIZER_WORDSIZE == 32 char *addr = (char*)0x22000000; #else # if defined(__powerpc64__) char *addr = (char*)0x024000800000; # else char *addr = (char*)0x0000100000080000; # endif #endif EXPECT_DEATH(*addr = 1, "AddressSanitizer: SEGV on unknown"); } #endif // ASAN_NEEDS_SEGV extern "C" { NOINLINE static void UseThenFreeThenUse() { char *x = Ident((char*)malloc(8)); *x = 1; free_aaa(x); *x = 2; } } TEST(AddressSanitizer, UseThenFreeThenUseTest) { EXPECT_DEATH(UseThenFreeThenUse(), "freed by thread"); } TEST(AddressSanitizer, StrDupTest) { free(strdup(Ident("123"))); } // Currently we create and poison redzone at right of global variables. static char static110[110]; const char ConstGlob[7] = {1, 2, 3, 4, 5, 6, 7}; static const char StaticConstGlob[3] = {9, 8, 7}; TEST(AddressSanitizer, GlobalTest) { static char func_static15[15]; static char fs1[10]; static char fs2[10]; static char fs3[10]; glob5[Ident(0)] = 0; glob5[Ident(1)] = 0; glob5[Ident(2)] = 0; glob5[Ident(3)] = 0; glob5[Ident(4)] = 0; EXPECT_DEATH(glob5[Ident(5)] = 0, "0 bytes to the right of global variable.*glob5.* size 5"); EXPECT_DEATH(glob5[Ident(5+6)] = 0, "6 bytes to the right of global variable.*glob5.* size 5"); Ident(static110); // avoid optimizations static110[Ident(0)] = 0; static110[Ident(109)] = 0; EXPECT_DEATH(static110[Ident(110)] = 0, "0 bytes to the right of global variable"); EXPECT_DEATH(static110[Ident(110+7)] = 0, "7 bytes to the right of global variable"); Ident(func_static15); // avoid optimizations func_static15[Ident(0)] = 0; EXPECT_DEATH(func_static15[Ident(15)] = 0, "0 bytes to the right of global variable"); EXPECT_DEATH(func_static15[Ident(15 + 9)] = 0, "9 bytes to the right of global variable"); Ident(fs1); Ident(fs2); Ident(fs3); // We don't create left redzones, so this is not 100% guaranteed to fail. // But most likely will. EXPECT_DEATH(fs2[Ident(-1)] = 0, "is located.*of global variable"); EXPECT_DEATH(Ident(Ident(ConstGlob)[8]), "is located 1 bytes to the right of .*ConstGlob"); EXPECT_DEATH(Ident(Ident(StaticConstGlob)[5]), "is located 2 bytes to the right of .*StaticConstGlob"); // call stuff from another file. GlobalsTest(0); } TEST(AddressSanitizer, GlobalStringConstTest) { static const char *zoo = "FOOBAR123"; const char *p = Ident(zoo); EXPECT_DEATH(Ident(p[15]), "is ascii string 'FOOBAR123'"); } TEST(AddressSanitizer, FileNameInGlobalReportTest) { static char zoo[10]; const char *p = Ident(zoo); // The file name should be present in the report. EXPECT_DEATH(Ident(p[15]), "zoo.*asan_test."); } int *ReturnsPointerToALocalObject() { int a = 0; return Ident(&a); } #if ASAN_UAR == 1 TEST(AddressSanitizer, LocalReferenceReturnTest) { int *(*f)() = Ident(ReturnsPointerToALocalObject); int *p = f(); // Call 'f' a few more times, 'p' should still be poisoned. for (int i = 0; i < 32; i++) f(); EXPECT_DEATH(*p = 1, "AddressSanitizer: stack-use-after-return"); EXPECT_DEATH(*p = 1, "is located.*in frame .*ReturnsPointerToALocal"); } #endif template NOINLINE static void FuncWithStack() { char x[kSize]; Ident(x)[0] = 0; Ident(x)[kSize-1] = 0; } static void LotsOfStackReuse() { int LargeStack[10000]; Ident(LargeStack)[0] = 0; for (int i = 0; i < 10000; i++) { FuncWithStack<128 * 1>(); FuncWithStack<128 * 2>(); FuncWithStack<128 * 4>(); FuncWithStack<128 * 8>(); FuncWithStack<128 * 16>(); FuncWithStack<128 * 32>(); FuncWithStack<128 * 64>(); FuncWithStack<128 * 128>(); FuncWithStack<128 * 256>(); FuncWithStack<128 * 512>(); Ident(LargeStack)[0] = 0; } } TEST(AddressSanitizer, StressStackReuseTest) { LotsOfStackReuse(); } TEST(AddressSanitizer, ThreadedStressStackReuseTest) { const int kNumThreads = 20; pthread_t t[kNumThreads]; for (int i = 0; i < kNumThreads; i++) { PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))LotsOfStackReuse, 0); } for (int i = 0; i < kNumThreads; i++) { PTHREAD_JOIN(t[i], 0); } } static void *PthreadExit(void *a) { pthread_exit(0); return 0; } TEST(AddressSanitizer, PthreadExitTest) { pthread_t t; for (int i = 0; i < 1000; i++) { PTHREAD_CREATE(&t, 0, PthreadExit, 0); PTHREAD_JOIN(t, 0); } } // FIXME: Why does clang-cl define __EXCEPTIONS? #if defined(__EXCEPTIONS) && !defined(_WIN32) NOINLINE static void StackReuseAndException() { int large_stack[1000]; Ident(large_stack); ASAN_THROW(1); } // TODO(kcc): support exceptions with use-after-return. TEST(AddressSanitizer, DISABLED_StressStackReuseAndExceptionsTest) { for (int i = 0; i < 10000; i++) { try { StackReuseAndException(); } catch(...) { } } } #endif #if !defined(_WIN32) TEST(AddressSanitizer, MlockTest) { EXPECT_EQ(0, mlockall(MCL_CURRENT)); EXPECT_EQ(0, mlock((void*)0x12345, 0x5678)); EXPECT_EQ(0, munlockall()); EXPECT_EQ(0, munlock((void*)0x987, 0x654)); } #endif struct LargeStruct { int foo[100]; }; // Test for bug http://llvm.org/bugs/show_bug.cgi?id=11763. // Struct copy should not cause asan warning even if lhs == rhs. TEST(AddressSanitizer, LargeStructCopyTest) { LargeStruct a; *Ident(&a) = *Ident(&a); } ATTRIBUTE_NO_SANITIZE_ADDRESS static void NoSanitizeAddress() { char *foo = new char[10]; Ident(foo)[10] = 0; delete [] foo; } TEST(AddressSanitizer, AttributeNoSanitizeAddressTest) { Ident(NoSanitizeAddress)(); } // The new/delete/etc mismatch checks don't work on Android, // as calls to new/delete go through malloc/free. // OS X support is tracked here: // https://code.google.com/p/address-sanitizer/issues/detail?id=131 // Windows support is tracked here: // https://code.google.com/p/address-sanitizer/issues/detail?id=309 #if !defined(__ANDROID__) && \ !defined(__APPLE__) && \ !defined(_WIN32) static string MismatchStr(const string &str) { return string("AddressSanitizer: alloc-dealloc-mismatch \\(") + str; } TEST(AddressSanitizer, AllocDeallocMismatch) { EXPECT_DEATH(free(Ident(new int)), MismatchStr("operator new vs free")); EXPECT_DEATH(free(Ident(new int[2])), MismatchStr("operator new \\[\\] vs free")); EXPECT_DEATH(delete (Ident(new int[2])), MismatchStr("operator new \\[\\] vs operator delete")); EXPECT_DEATH(delete (Ident((int*)malloc(2 * sizeof(int)))), MismatchStr("malloc vs operator delete")); EXPECT_DEATH(delete [] (Ident(new int)), MismatchStr("operator new vs operator delete \\[\\]")); EXPECT_DEATH(delete [] (Ident((int*)malloc(2 * sizeof(int)))), MismatchStr("malloc vs operator delete \\[\\]")); } #endif // ------------------ demo tests; run each one-by-one ------------- // e.g. --gtest_filter=*DemoOOBLeftHigh --gtest_also_run_disabled_tests TEST(AddressSanitizer, DISABLED_DemoThreadedTest) { ThreadedTestSpawn(); } void *SimpleBugOnSTack(void *x = 0) { char a[20]; Ident(a)[20] = 0; return 0; } TEST(AddressSanitizer, DISABLED_DemoStackTest) { SimpleBugOnSTack(); } TEST(AddressSanitizer, DISABLED_DemoThreadStackTest) { pthread_t t; PTHREAD_CREATE(&t, 0, SimpleBugOnSTack, 0); PTHREAD_JOIN(t, 0); } TEST(AddressSanitizer, DISABLED_DemoUAFLowIn) { uaf_test(10, 0); } TEST(AddressSanitizer, DISABLED_DemoUAFLowLeft) { uaf_test(10, -2); } TEST(AddressSanitizer, DISABLED_DemoUAFLowRight) { uaf_test(10, 10); } TEST(AddressSanitizer, DISABLED_DemoUAFHigh) { uaf_test(kLargeMalloc, 0); } TEST(AddressSanitizer, DISABLED_DemoOOM) { size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 40) : (0xf0000000); printf("%p\n", malloc(size)); } TEST(AddressSanitizer, DISABLED_DemoDoubleFreeTest) { DoubleFree(); } TEST(AddressSanitizer, DISABLED_DemoNullDerefTest) { int *a = 0; Ident(a)[10] = 0; } TEST(AddressSanitizer, DISABLED_DemoFunctionStaticTest) { static char a[100]; static char b[100]; static char c[100]; Ident(a); Ident(b); Ident(c); Ident(a)[5] = 0; Ident(b)[105] = 0; Ident(a)[5] = 0; } TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) { const size_t kAllocSize = (1 << 28) - 1024; size_t total_size = 0; while (true) { char *x = (char*)malloc(kAllocSize); memset(x, 0, kAllocSize); total_size += kAllocSize; fprintf(stderr, "total: %ldM %p\n", (long)total_size >> 20, x); } } // http://code.google.com/p/address-sanitizer/issues/detail?id=66 TEST(AddressSanitizer, BufferOverflowAfterManyFrees) { for (int i = 0; i < 1000000; i++) { delete [] (Ident(new char [8644])); } char *x = new char[8192]; EXPECT_DEATH(x[Ident(8192)] = 0, "AddressSanitizer: heap-buffer-overflow"); delete [] Ident(x); } // Test that instrumentation of stack allocations takes into account // AllocSize of a type, and not its StoreSize (16 vs 10 bytes for long double). // See http://llvm.org/bugs/show_bug.cgi?id=12047 for more details. TEST(AddressSanitizer, LongDoubleNegativeTest) { long double a, b; static long double c; memcpy(Ident(&a), Ident(&b), sizeof(long double)); memcpy(Ident(&c), Ident(&b), sizeof(long double)); } #if !defined(_WIN32) TEST(AddressSanitizer, pthread_getschedparam) { int policy; struct sched_param param; EXPECT_DEATH( pthread_getschedparam(pthread_self(), &policy, Ident(¶m) + 2), "AddressSanitizer: stack-buffer-.*flow"); EXPECT_DEATH( pthread_getschedparam(pthread_self(), Ident(&policy) - 1, ¶m), "AddressSanitizer: stack-buffer-.*flow"); int res = pthread_getschedparam(pthread_self(), &policy, ¶m); ASSERT_EQ(0, res); +} +#endif + +#if SANITIZER_TEST_HAS_PRINTF_L +static int vsnprintf_l_wrapper(char *s, size_t n, + locale_t l, const char *format, ...) { + va_list va; + va_start(va, format); + int res = vsnprintf_l(s, n , l, format, va); + va_end(va); + return res; +} + +TEST(AddressSanitizer, snprintf_l) { + char buff[5]; + // Check that snprintf_l() works fine with Asan. + int res = snprintf_l(buff, 5, + _LIBCPP_GET_C_LOCALE, "%s", "snprintf_l()"); + EXPECT_EQ(12, res); + // Check that vsnprintf_l() works fine with Asan. + res = vsnprintf_l_wrapper(buff, 5, + _LIBCPP_GET_C_LOCALE, "%s", "vsnprintf_l()"); + EXPECT_EQ(13, res); + + EXPECT_DEATH(snprintf_l(buff, 10, + _LIBCPP_GET_C_LOCALE, "%s", "snprintf_l()"), + "AddressSanitizer: stack-buffer-overflow"); + EXPECT_DEATH(vsnprintf_l_wrapper(buff, 10, + _LIBCPP_GET_C_LOCALE, "%s", "vsnprintf_l()"), + "AddressSanitizer: stack-buffer-overflow"); } #endif Index: projects/clang360-import/contrib/compiler-rt/lib/builtins/atomic.c =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/builtins/atomic.c (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/builtins/atomic.c (revision 279191) @@ -1,337 +1,331 @@ /*===-- atomic.c - Implement support functions for atomic operations.------=== * * 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. * *===----------------------------------------------------------------------=== * * atomic.c defines a set of functions for performing atomic accesses on * arbitrary-sized memory locations. This design uses locks that should * be fast in the uncontended case, for two reasons: * * 1) This code must work with C programs that do not link to anything * (including pthreads) and so it should not depend on any pthread * functions. * 2) Atomic operations, rather than explicit mutexes, are most commonly used * on code where contended operations are rate. * * To avoid needing a per-object lock, this code allocates an array of * locks and hashes the object pointers to find the one that it should use. * For operations that must be atomic on two locations, the lower lock is * always acquired first, to avoid deadlock. * *===----------------------------------------------------------------------=== */ #include #include +#include "assembly.h" + // Clang objects if you redefine a builtin. This little hack allows us to // define a function with the same name as an intrinsic. -#if __APPLE__ -// mach-o has extra leading underscore -#pragma redefine_extname __atomic_load_c ___atomic_load -#pragma redefine_extname __atomic_store_c ___atomic_store -#pragma redefine_extname __atomic_exchange_c ___atomic_exchange -#pragma redefine_extname __atomic_compare_exchange_c ___atomic_compare_exchange -#else -#pragma redefine_extname __atomic_load_c __atomic_load -#pragma redefine_extname __atomic_store_c __atomic_store -#pragma redefine_extname __atomic_exchange_c __atomic_exchange -#pragma redefine_extname __atomic_compare_exchange_c __atomic_compare_exchange -#endif +#pragma redefine_extname __atomic_load_c SYMBOL_NAME(__atomic_load) +#pragma redefine_extname __atomic_store_c SYMBOL_NAME(__atomic_store) +#pragma redefine_extname __atomic_exchange_c SYMBOL_NAME(__atomic_exchange) +#pragma redefine_extname __atomic_compare_exchange_c SYMBOL_NAME(__atomic_compare_exchange) /// Number of locks. This allocates one page on 32-bit platforms, two on /// 64-bit. This can be specified externally if a different trade between /// memory usage and contention probability is required for a given platform. #ifndef SPINLOCK_COUNT #define SPINLOCK_COUNT (1<<10) #endif static const long SPINLOCK_MASK = SPINLOCK_COUNT - 1; //////////////////////////////////////////////////////////////////////////////// // Platform-specific lock implementation. Falls back to spinlocks if none is // defined. Each platform should define the Lock type, and corresponding // lock() and unlock() functions. //////////////////////////////////////////////////////////////////////////////// #ifdef __FreeBSD__ #include #include #include #include typedef struct _usem Lock; inline static void unlock(Lock *l) { __c11_atomic_store((_Atomic(uint32_t)*)&l->_count, 1, __ATOMIC_RELEASE); __c11_atomic_thread_fence(__ATOMIC_SEQ_CST); if (l->_has_waiters) _umtx_op(l, UMTX_OP_SEM_WAKE, 1, 0, 0); } inline static void lock(Lock *l) { uint32_t old = 1; while (!__c11_atomic_compare_exchange_weak((_Atomic(uint32_t)*)&l->_count, &old, 0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { _umtx_op(l, UMTX_OP_SEM_WAIT, 0, 0, 0); old = 1; } } /// locks for atomic operations static Lock locks[SPINLOCK_COUNT] = { [0 ... SPINLOCK_COUNT-1] = {0,1,0} }; #elif defined(__APPLE__) #include typedef OSSpinLock Lock; inline static void unlock(Lock *l) { OSSpinLockUnlock(l); } /// Locks a lock. In the current implementation, this is potentially /// unbounded in the contended case. inline static void lock(Lock *l) { OSSpinLockLock(l); } static Lock locks[SPINLOCK_COUNT]; // initialized to OS_SPINLOCK_INIT which is 0 #else typedef _Atomic(uintptr_t) Lock; /// Unlock a lock. This is a release operation. inline static void unlock(Lock *l) { __c11_atomic_store(l, 0, __ATOMIC_RELEASE); } /// Locks a lock. In the current implementation, this is potentially /// unbounded in the contended case. inline static void lock(Lock *l) { uintptr_t old = 0; while (!__c11_atomic_compare_exchange_weak(l, &old, 1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) old = 0; } /// locks for atomic operations static Lock locks[SPINLOCK_COUNT]; #endif /// Returns a lock to use for a given pointer. static inline Lock *lock_for_pointer(void *ptr) { intptr_t hash = (intptr_t)ptr; // Disregard the lowest 4 bits. We want all values that may be part of the // same memory operation to hash to the same value and therefore use the same // lock. hash >>= 4; // Use the next bits as the basis for the hash intptr_t low = hash & SPINLOCK_MASK; // Now use the high(er) set of bits to perturb the hash, so that we don't // get collisions from atomic fields in a single object hash >>= 16; hash ^= low; // Return a pointer to the word to use return locks + (hash & SPINLOCK_MASK); } /// Macros for determining whether a size is lock free. Clang can not yet /// codegen __atomic_is_lock_free(16), so for now we assume 16-byte values are /// not lock free. #define IS_LOCK_FREE_1 __c11_atomic_is_lock_free(1) #define IS_LOCK_FREE_2 __c11_atomic_is_lock_free(2) #define IS_LOCK_FREE_4 __c11_atomic_is_lock_free(4) #define IS_LOCK_FREE_8 __c11_atomic_is_lock_free(8) #define IS_LOCK_FREE_16 0 /// Macro that calls the compiler-generated lock-free versions of functions /// when they exist. #define LOCK_FREE_CASES() \ do {\ switch (size) {\ case 2:\ if (IS_LOCK_FREE_2) {\ LOCK_FREE_ACTION(uint16_t);\ }\ case 4:\ if (IS_LOCK_FREE_4) {\ LOCK_FREE_ACTION(uint32_t);\ }\ case 8:\ if (IS_LOCK_FREE_8) {\ LOCK_FREE_ACTION(uint64_t);\ }\ case 16:\ if (IS_LOCK_FREE_16) {\ /* FIXME: __uint128_t isn't available on 32 bit platforms. LOCK_FREE_ACTION(__uint128_t);*/\ }\ }\ } while (0) /// An atomic load operation. This is atomic with respect to the source /// pointer only. void __atomic_load_c(int size, void *src, void *dest, int model) { #define LOCK_FREE_ACTION(type) \ *((type*)dest) = __c11_atomic_load((_Atomic(type)*)src, model);\ return; LOCK_FREE_CASES(); #undef LOCK_FREE_ACTION Lock *l = lock_for_pointer(src); lock(l); memcpy(dest, src, size); unlock(l); } /// An atomic store operation. This is atomic with respect to the destination /// pointer only. void __atomic_store_c(int size, void *dest, void *src, int model) { #define LOCK_FREE_ACTION(type) \ __c11_atomic_store((_Atomic(type)*)dest, *(type*)dest, model);\ return; LOCK_FREE_CASES(); #undef LOCK_FREE_ACTION Lock *l = lock_for_pointer(dest); lock(l); memcpy(dest, src, size); unlock(l); } /// Atomic compare and exchange operation. If the value at *ptr is identical /// to the value at *expected, then this copies value at *desired to *ptr. If /// they are not, then this stores the current value from *ptr in *expected. /// /// This function returns 1 if the exchange takes place or 0 if it fails. int __atomic_compare_exchange_c(int size, void *ptr, void *expected, void *desired, int success, int failure) { #define LOCK_FREE_ACTION(type) \ return __c11_atomic_compare_exchange_strong((_Atomic(type)*)ptr, (type*)expected,\ *(type*)desired, success, failure) LOCK_FREE_CASES(); #undef LOCK_FREE_ACTION Lock *l = lock_for_pointer(ptr); lock(l); if (memcmp(ptr, expected, size) == 0) { memcpy(ptr, desired, size); unlock(l); return 1; } memcpy(expected, ptr, size); unlock(l); return 0; } /// Performs an atomic exchange operation between two pointers. This is atomic /// with respect to the target address. void __atomic_exchange_c(int size, void *ptr, void *val, void *old, int model) { #define LOCK_FREE_ACTION(type) \ *(type*)old = __c11_atomic_exchange((_Atomic(type)*)ptr, *(type*)val,\ model);\ return; LOCK_FREE_CASES(); #undef LOCK_FREE_ACTION Lock *l = lock_for_pointer(ptr); lock(l); memcpy(old, ptr, size); memcpy(ptr, val, size); unlock(l); } //////////////////////////////////////////////////////////////////////////////// // Where the size is known at compile time, the compiler may emit calls to // specialised versions of the above functions. //////////////////////////////////////////////////////////////////////////////// #define OPTIMISED_CASES\ OPTIMISED_CASE(1, IS_LOCK_FREE_1, uint8_t)\ OPTIMISED_CASE(2, IS_LOCK_FREE_2, uint16_t)\ OPTIMISED_CASE(4, IS_LOCK_FREE_4, uint32_t)\ OPTIMISED_CASE(8, IS_LOCK_FREE_8, uint64_t)\ /* FIXME: __uint128_t isn't available on 32 bit platforms. OPTIMISED_CASE(16, IS_LOCK_FREE_16, __uint128_t)*/\ #define OPTIMISED_CASE(n, lockfree, type)\ type __atomic_load_##n(type *src, int model) {\ if (lockfree)\ return __c11_atomic_load((_Atomic(type)*)src, model);\ Lock *l = lock_for_pointer(src);\ lock(l);\ type val = *src;\ unlock(l);\ return val;\ } OPTIMISED_CASES #undef OPTIMISED_CASE #define OPTIMISED_CASE(n, lockfree, type)\ void __atomic_store_##n(type *dest, type val, int model) {\ if (lockfree) {\ __c11_atomic_store((_Atomic(type)*)dest, val, model);\ return;\ }\ Lock *l = lock_for_pointer(dest);\ lock(l);\ *dest = val;\ unlock(l);\ return;\ } OPTIMISED_CASES #undef OPTIMISED_CASE #define OPTIMISED_CASE(n, lockfree, type)\ type __atomic_exchange_##n(type *dest, type val, int model) {\ if (lockfree)\ return __c11_atomic_exchange((_Atomic(type)*)dest, val, model);\ Lock *l = lock_for_pointer(dest);\ lock(l);\ type tmp = *dest;\ *dest = val;\ unlock(l);\ return tmp;\ } OPTIMISED_CASES #undef OPTIMISED_CASE #define OPTIMISED_CASE(n, lockfree, type)\ int __atomic_compare_exchange_##n(type *ptr, type *expected, type desired,\ int success, int failure) {\ if (lockfree)\ return __c11_atomic_compare_exchange_strong((_Atomic(type)*)ptr, expected, desired,\ success, failure);\ Lock *l = lock_for_pointer(ptr);\ lock(l);\ if (*ptr == *expected) {\ *ptr = desired;\ unlock(l);\ return 1;\ }\ *expected = *ptr;\ unlock(l);\ return 0;\ } OPTIMISED_CASES #undef OPTIMISED_CASE //////////////////////////////////////////////////////////////////////////////// // Atomic read-modify-write operations for integers of various sizes. //////////////////////////////////////////////////////////////////////////////// #define ATOMIC_RMW(n, lockfree, type, opname, op) \ type __atomic_fetch_##opname##_##n(type *ptr, type val, int model) {\ if (lockfree) \ return __c11_atomic_fetch_##opname((_Atomic(type)*)ptr, val, model);\ Lock *l = lock_for_pointer(ptr);\ lock(l);\ type tmp = *ptr;\ *ptr = tmp op val;\ unlock(l);\ return tmp;\ } #define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, add, +) OPTIMISED_CASES #undef OPTIMISED_CASE #define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, sub, -) OPTIMISED_CASES #undef OPTIMISED_CASE #define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, and, &) OPTIMISED_CASES #undef OPTIMISED_CASE #define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, or, |) OPTIMISED_CASES #undef OPTIMISED_CASE #define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, xor, ^) OPTIMISED_CASES #undef OPTIMISED_CASE Index: projects/clang360-import/contrib/compiler-rt/lib/builtins/clear_cache.c =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/builtins/clear_cache.c (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/builtins/clear_cache.c (revision 279191) @@ -1,101 +1,156 @@ /* ===-- clear_cache.c - Implement __clear_cache ---------------------------=== * * 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. * * ===----------------------------------------------------------------------=== */ #include "int_lib.h" #if __APPLE__ #include #endif #if defined(__FreeBSD__) && defined(__arm__) #include #include #endif #if defined(__NetBSD__) && defined(__arm__) #include #endif #if defined(__ANDROID__) && defined(__mips__) #include + #include + #ifdef __LP64__ + /* + * clear_mips_cache - Invalidates instruction cache for Mips. + */ + static void clear_mips_cache(const void* Addr, size_t Size) { + asm volatile ( + ".set push\n" + ".set noreorder\n" + ".set noat\n" + "beq %[Size], $zero, 20f\n" /* If size == 0, branch around. */ + "nop\n" + "daddu %[Size], %[Addr], %[Size]\n" /* Calculate end address + 1 */ + "rdhwr $v0, $1\n" /* Get step size for SYNCI. + $1 is $HW_SYNCI_Step */ + "beq $v0, $zero, 20f\n" /* If no caches require + synchronization, branch + around. */ + "nop\n" + "10:\n" + "synci 0(%[Addr])\n" /* Synchronize all caches around + address. */ + "daddu %[Addr], %[Addr], $v0\n" /* Add step size. */ + "sltu $at, %[Addr], %[Size]\n" /* Compare current with end + address. */ + "bne $at, $zero, 10b\n" /* Branch if more to do. */ + "nop\n" + "sync\n" /* Clear memory hazards. */ + "20:\n" + "bal 30f\n" + "nop\n" + "30:\n" + "daddiu $ra, $ra, 12\n" /* $ra has a value of $pc here. + Add offset of 12 to point to the + instruction after the last nop. + */ + "jr.hb $ra\n" /* Return, clearing instruction + hazards. */ + "nop\n" + ".set pop\n" + : [Addr] "+r"(Addr), [Size] "+r"(Size) + :: "at", "ra", "v0", "memory" + ); + } + #endif #endif #if defined(__ANDROID__) && defined(__arm__) #include #endif /* * The compiler generates calls to __clear_cache() when creating * trampoline functions on the stack for use with nested functions. * It is expected to invalidate the instruction cache for the * specified range. */ void __clear_cache(void *start, void *end) { #if __i386__ || __x86_64__ /* * Intel processors have a unified instruction and data cache * so there is nothing to do */ #elif defined(__arm__) && !defined(__APPLE__) #if defined(__FreeBSD__) || defined(__NetBSD__) struct arm_sync_icache_args arg; arg.addr = (uintptr_t)start; arg.len = (uintptr_t)end - (uintptr_t)start; sysarch(ARM_SYNC_ICACHE, &arg); #elif defined(__ANDROID__) - const register int start_reg __asm("r0") = (int) (intptr_t) start; + register int start_reg __asm("r0") = (int) (intptr_t) start; const register int end_reg __asm("r1") = (int) (intptr_t) end; const register int flags __asm("r2") = 0; const register int syscall_nr __asm("r7") = __ARM_NR_cacheflush; __asm __volatile("svc 0x0" : "=r"(start_reg) : "r"(syscall_nr), "r"(start_reg), "r"(end_reg), "r"(flags) : "r0"); if (start_reg != 0) { compilerrt_abort(); } #else compilerrt_abort(); #endif #elif defined(__ANDROID__) && defined(__mips__) const uintptr_t start_int = (uintptr_t) start; const uintptr_t end_int = (uintptr_t) end; - _flush_cache(start, (end_int - start_int), BCACHE); + #ifdef __LP64__ + // Call synci implementation for short address range. + const uintptr_t address_range_limit = 256; + if ((end_int - start_int) <= address_range_limit) { + clear_mips_cache(start, (end_int - start_int)); + } else { + syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE); + } + #else + syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE); + #endif #elif defined(__aarch64__) && !defined(__APPLE__) uint64_t xstart = (uint64_t)(uintptr_t) start; uint64_t xend = (uint64_t)(uintptr_t) end; // Get Cache Type Info uint64_t ctr_el0; __asm __volatile("mrs %0, ctr_el0" : "=r"(ctr_el0)); /* * dc & ic instructions must use 64bit registers so we don't use * 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) __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) __asm __volatile("ic ivau, %0" :: "r"(addr)); __asm __volatile("isb sy"); #else #if __APPLE__ /* On Darwin, sys_icache_invalidate() provides this functionality */ sys_icache_invalidate(start, end-start); #else compilerrt_abort(); #endif #endif } Index: projects/clang360-import/contrib/compiler-rt/lib/builtins/gcc_personality_v0.c =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/builtins/gcc_personality_v0.c (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/builtins/gcc_personality_v0.c (revision 279191) @@ -1,249 +1,209 @@ /* ===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------=== * * 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. * * ===----------------------------------------------------------------------=== * */ #include "int_lib.h" -/* - * _Unwind_* stuff based on C++ ABI public documentation - * http://refspecs.freestandards.org/abi-eh-1.21.html - */ +#include -typedef enum { - _URC_NO_REASON = 0, - _URC_FOREIGN_EXCEPTION_CAUGHT = 1, - _URC_FATAL_PHASE2_ERROR = 2, - _URC_FATAL_PHASE1_ERROR = 3, - _URC_NORMAL_STOP = 4, - _URC_END_OF_STACK = 5, - _URC_HANDLER_FOUND = 6, - _URC_INSTALL_CONTEXT = 7, - _URC_CONTINUE_UNWIND = 8 -} _Unwind_Reason_Code; - -typedef enum { - _UA_SEARCH_PHASE = 1, - _UA_CLEANUP_PHASE = 2, - _UA_HANDLER_FRAME = 4, - _UA_FORCE_UNWIND = 8, - _UA_END_OF_STACK = 16 -} _Unwind_Action; - -typedef struct _Unwind_Context* _Unwind_Context_t; - -struct _Unwind_Exception { - uint64_t exception_class; - void (*exception_cleanup)(_Unwind_Reason_Code reason, - struct _Unwind_Exception* exc); - uintptr_t private_1; - uintptr_t private_2; -}; - -COMPILER_RT_ABI const uint8_t* _Unwind_GetLanguageSpecificData(_Unwind_Context_t c); -COMPILER_RT_ABI void _Unwind_SetGR(_Unwind_Context_t c, int i, uintptr_t n); -COMPILER_RT_ABI void _Unwind_SetIP(_Unwind_Context_t, uintptr_t new_value); -COMPILER_RT_ABI uintptr_t _Unwind_GetIP(_Unwind_Context_t context); -COMPILER_RT_ABI uintptr_t _Unwind_GetRegionStart(_Unwind_Context_t context); - - /* * Pointer encodings documented at: * http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html */ #define DW_EH_PE_omit 0xff /* no data follows */ #define DW_EH_PE_absptr 0x00 #define DW_EH_PE_uleb128 0x01 #define DW_EH_PE_udata2 0x02 #define DW_EH_PE_udata4 0x03 #define DW_EH_PE_udata8 0x04 #define DW_EH_PE_sleb128 0x09 #define DW_EH_PE_sdata2 0x0A #define DW_EH_PE_sdata4 0x0B #define DW_EH_PE_sdata8 0x0C #define DW_EH_PE_pcrel 0x10 #define DW_EH_PE_textrel 0x20 #define DW_EH_PE_datarel 0x30 #define DW_EH_PE_funcrel 0x40 #define DW_EH_PE_aligned 0x50 #define DW_EH_PE_indirect 0x80 /* gcc extension */ /* read a uleb128 encoded value and advance pointer */ static uintptr_t readULEB128(const uint8_t** data) { uintptr_t result = 0; uintptr_t shift = 0; unsigned char byte; const uint8_t* p = *data; do { byte = *p++; result |= (byte & 0x7f) << shift; shift += 7; } while (byte & 0x80); *data = p; return result; } /* read a pointer encoded value and advance pointer */ static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding) { const uint8_t* p = *data; uintptr_t result = 0; if ( encoding == DW_EH_PE_omit ) return 0; /* first get value */ switch (encoding & 0x0F) { case DW_EH_PE_absptr: result = *((const uintptr_t*)p); p += sizeof(uintptr_t); break; case DW_EH_PE_uleb128: result = readULEB128(&p); break; case DW_EH_PE_udata2: result = *((const uint16_t*)p); p += sizeof(uint16_t); break; case DW_EH_PE_udata4: result = *((const uint32_t*)p); p += sizeof(uint32_t); break; case DW_EH_PE_udata8: result = *((const uint64_t*)p); p += sizeof(uint64_t); break; case DW_EH_PE_sdata2: result = *((const int16_t*)p); p += sizeof(int16_t); break; case DW_EH_PE_sdata4: result = *((const int32_t*)p); p += sizeof(int32_t); break; case DW_EH_PE_sdata8: result = *((const int64_t*)p); p += sizeof(int64_t); break; case DW_EH_PE_sleb128: default: /* not supported */ compilerrt_abort(); break; } /* then add relative offset */ switch ( encoding & 0x70 ) { case DW_EH_PE_absptr: /* do nothing */ break; case DW_EH_PE_pcrel: result += (uintptr_t)(*data); break; case DW_EH_PE_textrel: case DW_EH_PE_datarel: case DW_EH_PE_funcrel: case DW_EH_PE_aligned: default: /* not supported */ compilerrt_abort(); break; } /* then apply indirection */ if (encoding & DW_EH_PE_indirect) { result = *((const uintptr_t*)result); } *data = p; return result; } /* * The C compiler makes references to __gcc_personality_v0 in * the dwarf unwind information for translation units that use * __attribute__((cleanup(xx))) on local variables. * This personality routine is called by the system unwinder * on each frame as the stack is unwound during a C++ exception * throw through a C function compiled with -fexceptions. */ #if __USING_SJLJ_EXCEPTIONS__ // the setjump-longjump based exceptions personality routine has a different name COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0(int version, _Unwind_Action actions, uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, - _Unwind_Context_t context) + struct _Unwind_Context *context) #else COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, - _Unwind_Context_t context) + struct _Unwind_Context *context) #endif { /* Since C does not have catch clauses, there is nothing to do during */ /* phase 1 (the search phase). */ if ( actions & _UA_SEARCH_PHASE ) return _URC_CONTINUE_UNWIND; /* There is nothing to do if there is no LSDA for this frame. */ - const uint8_t* lsda = _Unwind_GetLanguageSpecificData(context); + const uint8_t* lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context); if ( lsda == (uint8_t*) 0 ) return _URC_CONTINUE_UNWIND; uintptr_t pc = _Unwind_GetIP(context)-1; uintptr_t funcStart = _Unwind_GetRegionStart(context); uintptr_t pcOffset = pc - funcStart; /* Parse LSDA header. */ uint8_t lpStartEncoding = *lsda++; if (lpStartEncoding != DW_EH_PE_omit) { readEncodedPointer(&lsda, lpStartEncoding); } uint8_t ttypeEncoding = *lsda++; if (ttypeEncoding != DW_EH_PE_omit) { readULEB128(&lsda); } /* Walk call-site table looking for range that includes current PC. */ uint8_t callSiteEncoding = *lsda++; uint32_t callSiteTableLength = readULEB128(&lsda); const uint8_t* callSiteTableStart = lsda; const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; const uint8_t* p=callSiteTableStart; while (p < callSiteTableEnd) { uintptr_t start = readEncodedPointer(&p, callSiteEncoding); uintptr_t length = readEncodedPointer(&p, callSiteEncoding); uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding); readULEB128(&p); /* action value not used for C code */ if ( landingPad == 0 ) continue; /* no landing pad for this entry */ if ( (start <= pcOffset) && (pcOffset < (start+length)) ) { /* Found landing pad for the PC. * Set Instruction Pointer to so we re-enter function * at landing pad. The landing pad is created by the compiler * to take two parameters in registers. */ _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject); _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); _Unwind_SetIP(context, funcStart+landingPad); return _URC_INSTALL_CONTEXT; } } /* No landing pad found, continue unwinding. */ return _URC_CONTINUE_UNWIND; } Index: projects/clang360-import/contrib/compiler-rt/lib/builtins/int_types.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/builtins/int_types.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/builtins/int_types.h (revision 279191) @@ -1,143 +1,144 @@ /* ===-- int_lib.h - configuration header for compiler-rt -----------------=== * * 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 is not part of the interface of this library. * * This file defines various standard types, most importantly a number of unions * used to access parts of larger types. * * ===----------------------------------------------------------------------=== */ #ifndef INT_TYPES_H #define INT_TYPES_H #include "int_endianness.h" typedef int si_int; typedef unsigned su_int; typedef long long di_int; typedef unsigned long long du_int; typedef union { di_int all; struct { #if _YUGA_LITTLE_ENDIAN su_int low; si_int high; #else si_int high; su_int low; #endif /* _YUGA_LITTLE_ENDIAN */ }s; } dwords; typedef union { du_int all; struct { #if _YUGA_LITTLE_ENDIAN su_int low; su_int high; #else su_int high; su_int low; #endif /* _YUGA_LITTLE_ENDIAN */ }s; } udwords; -#if __LP64__ +/* MIPS64 issue: PR 20098 */ +#if defined(__LP64__) && !(defined(__mips__) && defined(__clang__)) #define CRT_HAS_128BIT #endif #ifdef CRT_HAS_128BIT typedef int ti_int __attribute__ ((mode (TI))); typedef unsigned tu_int __attribute__ ((mode (TI))); typedef union { ti_int all; struct { #if _YUGA_LITTLE_ENDIAN du_int low; di_int high; #else di_int high; du_int low; #endif /* _YUGA_LITTLE_ENDIAN */ }s; } twords; typedef union { tu_int all; struct { #if _YUGA_LITTLE_ENDIAN du_int low; du_int high; #else du_int high; du_int low; #endif /* _YUGA_LITTLE_ENDIAN */ }s; } utwords; static inline ti_int make_ti(di_int h, di_int l) { twords r; r.s.high = h; r.s.low = l; return r.all; } static inline tu_int make_tu(du_int h, du_int l) { utwords r; r.s.high = h; r.s.low = l; return r.all; } #endif /* CRT_HAS_128BIT */ typedef union { su_int u; float f; } float_bits; typedef union { udwords u; double f; } double_bits; typedef struct { #if _YUGA_LITTLE_ENDIAN udwords low; udwords high; #else udwords high; udwords low; #endif /* _YUGA_LITTLE_ENDIAN */ } uqwords; typedef union { uqwords u; long double f; } long_double_bits; #endif /* INT_TYPES_H */ Index: projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan.cc (revision 279191) @@ -1,372 +1,381 @@ //===-- dfsan.cc ----------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of DataFlowSanitizer. // // DataFlowSanitizer runtime. This file defines the public interface to // DataFlowSanitizer as well as the definition of certain runtime functions // called automatically by the compiler (specifically the instrumentation pass // in llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp). // // The public interface is defined in include/sanitizer/dfsan_interface.h whose // functions are prefixed dfsan_ while the compiler interface functions are // prefixed __dfsan_. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" #include "dfsan/dfsan.h" using namespace __dfsan; typedef atomic_uint16_t atomic_dfsan_label; static const dfsan_label kInitializingLabel = -1; static const uptr kNumLabels = 1 << (sizeof(dfsan_label) * 8); static atomic_dfsan_label __dfsan_last_label; static dfsan_label_info __dfsan_label_info[kNumLabels]; Flags __dfsan::flags_data; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_retval_tls; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL dfsan_label __dfsan_arg_tls[64]; // On Linux/x86_64, memory is laid out as follows: // // +--------------------+ 0x800000000000 (top of memory) // | application memory | // +--------------------+ 0x700000008000 (kAppAddr) // | | // | unused | // | | // +--------------------+ 0x200200000000 (kUnusedAddr) // | union table | // +--------------------+ 0x200000000000 (kUnionTableAddr) // | shadow memory | // +--------------------+ 0x000000010000 (kShadowAddr) // | reserved by kernel | // +--------------------+ 0x000000000000 // // To derive a shadow memory address from an application memory address, // bits 44-46 are cleared to bring the address into the range // [0x000000008000,0x100000000000). Then the address is shifted left by 1 to // account for the double byte representation of shadow labels and move the // address into the shadow memory range. See the function shadow_for below. // On Linux/MIPS64, memory is laid out as follows: // // +--------------------+ 0x10000000000 (top of memory) // | application memory | // +--------------------+ 0xF000008000 (kAppAddr) // | | // | unused | // | | // +--------------------+ 0x2200000000 (kUnusedAddr) // | union table | // +--------------------+ 0x2000000000 (kUnionTableAddr) // | shadow memory | // +--------------------+ 0x0000010000 (kShadowAddr) // | reserved by kernel | // +--------------------+ 0x0000000000 typedef atomic_dfsan_label dfsan_union_table_t[kNumLabels][kNumLabels]; #if defined(__x86_64__) static const uptr kShadowAddr = 0x10000; static const uptr kUnionTableAddr = 0x200000000000; static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t); static const uptr kAppAddr = 0x700000008000; #elif defined(__mips64) static const uptr kShadowAddr = 0x10000; static const uptr kUnionTableAddr = 0x2000000000; static const uptr kUnusedAddr = kUnionTableAddr + sizeof(dfsan_union_table_t); static const uptr kAppAddr = 0xF000008000; #else # error "DFSan not supported for this platform!" #endif static atomic_dfsan_label *union_table(dfsan_label l1, dfsan_label l2) { return &(*(dfsan_union_table_t *) kUnionTableAddr)[l1][l2]; } // Checks we do not run out of labels. static void dfsan_check_label(dfsan_label label) { if (label == kInitializingLabel) { Report("FATAL: DataFlowSanitizer: out of labels\n"); Die(); } } // Resolves the union of two unequal labels. Nonequality is a precondition for // this function (the instrumentation pass inlines the equality test). extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label __dfsan_union(dfsan_label l1, dfsan_label l2) { DCHECK_NE(l1, l2); if (l1 == 0) return l2; if (l2 == 0) return l1; if (l1 > l2) Swap(l1, l2); atomic_dfsan_label *table_ent = union_table(l1, l2); // We need to deal with the case where two threads concurrently request // a union of the same pair of labels. If the table entry is uninitialized, // (i.e. 0) use a compare-exchange to set the entry to kInitializingLabel // (i.e. -1) to mark that we are initializing it. dfsan_label label = 0; if (atomic_compare_exchange_strong(table_ent, &label, kInitializingLabel, memory_order_acquire)) { // Check whether l2 subsumes l1. We don't need to check whether l1 // subsumes l2 because we are guaranteed here that l1 < l2, and (at least // in the cases we are interested in) a label may only subsume labels // created earlier (i.e. with a lower numerical value). if (__dfsan_label_info[l2].l1 == l1 || __dfsan_label_info[l2].l2 == l1) { label = l2; } else { label = atomic_fetch_add(&__dfsan_last_label, 1, memory_order_relaxed) + 1; dfsan_check_label(label); __dfsan_label_info[label].l1 = l1; __dfsan_label_info[label].l2 = l2; } atomic_store(table_ent, label, memory_order_release); } else if (label == kInitializingLabel) { // Another thread is initializing the entry. Wait until it is finished. do { internal_sched_yield(); label = atomic_load(table_ent, memory_order_acquire); } while (label == kInitializingLabel); } return label; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label __dfsan_union_load(const dfsan_label *ls, uptr n) { dfsan_label label = ls[0]; for (uptr i = 1; i != n; ++i) { dfsan_label next_label = ls[i]; if (label != next_label) label = __dfsan_union(label, next_label); } return label; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_unimplemented(char *fname) { if (flags().warn_unimplemented) Report("WARNING: DataFlowSanitizer: call to uninstrumented function %s\n", fname); } // Use '-mllvm -dfsan-debug-nonzero-labels' and break on this function // to try to figure out where labels are being introduced in a nominally // label-free program. extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_nonzero_label() { if (flags().warn_nonzero_labels) Report("WARNING: DataFlowSanitizer: saw nonzero label\n"); } // Indirect call to an uninstrumented vararg function. We don't have a way of // handling these at the moment. extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_vararg_wrapper(const char *fname) { Report("FATAL: DataFlowSanitizer: unsupported indirect call to vararg " "function %s\n", fname); Die(); } // Like __dfsan_union, but for use from the client or custom functions. Hence // the equality comparison is done here before calling __dfsan_union. SANITIZER_INTERFACE_ATTRIBUTE dfsan_label dfsan_union(dfsan_label l1, dfsan_label l2) { if (l1 == l2) return l1; return __dfsan_union(l1, l2); } extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label dfsan_create_label(const char *desc, void *userdata) { dfsan_label label = atomic_fetch_add(&__dfsan_last_label, 1, memory_order_relaxed) + 1; dfsan_check_label(label); __dfsan_label_info[label].l1 = __dfsan_label_info[label].l2 = 0; __dfsan_label_info[label].desc = desc; __dfsan_label_info[label].userdata = userdata; return label; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __dfsan_set_label(dfsan_label label, void *addr, uptr size) { for (dfsan_label *labelp = shadow_for(addr); size != 0; --size, ++labelp) { // Don't write the label if it is already the value we need it to be. // In a program where most addresses are not labeled, it is common that // a page of shadow memory is entirely zeroed. The Linux copy-on-write // implementation will share all of the zeroed pages, making a copy of a // page when any value is written. The un-sharing will happen even if // the value written does not change the value in memory. Avoiding the // write when both |label| and |*labelp| are zero dramatically reduces // the amount of real memory used by large programs. if (label == *labelp) continue; *labelp = label; } } SANITIZER_INTERFACE_ATTRIBUTE void dfsan_set_label(dfsan_label label, void *addr, uptr size) { __dfsan_set_label(label, addr, size); } SANITIZER_INTERFACE_ATTRIBUTE void dfsan_add_label(dfsan_label label, void *addr, uptr size) { for (dfsan_label *labelp = shadow_for(addr); size != 0; --size, ++labelp) if (*labelp != label) *labelp = __dfsan_union(*labelp, label); } // Unlike the other dfsan interface functions the behavior of this function // depends on the label of one of its arguments. Hence it is implemented as a // custom function. extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label __dfsw_dfsan_get_label(long data, dfsan_label data_label, dfsan_label *ret_label) { *ret_label = 0; return data_label; } SANITIZER_INTERFACE_ATTRIBUTE dfsan_label dfsan_read_label(const void *addr, uptr size) { if (size == 0) return 0; return __dfsan_union_load(shadow_for(addr), size); } -SANITIZER_INTERFACE_ATTRIBUTE +extern "C" SANITIZER_INTERFACE_ATTRIBUTE const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label) { return &__dfsan_label_info[label]; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE int dfsan_has_label(dfsan_label label, dfsan_label elem) { if (label == elem) return true; const dfsan_label_info *info = dfsan_get_label_info(label); if (info->l1 != 0) { return dfsan_has_label(info->l1, elem) || dfsan_has_label(info->l2, elem); } else { return false; } } extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label dfsan_has_label_with_desc(dfsan_label label, const char *desc) { const dfsan_label_info *info = dfsan_get_label_info(label); if (info->l1 != 0) { return dfsan_has_label_with_desc(info->l1, desc) || dfsan_has_label_with_desc(info->l2, desc); } else { return internal_strcmp(desc, info->desc) == 0; } } extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr dfsan_get_label_count(void) { dfsan_label max_label_allocated = atomic_load(&__dfsan_last_label, memory_order_relaxed); return static_cast(max_label_allocated); } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void dfsan_dump_labels(int fd) { dfsan_label last_label = atomic_load(&__dfsan_last_label, memory_order_relaxed); for (uptr l = 1; l <= last_label; ++l) { 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)); 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)); } internal_write(fd, "\n", 1); } } -static void InitializeFlags(Flags &f, const char *env) { - f.warn_unimplemented = true; - f.warn_nonzero_labels = false; - f.strict_data_dependencies = true; - f.dump_labels_at_exit = ""; +void Flags::SetDefaults() { +#define DFSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "dfsan_flags.inc" +#undef DFSAN_FLAG +} - ParseFlag(env, &f.warn_unimplemented, "warn_unimplemented", ""); - ParseFlag(env, &f.warn_nonzero_labels, "warn_nonzero_labels", ""); - ParseFlag(env, &f.strict_data_dependencies, "strict_data_dependencies", ""); - ParseFlag(env, &f.dump_labels_at_exit, "dump_labels_at_exit", ""); +void RegisterDfsanFlags(FlagParser *parser, Flags *f) { +#define DFSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "dfsan_flags.inc" +#undef DFSAN_FLAG +} + +static void InitializeFlags(Flags &f, const char *env) { + FlagParser parser; + RegisterDfsanFlags(&parser, &f); + f.SetDefaults(); + parser.ParseString(env); } static void dfsan_fini() { if (internal_strcmp(flags().dump_labels_at_exit, "") != 0) { fd_t fd = OpenFile(flags().dump_labels_at_exit, true /* write */); if (fd == kInvalidFd) { Report("WARNING: DataFlowSanitizer: unable to open output file %s\n", flags().dump_labels_at_exit); return; } Report("INFO: DataFlowSanitizer: dumping labels to %s\n", flags().dump_labels_at_exit); dfsan_dump_labels(fd); internal_close(fd); } } #ifdef DFSAN_NOLIBC extern "C" void dfsan_init() { #else static void dfsan_init(int argc, char **argv, char **envp) { #endif MmapFixedNoReserve(kShadowAddr, kUnusedAddr - kShadowAddr); // Protect the region of memory we don't use, to preserve the one-to-one // mapping from application to shadow memory. But if ASLR is disabled, Linux // will load our executable in the middle of our unused region. This mostly // works so long as the program doesn't use too much memory. We support this // 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); InitializeFlags(flags(), GetEnv("DFSAN_OPTIONS")); InitializeInterceptors(); // Register the fini callback to run when the program terminates successfully // or it is killed by the runtime. Atexit(dfsan_fini); SetDieCallback(dfsan_fini); __dfsan_label_info[kInitializingLabel].desc = ""; } #if !defined(DFSAN_NOLIBC) && SANITIZER_CAN_USE_PREINIT_ARRAY __attribute__((section(".preinit_array"), used)) static void (*dfsan_init_ptr)(int, char **, char **) = dfsan_init; #endif Index: projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan.h (revision 279191) @@ -1,79 +1,73 @@ //===-- dfsan.h -------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of DataFlowSanitizer. // // Private DFSan header. //===----------------------------------------------------------------------===// #ifndef DFSAN_H #define DFSAN_H #include "sanitizer_common/sanitizer_internal_defs.h" // Copy declarations from public sanitizer/dfsan_interface.h header here. typedef u16 dfsan_label; struct dfsan_label_info { dfsan_label l1; dfsan_label l2; const char *desc; void *userdata; }; extern "C" { void dfsan_add_label(dfsan_label label, void *addr, uptr size); void dfsan_set_label(dfsan_label label, void *addr, uptr size); dfsan_label dfsan_read_label(const void *addr, uptr size); dfsan_label dfsan_union(dfsan_label l1, dfsan_label l2); } // extern "C" template void dfsan_set_label(dfsan_label label, T &data) { // NOLINT dfsan_set_label(label, (void *)&data, sizeof(T)); } namespace __dfsan { void InitializeInterceptors(); inline dfsan_label *shadow_for(void *ptr) { #if defined(__x86_64__) return (dfsan_label *) ((((uptr) ptr) & ~0x700000000000) << 1); #elif defined(__mips64) return (dfsan_label *) ((((uptr) ptr) & ~0xF000000000) << 1); #endif } inline const dfsan_label *shadow_for(const void *ptr) { return shadow_for(const_cast(ptr)); } struct Flags { - // Whether to warn on unimplemented functions. - bool warn_unimplemented; - // Whether to warn on non-zero labels. - bool warn_nonzero_labels; - // Whether to propagate labels only when there is an obvious data dependency - // (e.g., when comparing strings, ignore the fact that the output of the - // comparison might be data-dependent on the content of the strings). This - // applies only to the custom functions defined in 'custom.c'. - bool strict_data_dependencies; - // The path of the file where to dump the labels when the program terminates. - const char* dump_labels_at_exit; +#define DFSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "dfsan_flags.inc" +#undef DFSAN_FLAG + + void SetDefaults(); }; extern Flags flags_data; inline Flags &flags() { return flags_data; } } // namespace __dfsan #endif // DFSAN_H Index: projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan_custom.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan_custom.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan_custom.cc (revision 279191) @@ -1,1124 +1,1125 @@ //===-- dfsan.cc ----------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of DataFlowSanitizer. // // This file defines the custom functions listed in done_abilist.txt. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_linux.h" #include "dfsan/dfsan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace __dfsan; extern "C" { SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_stat(const char *path, struct stat *buf, dfsan_label path_label, dfsan_label buf_label, dfsan_label *ret_label) { int ret = stat(path, buf); if (ret == 0) dfsan_set_label(0, buf, sizeof(struct stat)); *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_fstat(int fd, struct stat *buf, dfsan_label fd_label, dfsan_label buf_label, dfsan_label *ret_label) { int ret = fstat(fd, buf); if (ret == 0) dfsan_set_label(0, buf, sizeof(struct stat)); *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strchr(const char *s, int c, dfsan_label s_label, dfsan_label c_label, dfsan_label *ret_label) { for (size_t i = 0;; ++i) { if (s[i] == c || s[i] == 0) { if (flags().strict_data_dependencies) { *ret_label = s_label; } else { *ret_label = dfsan_union(dfsan_read_label(s, i + 1), dfsan_union(s_label, c_label)); } return s[i] == 0 ? 0 : const_cast(s+i); } } } 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) { const char *cs1 = (const char *) s1, *cs2 = (const char *) s2; for (size_t i = 0; i != n; ++i) { if (cs1[i] != cs2[i]) { if (flags().strict_data_dependencies) { *ret_label = 0; } else { *ret_label = dfsan_union(dfsan_read_label(cs1, i + 1), dfsan_read_label(cs2, i + 1)); } return cs1[i] - cs2[i]; } } if (flags().strict_data_dependencies) { *ret_label = 0; } else { *ret_label = dfsan_union(dfsan_read_label(cs1, n), dfsan_read_label(cs2, n)); } return 0; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strcmp(const char *s1, const char *s2, dfsan_label s1_label, dfsan_label s2_label, dfsan_label *ret_label) { for (size_t i = 0;; ++i) { if (s1[i] != s2[i] || s1[i] == 0 || s2[i] == 0) { if (flags().strict_data_dependencies) { *ret_label = 0; } else { *ret_label = dfsan_union(dfsan_read_label(s1, i + 1), dfsan_read_label(s2, i + 1)); } return s1[i] - s2[i]; } } return 0; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strcasecmp(const char *s1, const char *s2, dfsan_label s1_label, dfsan_label s2_label, dfsan_label *ret_label) { for (size_t i = 0;; ++i) { if (tolower(s1[i]) != tolower(s2[i]) || s1[i] == 0 || s2[i] == 0) { if (flags().strict_data_dependencies) { *ret_label = 0; } else { *ret_label = dfsan_union(dfsan_read_label(s1, i + 1), dfsan_read_label(s2, i + 1)); } return s1[i] - s2[i]; } } return 0; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strncmp(const char *s1, const char *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label, dfsan_label *ret_label) { if (n == 0) { *ret_label = 0; return 0; } for (size_t i = 0;; ++i) { if (s1[i] != s2[i] || s1[i] == 0 || s2[i] == 0 || i == n - 1) { if (flags().strict_data_dependencies) { *ret_label = 0; } else { *ret_label = dfsan_union(dfsan_read_label(s1, i + 1), dfsan_read_label(s2, i + 1)); } return s1[i] - s2[i]; } } return 0; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_strncasecmp(const char *s1, const char *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label, dfsan_label *ret_label) { if (n == 0) { *ret_label = 0; return 0; } for (size_t i = 0;; ++i) { if (tolower(s1[i]) != tolower(s2[i]) || s1[i] == 0 || s2[i] == 0 || i == n - 1) { if (flags().strict_data_dependencies) { *ret_label = 0; } else { *ret_label = dfsan_union(dfsan_read_label(s1, i + 1), dfsan_read_label(s2, i + 1)); } return s1[i] - s2[i]; } } return 0; } SANITIZER_INTERFACE_ATTRIBUTE void *__dfsw_calloc(size_t nmemb, size_t size, dfsan_label nmemb_label, dfsan_label size_label, dfsan_label *ret_label) { void *p = calloc(nmemb, size); dfsan_set_label(0, p, nmemb * size); *ret_label = 0; return p; } SANITIZER_INTERFACE_ATTRIBUTE size_t __dfsw_strlen(const char *s, dfsan_label s_label, dfsan_label *ret_label) { size_t ret = strlen(s); if (flags().strict_data_dependencies) { *ret_label = 0; } else { *ret_label = dfsan_read_label(s, ret + 1); } return ret; } static void *dfsan_memcpy(void *dest, const void *src, size_t n) { dfsan_label *sdest = shadow_for(dest); const dfsan_label *ssrc = shadow_for(src); internal_memcpy((void *)sdest, (const void *)ssrc, n * sizeof(dfsan_label)); return internal_memcpy(dest, src, n); } static void dfsan_memset(void *s, int c, dfsan_label c_label, size_t n) { internal_memset(s, c, n); dfsan_set_label(c_label, s, n); } SANITIZER_INTERFACE_ATTRIBUTE void *__dfsw_memcpy(void *dest, const void *src, size_t n, dfsan_label dest_label, dfsan_label src_label, dfsan_label n_label, dfsan_label *ret_label) { *ret_label = dest_label; return dfsan_memcpy(dest, src, n); } SANITIZER_INTERFACE_ATTRIBUTE void *__dfsw_memset(void *s, int c, size_t n, dfsan_label s_label, dfsan_label c_label, dfsan_label n_label, dfsan_label *ret_label) { dfsan_memset(s, c, c_label, n); *ret_label = s_label; return s; } SANITIZER_INTERFACE_ATTRIBUTE char * __dfsw_strdup(const char *s, dfsan_label s_label, dfsan_label *ret_label) { size_t len = strlen(s); void *p = malloc(len+1); dfsan_memcpy(p, s, len+1); *ret_label = 0; return static_cast(p); } SANITIZER_INTERFACE_ATTRIBUTE char * __dfsw_strncpy(char *s1, const char *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label, dfsan_label *ret_label) { size_t len = strlen(s2); if (len < n) { dfsan_memcpy(s1, s2, len+1); dfsan_memset(s1+len+1, 0, 0, n-len-1); } else { dfsan_memcpy(s1, s2, n); } *ret_label = s1_label; return s1; } SANITIZER_INTERFACE_ATTRIBUTE ssize_t __dfsw_pread(int fd, void *buf, size_t count, off_t offset, dfsan_label fd_label, dfsan_label buf_label, dfsan_label count_label, dfsan_label offset_label, dfsan_label *ret_label) { ssize_t ret = pread(fd, buf, count, offset); if (ret > 0) dfsan_set_label(0, buf, ret); *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE ssize_t __dfsw_read(int fd, void *buf, size_t count, dfsan_label fd_label, dfsan_label buf_label, dfsan_label count_label, dfsan_label *ret_label) { ssize_t ret = read(fd, buf, count); if (ret > 0) dfsan_set_label(0, buf, ret); *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_clock_gettime(clockid_t clk_id, struct timespec *tp, dfsan_label clk_id_label, dfsan_label tp_label, dfsan_label *ret_label) { int ret = clock_gettime(clk_id, tp); if (ret == 0) dfsan_set_label(0, tp, sizeof(struct timespec)); *ret_label = 0; return ret; } static void unpoison(const void *ptr, uptr size) { dfsan_set_label(0, const_cast(ptr), size); } // dlopen() ultimately calls mmap() down inside the loader, which generally // doesn't participate in dynamic symbol resolution. Therefore we won't // intercept its calls to mmap, and we have to hook it here. SANITIZER_INTERFACE_ATTRIBUTE void * __dfsw_dlopen(const char *filename, int flag, dfsan_label filename_label, dfsan_label flag_label, dfsan_label *ret_label) { - link_map *map = (link_map *)dlopen(filename, flag); + void *handle = dlopen(filename, flag); + link_map *map = GET_LINK_MAP_BY_DLOPEN_HANDLE(handle); if (map) ForEachMappedRegion(map, unpoison); *ret_label = 0; - return (void *)map; + return handle; } struct pthread_create_info { void *(*start_routine_trampoline)(void *, void *, dfsan_label, dfsan_label *); void *start_routine; void *arg; }; static void *pthread_create_cb(void *p) { pthread_create_info pci(*(pthread_create_info *)p); free(p); dfsan_label ret_label; return pci.start_routine_trampoline(pci.start_routine, pci.arg, 0, &ret_label); } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_pthread_create( pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine_trampoline)(void *, void *, dfsan_label, dfsan_label *), void *start_routine, void *arg, dfsan_label thread_label, dfsan_label attr_label, dfsan_label start_routine_label, dfsan_label arg_label, dfsan_label *ret_label) { pthread_create_info *pci = (pthread_create_info *)malloc(sizeof(pthread_create_info)); pci->start_routine_trampoline = start_routine_trampoline; pci->start_routine = start_routine; pci->arg = arg; int rv = pthread_create(thread, attr, pthread_create_cb, (void *)pci); if (rv != 0) free(pci); *ret_label = 0; return rv; } struct dl_iterate_phdr_info { int (*callback_trampoline)(void *callback, struct dl_phdr_info *info, size_t size, void *data, dfsan_label info_label, dfsan_label size_label, dfsan_label data_label, dfsan_label *ret_label); void *callback; void *data; }; int dl_iterate_phdr_cb(struct dl_phdr_info *info, size_t size, void *data) { dl_iterate_phdr_info *dipi = (dl_iterate_phdr_info *)data; dfsan_set_label(0, *info); dfsan_set_label(0, const_cast(info->dlpi_name), strlen(info->dlpi_name) + 1); dfsan_set_label( 0, const_cast(reinterpret_cast(info->dlpi_phdr)), sizeof(*info->dlpi_phdr) * info->dlpi_phnum); dfsan_label ret_label; return dipi->callback_trampoline(dipi->callback, info, size, dipi->data, 0, 0, 0, &ret_label); } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_dl_iterate_phdr( int (*callback_trampoline)(void *callback, struct dl_phdr_info *info, size_t size, void *data, dfsan_label info_label, dfsan_label size_label, dfsan_label data_label, dfsan_label *ret_label), void *callback, void *data, dfsan_label callback_label, dfsan_label data_label, dfsan_label *ret_label) { dl_iterate_phdr_info dipi = { callback_trampoline, callback, data }; *ret_label = 0; return dl_iterate_phdr(dl_iterate_phdr_cb, &dipi); } SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_ctime_r(const time_t *timep, char *buf, dfsan_label timep_label, dfsan_label buf_label, dfsan_label *ret_label) { char *ret = ctime_r(timep, buf); if (ret) { dfsan_set_label(dfsan_read_label(timep, sizeof(time_t)), buf, strlen(buf) + 1); *ret_label = buf_label; } else { *ret_label = 0; } return ret; } SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_fgets(char *s, int size, FILE *stream, dfsan_label s_label, dfsan_label size_label, dfsan_label stream_label, dfsan_label *ret_label) { char *ret = fgets(s, size, stream); if (ret) { dfsan_set_label(0, ret, strlen(ret) + 1); *ret_label = s_label; } else { *ret_label = 0; } return ret; } SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_getcwd(char *buf, size_t size, dfsan_label buf_label, dfsan_label size_label, dfsan_label *ret_label) { char *ret = getcwd(buf, size); if (ret) { dfsan_set_label(0, ret, strlen(ret) + 1); *ret_label = buf_label; } else { *ret_label = 0; } return ret; } SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_get_current_dir_name(dfsan_label *ret_label) { char *ret = get_current_dir_name(); if (ret) { dfsan_set_label(0, ret, strlen(ret) + 1); } *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_gethostname(char *name, size_t len, dfsan_label name_label, dfsan_label len_label, dfsan_label *ret_label) { int ret = gethostname(name, len); if (ret == 0) { dfsan_set_label(0, name, strlen(name) + 1); } *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_getrlimit(int resource, struct rlimit *rlim, dfsan_label resource_label, dfsan_label rlim_label, dfsan_label *ret_label) { int ret = getrlimit(resource, rlim); if (ret == 0) { dfsan_set_label(0, rlim, sizeof(struct rlimit)); } *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_getrusage(int who, struct rusage *usage, dfsan_label who_label, dfsan_label usage_label, dfsan_label *ret_label) { int ret = getrusage(who, usage); if (ret == 0) { dfsan_set_label(0, usage, sizeof(struct rusage)); } *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strcpy(char *dest, const char *src, dfsan_label dst_label, dfsan_label src_label, dfsan_label *ret_label) { char *ret = strcpy(dest, src); if (ret) { internal_memcpy(shadow_for(dest), shadow_for(src), sizeof(dfsan_label) * (strlen(src) + 1)); } *ret_label = dst_label; return ret; } SANITIZER_INTERFACE_ATTRIBUTE long int __dfsw_strtol(const char *nptr, char **endptr, int base, dfsan_label nptr_label, dfsan_label endptr_label, dfsan_label base_label, dfsan_label *ret_label) { char *tmp_endptr; long int ret = strtol(nptr, &tmp_endptr, base); if (endptr) { *endptr = tmp_endptr; } if (tmp_endptr > nptr) { // If *tmp_endptr is '\0' include its label as well. *ret_label = dfsan_union( base_label, dfsan_read_label(nptr, tmp_endptr - nptr + (*tmp_endptr ? 0 : 1))); } else { *ret_label = 0; } return ret; } SANITIZER_INTERFACE_ATTRIBUTE double __dfsw_strtod(const char *nptr, char **endptr, dfsan_label nptr_label, dfsan_label endptr_label, dfsan_label *ret_label) { char *tmp_endptr; double ret = strtod(nptr, &tmp_endptr); if (endptr) { *endptr = tmp_endptr; } if (tmp_endptr > nptr) { // If *tmp_endptr is '\0' include its label as well. *ret_label = dfsan_read_label( nptr, tmp_endptr - nptr + (*tmp_endptr ? 0 : 1)); } else { *ret_label = 0; } return ret; } SANITIZER_INTERFACE_ATTRIBUTE long long int __dfsw_strtoll(const char *nptr, char **endptr, int base, dfsan_label nptr_label, dfsan_label endptr_label, dfsan_label base_label, dfsan_label *ret_label) { char *tmp_endptr; long long int ret = strtoll(nptr, &tmp_endptr, base); if (endptr) { *endptr = tmp_endptr; } if (tmp_endptr > nptr) { // If *tmp_endptr is '\0' include its label as well. *ret_label = dfsan_union( base_label, dfsan_read_label(nptr, tmp_endptr - nptr + (*tmp_endptr ? 0 : 1))); } else { *ret_label = 0; } return ret; } SANITIZER_INTERFACE_ATTRIBUTE unsigned long int __dfsw_strtoul(const char *nptr, char **endptr, int base, dfsan_label nptr_label, dfsan_label endptr_label, dfsan_label base_label, dfsan_label *ret_label) { char *tmp_endptr; unsigned long int ret = strtoul(nptr, &tmp_endptr, base); if (endptr) { *endptr = tmp_endptr; } if (tmp_endptr > nptr) { // If *tmp_endptr is '\0' include its label as well. *ret_label = dfsan_union( base_label, dfsan_read_label(nptr, tmp_endptr - nptr + (*tmp_endptr ? 0 : 1))); } else { *ret_label = 0; } return ret; } SANITIZER_INTERFACE_ATTRIBUTE long long unsigned int __dfsw_strtoull(const char *nptr, char **endptr, dfsan_label nptr_label, int base, dfsan_label endptr_label, dfsan_label base_label, dfsan_label *ret_label) { char *tmp_endptr; long long unsigned int ret = strtoull(nptr, &tmp_endptr, base); if (endptr) { *endptr = tmp_endptr; } if (tmp_endptr > nptr) { // If *tmp_endptr is '\0' include its label as well. *ret_label = dfsan_union( base_label, dfsan_read_label(nptr, tmp_endptr - nptr + (*tmp_endptr ? 0 : 1))); } else { *ret_label = 0; } return ret; } SANITIZER_INTERFACE_ATTRIBUTE time_t __dfsw_time(time_t *t, dfsan_label t_label, dfsan_label *ret_label) { time_t ret = time(t); if (ret != (time_t) -1 && t) { dfsan_set_label(0, t, sizeof(time_t)); } *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_inet_pton(int af, const char *src, void *dst, dfsan_label af_label, dfsan_label src_label, dfsan_label dst_label, dfsan_label *ret_label) { int ret = inet_pton(af, src, dst); if (ret == 1) { dfsan_set_label(dfsan_read_label(src, strlen(src) + 1), dst, af == AF_INET ? sizeof(struct in_addr) : sizeof(in6_addr)); } *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE struct tm *__dfsw_localtime_r(const time_t *timep, struct tm *result, dfsan_label timep_label, dfsan_label result_label, dfsan_label *ret_label) { struct tm *ret = localtime_r(timep, result); if (ret) { dfsan_set_label(dfsan_read_label(timep, sizeof(time_t)), result, sizeof(struct tm)); *ret_label = result_label; } else { *ret_label = 0; } return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_getpwuid_r(id_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result, dfsan_label uid_label, dfsan_label pwd_label, dfsan_label buf_label, dfsan_label buflen_label, dfsan_label result_label, dfsan_label *ret_label) { // Store the data in pwd, the strings referenced from pwd in buf, and the // address of pwd in *result. On failure, NULL is stored in *result. int ret = getpwuid_r(uid, pwd, buf, buflen, result); if (ret == 0) { dfsan_set_label(0, pwd, sizeof(struct passwd)); dfsan_set_label(0, buf, strlen(buf) + 1); } *ret_label = 0; dfsan_set_label(0, result, sizeof(struct passwd*)); return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_poll(struct pollfd *fds, nfds_t nfds, int timeout, dfsan_label dfs_label, dfsan_label nfds_label, dfsan_label timeout_label, dfsan_label *ret_label) { int ret = poll(fds, nfds, timeout); if (ret >= 0) { for (; nfds > 0; --nfds) { dfsan_set_label(0, &fds[nfds - 1].revents, sizeof(fds[nfds - 1].revents)); } } *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout, dfsan_label nfds_label, dfsan_label readfds_label, dfsan_label writefds_label, dfsan_label exceptfds_label, dfsan_label timeout_label, dfsan_label *ret_label) { int ret = select(nfds, readfds, writefds, exceptfds, timeout); // Clear everything (also on error) since their content is either set or // undefined. if (readfds) { dfsan_set_label(0, readfds, sizeof(fd_set)); } if (writefds) { dfsan_set_label(0, writefds, sizeof(fd_set)); } if (exceptfds) { dfsan_set_label(0, exceptfds, sizeof(fd_set)); } dfsan_set_label(0, timeout, sizeof(struct timeval)); *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask, dfsan_label pid_label, dfsan_label cpusetsize_label, dfsan_label mask_label, dfsan_label *ret_label) { int ret = sched_getaffinity(pid, cpusetsize, mask); if (ret == 0) { dfsan_set_label(0, mask, cpusetsize); } *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_sigemptyset(sigset_t *set, dfsan_label set_label, dfsan_label *ret_label) { int ret = sigemptyset(set); dfsan_set_label(0, set, sizeof(sigset_t)); return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact, dfsan_label signum_label, dfsan_label act_label, dfsan_label oldact_label, dfsan_label *ret_label) { int ret = sigaction(signum, act, oldact); if (oldact) { dfsan_set_label(0, oldact, sizeof(struct sigaction)); } *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_gettimeofday(struct timeval *tv, struct timezone *tz, dfsan_label tv_label, dfsan_label tz_label, dfsan_label *ret_label) { int ret = gettimeofday(tv, tz); if (tv) { dfsan_set_label(0, tv, sizeof(struct timeval)); } if (tz) { dfsan_set_label(0, tz, sizeof(struct timezone)); } *ret_label = 0; return ret; } SANITIZER_INTERFACE_ATTRIBUTE void *__dfsw_memchr(void *s, int c, size_t n, dfsan_label s_label, dfsan_label c_label, dfsan_label n_label, dfsan_label *ret_label) { void *ret = memchr(s, c, n); if (flags().strict_data_dependencies) { *ret_label = ret ? s_label : 0; } else { size_t len = ret ? reinterpret_cast(ret) - reinterpret_cast(s) + 1 : n; *ret_label = dfsan_union(dfsan_read_label(s, len), dfsan_union(s_label, c_label)); } return ret; } SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strrchr(char *s, int c, dfsan_label s_label, dfsan_label c_label, dfsan_label *ret_label) { char *ret = strrchr(s, c); if (flags().strict_data_dependencies) { *ret_label = ret ? s_label : 0; } else { *ret_label = dfsan_union(dfsan_read_label(s, strlen(s) + 1), dfsan_union(s_label, c_label)); } return ret; } SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strstr(char *haystack, char *needle, dfsan_label haystack_label, dfsan_label needle_label, dfsan_label *ret_label) { char *ret = strstr(haystack, needle); if (flags().strict_data_dependencies) { *ret_label = ret ? haystack_label : 0; } else { size_t len = ret ? ret + strlen(needle) - haystack : strlen(haystack) + 1; *ret_label = dfsan_union(dfsan_read_label(haystack, len), dfsan_union(dfsan_read_label(needle, strlen(needle) + 1), dfsan_union(haystack_label, needle_label))); } return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_nanosleep(const struct timespec *req, struct timespec *rem, dfsan_label req_label, dfsan_label rem_label, dfsan_label *ret_label) { int ret = nanosleep(req, rem); *ret_label = 0; if (ret == -1) { // Interrupted by a signal, rem is filled with the remaining time. dfsan_set_label(0, rem, sizeof(struct timespec)); } return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_socketpair(int domain, int type, int protocol, int sv[2], dfsan_label domain_label, dfsan_label type_label, dfsan_label protocol_label, dfsan_label sv_label, dfsan_label *ret_label) { int ret = socketpair(domain, type, protocol, sv); *ret_label = 0; if (ret == 0) { dfsan_set_label(0, sv, sizeof(*sv) * 2); } return ret; } // Type of the trampoline function passed to the custom version of // dfsan_set_write_callback. typedef void (*write_trampoline_t)( void *callback, int fd, const void *buf, ssize_t count, dfsan_label fd_label, dfsan_label buf_label, dfsan_label count_label); // Calls to dfsan_set_write_callback() set the values in this struct. // Calls to the custom version of write() read (and invoke) them. static struct { write_trampoline_t write_callback_trampoline = NULL; void *write_callback = NULL; } write_callback_info; SANITIZER_INTERFACE_ATTRIBUTE void __dfsw_dfsan_set_write_callback( write_trampoline_t write_callback_trampoline, void *write_callback, dfsan_label write_callback_label, dfsan_label *ret_label) { write_callback_info.write_callback_trampoline = write_callback_trampoline; write_callback_info.write_callback = write_callback; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_write(int fd, const void *buf, size_t count, dfsan_label fd_label, dfsan_label buf_label, dfsan_label count_label, dfsan_label *ret_label) { if (write_callback_info.write_callback != NULL) { write_callback_info.write_callback_trampoline( write_callback_info.write_callback, fd, buf, count, fd_label, buf_label, count_label); } *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 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; }; // 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. // // 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 chunks(8); size_t off = 0; while (*format) { chunks.push_back(Chunk()); Chunk& chunk = chunks.back(); chunk.ptr = str + off; chunk.arg = nullptr; int status = 0; if (*format != '%') { // 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; } 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; 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); } else { status = FORMAT_CHUNK(double); } chunk.label_type = Chunk::NUMERIC; end_format = true; break; case 'c': status = FORMAT_CHUNK(int); chunk.label_type = Chunk::NUMERIC; end_format = true; 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; break; case 'p': status = FORMAT_CHUNK(void *); chunk.label_type = Chunk::NUMERIC; end_format = true; 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; } } #undef FORMAT_CHUNK } if (status < 0) { return status; } // 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(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; } } } *ret_label = 0; // Number of bytes written in total. return off; } 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); va_end(ap); return ret; } SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_snprintf(char *str, size_t size, const char *format, dfsan_label str_label, dfsan_label size_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, true, size, format, va_labels, ret_label, ap); va_end(ap); return ret; } } Index: projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan_flags.inc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan_flags.inc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan_flags.inc (revision 279191) @@ -0,0 +1,32 @@ +//===-- dfsan_flags.inc -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// DFSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef DFSAN_FLAG +# error "Define DFSAN_FLAG prior to including this file!" +#endif + +// DFSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +DFSAN_FLAG(bool, warn_unimplemented, true, + "Whether to warn on unimplemented functions.") +DFSAN_FLAG(bool, warn_nonzero_labels, false, + "Whether to warn on unimplemented functions.") +DFSAN_FLAG( + bool, strict_data_dependencies, true, + "Whether to propagate labels only when there is an obvious data dependency" + "(e.g., when comparing strings, ignore the fact that the output of the" + "comparison might be data-dependent on the content of the strings). This" + "applies only to the custom functions defined in 'custom.c'.") +DFSAN_FLAG(const char *, dump_labels_at_exit, "", "The path of the file where " + "to dump the labels when the " + "program terminates.") Property changes on: projects/clang360-import/contrib/compiler-rt/lib/dfsan/dfsan_flags.inc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan.cc (revision 279191) @@ -1,63 +1,66 @@ //=-- lsan.cc -------------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of LeakSanitizer. // Standalone LSan RTL. // //===----------------------------------------------------------------------===// #include "lsan.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "lsan_allocator.h" #include "lsan_common.h" #include "lsan_thread.h" bool lsan_inited; bool lsan_init_is_running; namespace __lsan { ///// Interface to the common LSan module. ///// bool WordIsPoisoned(uptr addr) { return false; } } // namespace __lsan using namespace __lsan; // NOLINT extern "C" void __lsan_init() { CHECK(!lsan_init_is_running); if (lsan_inited) return; lsan_init_is_running = true; SanitizerToolName = "LeakSanitizer"; InitCommonLsan(true); InitializeAllocator(); InitTlsSize(); InitializeInterceptors(); InitializeThreadRegistry(); u32 tid = ThreadCreate(0, 0, true); CHECK_EQ(tid, 0); ThreadStart(tid, GetTid()); SetCurrentThread(tid); if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) Atexit(DoLeakCheck); + + InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); + lsan_inited = true; lsan_init_is_running = false; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_print_stack_trace() { GET_STACK_TRACE_FATAL; stack.Print(); } Index: projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_allocator.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_allocator.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_allocator.cc (revision 279191) @@ -1,240 +1,240 @@ //=-- lsan_allocator.cc ---------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of LeakSanitizer. // See lsan_allocator.h for details. // //===----------------------------------------------------------------------===// #include "lsan_allocator.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "lsan_common.h" extern "C" void *memset(void *ptr, int value, uptr num); namespace __lsan { static const uptr kMaxAllowedMallocSize = 8UL << 30; static const uptr kAllocatorSpace = 0x600000000000ULL; static const uptr kAllocatorSize = 0x40000000000ULL; // 4T. struct ChunkMetadata { bool allocated : 8; // Must be first. ChunkTag tag : 2; uptr requested_size : 54; u32 stack_trace_id; }; typedef SizeClassAllocator64 PrimaryAllocator; typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator<> SecondaryAllocator; typedef CombinedAllocator Allocator; static Allocator allocator; static THREADLOCAL AllocatorCache cache; void InitializeAllocator() { - allocator.Init(); + allocator.InitLinkerInitialized(common_flags()->allocator_may_return_null); } void AllocatorThreadFinish() { allocator.SwallowCache(&cache); } static ChunkMetadata *Metadata(const void *p) { return reinterpret_cast(allocator.GetMetaData(p)); } static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) { if (!p) return; ChunkMetadata *m = Metadata(p); CHECK(m); m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked; m->stack_trace_id = StackDepotPut(stack); m->requested_size = size; atomic_store(reinterpret_cast(m), 1, memory_order_relaxed); } static void RegisterDeallocation(void *p) { if (!p) return; ChunkMetadata *m = Metadata(p); CHECK(m); atomic_store(reinterpret_cast(m), 0, memory_order_relaxed); } void *Allocate(const StackTrace &stack, uptr size, uptr alignment, bool cleared) { if (size == 0) size = 1; if (size > kMaxAllowedMallocSize) { Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size); return 0; } void *p = allocator.Allocate(&cache, size, alignment, false); // Do not rely on the allocator to clear the memory (it's slow). if (cleared && allocator.FromPrimary(p)) memset(p, 0, size); RegisterAllocation(stack, p, size); if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size); return p; } void Deallocate(void *p) { if (&__sanitizer_free_hook) __sanitizer_free_hook(p); RegisterDeallocation(p); allocator.Deallocate(&cache, p); } void *Reallocate(const StackTrace &stack, void *p, uptr new_size, uptr alignment) { RegisterDeallocation(p); if (new_size > kMaxAllowedMallocSize) { Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size); allocator.Deallocate(&cache, p); return 0; } p = allocator.Reallocate(&cache, p, new_size, alignment); RegisterAllocation(stack, p, new_size); return p; } void GetAllocatorCacheRange(uptr *begin, uptr *end) { *begin = (uptr)&cache; *end = *begin + sizeof(cache); } uptr GetMallocUsableSize(const void *p) { ChunkMetadata *m = Metadata(p); if (!m) return 0; return m->requested_size; } ///// Interface to the common LSan module. ///// void LockAllocator() { allocator.ForceLock(); } void UnlockAllocator() { allocator.ForceUnlock(); } void GetAllocatorGlobalRange(uptr *begin, uptr *end) { *begin = (uptr)&allocator; *end = *begin + sizeof(allocator); } uptr PointsIntoChunk(void* p) { uptr addr = reinterpret_cast(p); uptr chunk = reinterpret_cast(allocator.GetBlockBeginFastLocked(p)); if (!chunk) return 0; // LargeMmapAllocator considers pointers to the meta-region of a chunk to be // valid, but we don't want that. if (addr < chunk) return 0; ChunkMetadata *m = Metadata(reinterpret_cast(chunk)); CHECK(m); if (!m->allocated) return 0; if (addr < chunk + m->requested_size) return chunk; if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr)) return chunk; return 0; } uptr GetUserBegin(uptr chunk) { return chunk; } LsanMetadata::LsanMetadata(uptr chunk) { metadata_ = Metadata(reinterpret_cast(chunk)); CHECK(metadata_); } bool LsanMetadata::allocated() const { return reinterpret_cast(metadata_)->allocated; } ChunkTag LsanMetadata::tag() const { return reinterpret_cast(metadata_)->tag; } void LsanMetadata::set_tag(ChunkTag value) { reinterpret_cast(metadata_)->tag = value; } uptr LsanMetadata::requested_size() const { return reinterpret_cast(metadata_)->requested_size; } u32 LsanMetadata::stack_trace_id() const { return reinterpret_cast(metadata_)->stack_trace_id; } void ForEachChunk(ForEachChunkCallback callback, void *arg) { allocator.ForEachChunk(callback, arg); } IgnoreObjectResult IgnoreObjectLocked(const void *p) { void *chunk = allocator.GetBlockBegin(p); if (!chunk || p < chunk) return kIgnoreObjectInvalid; ChunkMetadata *m = Metadata(chunk); CHECK(m); if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) { if (m->tag == kIgnored) return kIgnoreObjectAlreadyIgnored; m->tag = kIgnored; return kIgnoreObjectSuccess; } else { return kIgnoreObjectInvalid; } } } // namespace __lsan using namespace __lsan; extern "C" { SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_current_allocated_bytes() { uptr stats[AllocatorStatCount]; allocator.GetStats(stats); return stats[AllocatorStatAllocated]; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_heap_size() { uptr stats[AllocatorStatCount]; allocator.GetStats(stats); return stats[AllocatorStatMapped]; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_free_bytes() { return 0; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_unmapped_bytes() { return 0; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_get_ownership(const void *p) { return Metadata(p) != 0; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_allocated_size(const void *p) { return GetMallocUsableSize(p); } } // extern "C" Index: projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_common.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_common.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_common.cc (revision 279191) @@ -1,712 +1,711 @@ //=-- lsan_common.cc ------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of LeakSanitizer. // Implementation of common leak checking functionality. // //===----------------------------------------------------------------------===// #include "lsan_common.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_placement_new.h" #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" #if CAN_SANITIZE_LEAKS namespace __lsan { // This mutex is used to prevent races between DoLeakCheck and IgnoreObject, and // also to protect the global list of root regions. BlockingMutex global_mutex(LINKER_INITIALIZED); THREADLOCAL int disable_counter; bool DisabledInThisThread() { return disable_counter > 0; } Flags lsan_flags; +void Flags::SetDefaults() { +#define LSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "lsan_flags.inc" +#undef LSAN_FLAG +} + +static void RegisterLsanFlags(FlagParser *parser, Flags *f) { +#define LSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "lsan_flags.inc" +#undef LSAN_FLAG +} + static void InitializeFlags(bool standalone) { Flags *f = flags(); - // Default values. - f->report_objects = false; - f->resolution = 0; - f->max_leaks = 0; - f->exitcode = 23; - f->use_registers = true; - f->use_globals = true; - f->use_stacks = true; - f->use_tls = true; - f->use_root_regions = true; - f->use_unaligned = false; - f->use_poisoned = false; - f->log_pointers = false; - f->log_threads = false; + FlagParser parser; + RegisterLsanFlags(&parser, f); + RegisterCommonFlags(&parser); - const char *options = GetEnv("LSAN_OPTIONS"); - if (options) { - ParseFlag(options, &f->use_registers, "use_registers", ""); - ParseFlag(options, &f->use_globals, "use_globals", ""); - ParseFlag(options, &f->use_stacks, "use_stacks", ""); - ParseFlag(options, &f->use_tls, "use_tls", ""); - ParseFlag(options, &f->use_root_regions, "use_root_regions", ""); - ParseFlag(options, &f->use_unaligned, "use_unaligned", ""); - ParseFlag(options, &f->use_poisoned, "use_poisoned", ""); - ParseFlag(options, &f->report_objects, "report_objects", ""); - ParseFlag(options, &f->resolution, "resolution", ""); - CHECK_GE(&f->resolution, 0); - ParseFlag(options, &f->max_leaks, "max_leaks", ""); - CHECK_GE(&f->max_leaks, 0); - ParseFlag(options, &f->log_pointers, "log_pointers", ""); - ParseFlag(options, &f->log_threads, "log_threads", ""); - ParseFlag(options, &f->exitcode, "exitcode", ""); - } + f->SetDefaults(); // Set defaults for common flags (only in standalone mode) and parse // them from LSAN_OPTIONS. - CommonFlags *cf = common_flags(); if (standalone) { - SetCommonFlagsDefaults(cf); - cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); - cf->malloc_context_size = 30; - cf->detect_leaks = true; + SetCommonFlagsDefaults(); + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); + cf.malloc_context_size = 30; + cf.detect_leaks = true; + OverrideCommonFlags(cf); } - ParseCommonFlagsFromString(cf, options); + + bool help_before = common_flags()->help; + + const char *options = GetEnv("LSAN_OPTIONS"); + parser.ParseString(options); + + SetVerbosity(common_flags()->verbosity); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (!help_before && common_flags()->help) + parser.PrintFlagDescriptions(); } #define LOG_POINTERS(...) \ do { \ if (flags()->log_pointers) Report(__VA_ARGS__); \ } while (0); #define LOG_THREADS(...) \ do { \ if (flags()->log_threads) Report(__VA_ARGS__); \ } while (0); static bool suppressions_inited = false; void InitializeSuppressions() { CHECK(!suppressions_inited); SuppressionContext::InitIfNecessary(); if (&__lsan_default_suppressions) SuppressionContext::Get()->Parse(__lsan_default_suppressions()); suppressions_inited = true; } struct RootRegion { const void *begin; uptr size; }; InternalMmapVector *root_regions; void InitializeRootRegions() { CHECK(!root_regions); ALIGNED(64) static char placeholder[sizeof(InternalMmapVector)]; root_regions = new(placeholder) InternalMmapVector(1); } void InitCommonLsan(bool standalone) { InitializeFlags(standalone); InitializeRootRegions(); if (common_flags()->detect_leaks) { // Initialization which can fail or print warnings should only be done if // LSan is actually enabled. InitializeSuppressions(); InitializePlatformSpecificModules(); } } class Decorator: public __sanitizer::SanitizerCommonDecorator { public: Decorator() : SanitizerCommonDecorator() { } const char *Error() { return Red(); } const char *Leak() { return Blue(); } const char *End() { return Default(); } }; static inline bool CanBeAHeapPointer(uptr p) { // Since our heap is located in mmap-ed memory, we can assume a sensible lower // bound on heap addresses. const uptr kMinAddress = 4 * 4096; if (p < kMinAddress) return false; #ifdef __x86_64__ // Accept only canonical form user-space addresses. return ((p >> 47) == 0); #else return true; #endif } // 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 // (|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) { const uptr alignment = flags()->pointer_alignment(); LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end); uptr pp = begin; if (pp % alignment) pp = pp + alignment - pp % alignment; for (; pp + sizeof(void *) <= end; pp += alignment) { // NOLINT void *p = *reinterpret_cast(pp); if (!CanBeAHeapPointer(reinterpret_cast(p))) continue; uptr chunk = PointsIntoChunk(p); if (!chunk) continue; // 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; // Do this check relatively late so we can log only the interesting cases. if (!flags()->use_poisoned && WordIsPoisoned(pp)) { LOG_POINTERS( "%p is poisoned: ignoring %p pointing into chunk %p-%p of size " "%zu.\n", pp, p, chunk, chunk + m.requested_size(), m.requested_size()); continue; } m.set_tag(tag); LOG_POINTERS("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p, chunk, chunk + m.requested_size(), m.requested_size()); if (frontier) frontier->push_back(chunk); } } void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) { Frontier *frontier = reinterpret_cast(arg); ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable); } // Scans thread data (stacks and TLS) for heap pointers. static void ProcessThreads(SuspendedThreadsList const &suspended_threads, Frontier *frontier) { InternalScopedBuffer registers(SuspendedThreadsList::RegisterCount()); uptr registers_begin = reinterpret_cast(registers.data()); uptr registers_end = registers_begin + registers.size(); for (uptr i = 0; i < suspended_threads.thread_count(); i++) { uptr os_id = static_cast(suspended_threads.GetThreadID(i)); LOG_THREADS("Processing thread %d.\n", os_id); uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end; bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end, &tls_begin, &tls_end, &cache_begin, &cache_end); if (!thread_found) { // If a thread can't be found in the thread registry, it's probably in the // process of destruction. Log this event and move on. LOG_THREADS("Thread %d not found in registry.\n", os_id); continue; } uptr sp; bool have_registers = (suspended_threads.GetRegistersAndSP(i, registers.data(), &sp) == 0); if (!have_registers) { Report("Unable to get registers from thread %d.\n"); // If unable to get SP, consider the entire stack to be reachable. sp = stack_begin; } if (flags()->use_registers && have_registers) ScanRangeForPointers(registers_begin, registers_end, frontier, "REGISTERS", kReachable); if (flags()->use_stacks) { LOG_THREADS("Stack at %p-%p (SP = %p).\n", stack_begin, stack_end, sp); if (sp < stack_begin || sp >= stack_end) { // SP is outside the recorded stack range (e.g. the thread is running a // signal handler on alternate stack). Again, consider the entire stack // range to be reachable. LOG_THREADS("WARNING: stack pointer not in stack range.\n"); } else { // Shrink the stack range to ignore out-of-scope values. stack_begin = sp; } ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK", kReachable); ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier); } if (flags()->use_tls) { LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end); if (cache_begin == cache_end) { ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable); } else { // Because LSan should not be loaded with dlopen(), we can assume // that allocator cache will be part of static TLS image. CHECK_LE(tls_begin, cache_begin); CHECK_GE(tls_end, cache_end); if (tls_begin < cache_begin) ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS", kReachable); if (tls_end > cache_end) ScanRangeForPointers(cache_end, tls_end, frontier, "TLS", kReachable); } } } } static void ProcessRootRegion(Frontier *frontier, uptr root_begin, uptr root_end) { MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr begin, end, prot; while (proc_maps.Next(&begin, &end, /*offset*/ 0, /*filename*/ 0, /*filename_size*/ 0, &prot)) { uptr intersection_begin = Max(root_begin, begin); uptr intersection_end = Min(end, root_end); if (intersection_begin >= intersection_end) continue; bool is_readable = prot & MemoryMappingLayout::kProtectionRead; LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n", root_begin, root_end, begin, end, is_readable ? "readable" : "unreadable"); if (is_readable) ScanRangeForPointers(intersection_begin, intersection_end, frontier, "ROOT", kReachable); } } // Scans root regions for heap pointers. static void ProcessRootRegions(Frontier *frontier) { if (!flags()->use_root_regions) return; CHECK(root_regions); for (uptr i = 0; i < root_regions->size(); i++) { RootRegion region = (*root_regions)[i]; uptr begin_addr = reinterpret_cast(region.begin); ProcessRootRegion(frontier, begin_addr, begin_addr + region.size); } } static void FloodFillTag(Frontier *frontier, ChunkTag tag) { while (frontier->size()) { uptr next_chunk = frontier->back(); frontier->pop_back(); LsanMetadata m(next_chunk); ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier, "HEAP", tag); } } // ForEachChunk callback. If the chunk is marked as leaked, marks all chunks // which are reachable from it as indirectly leaked. static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) { chunk = GetUserBegin(chunk); LsanMetadata m(chunk); if (m.allocated() && m.tag() != kReachable) { ScanRangeForPointers(chunk, chunk + m.requested_size(), /* frontier */ 0, "HEAP", kIndirectlyLeaked); } } // ForEachChunk callback. If chunk is marked as ignored, adds its address to // frontier. static void CollectIgnoredCb(uptr chunk, void *arg) { CHECK(arg); chunk = GetUserBegin(chunk); LsanMetadata m(chunk); if (m.allocated() && m.tag() == kIgnored) reinterpret_cast(arg)->push_back(chunk); } // Sets the appropriate tag on each chunk. static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) { // Holds the flood fill frontier. Frontier frontier(1); 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"); 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 */); } static void PrintStackTraceById(u32 stack_trace_id) { CHECK(stack_trace_id); StackDepotGet(stack_trace_id).Print(); } // ForEachChunk callback. Aggregates information about unreachable chunks into // a LeakReport. static void CollectLeaksCb(uptr chunk, void *arg) { CHECK(arg); LeakReport *leak_report = reinterpret_cast(arg); chunk = GetUserBegin(chunk); LsanMetadata m(chunk); if (!m.allocated()) return; if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { - uptr resolution = flags()->resolution; + u32 resolution = flags()->resolution; u32 stack_trace_id = 0; if (resolution > 0) { StackTrace stack = StackDepotGet(m.stack_trace_id()); stack.size = Min(stack.size, resolution); stack_trace_id = StackDepotPut(stack); } else { stack_trace_id = m.stack_trace_id(); } leak_report->AddLeakedChunk(chunk, stack_trace_id, m.requested_size(), m.tag()); } } static void PrintMatchedSuppressions() { InternalMmapVector matched(1); SuppressionContext::Get()->GetMatched(&matched); if (!matched.size()) return; const char *line = "-----------------------------------------------------"; Printf("%s\n", line); Printf("Suppressions used:\n"); Printf(" count bytes template\n"); for (uptr i = 0; i < matched.size(); i++) Printf("%7zu %10zu %s\n", static_cast(matched[i]->hit_count), matched[i]->weight, matched[i]->templ); Printf("%s\n\n", line); } struct DoLeakCheckParam { bool success; LeakReport leak_report; }; static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads, void *arg) { DoLeakCheckParam *param = reinterpret_cast(arg); CHECK(param); CHECK(!param->success); ClassifyAllChunks(suspended_threads); ForEachChunk(CollectLeaksCb, ¶m->leak_report); param->success = true; } void DoLeakCheck() { EnsureMainThreadIDIsCorrect(); BlockingMutexLock l(&global_mutex); static bool already_done; if (already_done) return; already_done = true; if (&__lsan_is_turned_off && __lsan_is_turned_off()) return; DoLeakCheckParam param; param.success = false; LockThreadRegistry(); LockAllocator(); StopTheWorld(DoLeakCheckCallback, ¶m); UnlockAllocator(); UnlockThreadRegistry(); if (!param.success) { Report("LeakSanitizer has encountered a fatal error.\n"); Die(); } param.leak_report.ApplySuppressions(); uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount(); if (unsuppressed_count > 0) { Decorator d; Printf("\n" "=================================================================" "\n"); Printf("%s", d.Error()); Report("ERROR: LeakSanitizer: detected memory leaks\n"); Printf("%s", d.End()); param.leak_report.ReportTopLeaks(flags()->max_leaks); } if (common_flags()->print_suppressions) PrintMatchedSuppressions(); if (unsuppressed_count > 0) { param.leak_report.PrintSummary(); if (flags()->exitcode) { if (common_flags()->coverage) __sanitizer_cov_dump(); internal__exit(flags()->exitcode); } } } static Suppression *GetSuppressionForAddr(uptr addr) { Suppression *s = nullptr; // Suppress by module name. const char *module_name; uptr module_offset; if (Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(addr, &module_name, &module_offset) && SuppressionContext::Get()->Match(module_name, SuppressionLeak, &s)) return s; // Suppress by file or function name. SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr); for (SymbolizedStack *cur = frames; cur; cur = cur->next) { if (SuppressionContext::Get()->Match(cur->info.function, SuppressionLeak, &s) || SuppressionContext::Get()->Match(cur->info.file, SuppressionLeak, &s)) { break; } } frames->ClearAll(); return s; } static Suppression *GetSuppressionForStack(u32 stack_trace_id) { StackTrace stack = StackDepotGet(stack_trace_id); for (uptr i = 0; i < stack.size; i++) { Suppression *s = GetSuppressionForAddr( StackTrace::GetPreviousInstructionPc(stack.trace[i])); if (s) return s; } return 0; } ///// LeakReport implementation. ///// // A hard limit on the number of distinct leaks, to avoid quadratic complexity // in LeakReport::AddLeakedChunk(). We don't expect to ever see this many leaks // in real-world applications. // FIXME: Get rid of this limit by changing the implementation of LeakReport to // use a hash table. const uptr kMaxLeaksConsidered = 5000; void LeakReport::AddLeakedChunk(uptr chunk, u32 stack_trace_id, uptr leaked_size, ChunkTag tag) { CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked); bool is_directly_leaked = (tag == kDirectlyLeaked); uptr i; for (i = 0; i < leaks_.size(); i++) { if (leaks_[i].stack_trace_id == stack_trace_id && leaks_[i].is_directly_leaked == is_directly_leaked) { leaks_[i].hit_count++; leaks_[i].total_size += leaked_size; break; } } if (i == leaks_.size()) { if (leaks_.size() == kMaxLeaksConsidered) return; Leak leak = { next_id_++, /* hit_count */ 1, leaked_size, stack_trace_id, is_directly_leaked, /* is_suppressed */ false }; leaks_.push_back(leak); } if (flags()->report_objects) { LeakedObject obj = {leaks_[i].id, chunk, leaked_size}; leaked_objects_.push_back(obj); } } static bool LeakComparator(const Leak &leak1, const Leak &leak2) { if (leak1.is_directly_leaked == leak2.is_directly_leaked) return leak1.total_size > leak2.total_size; else return leak1.is_directly_leaked; } void LeakReport::ReportTopLeaks(uptr num_leaks_to_report) { CHECK(leaks_.size() <= kMaxLeaksConsidered); Printf("\n"); if (leaks_.size() == kMaxLeaksConsidered) Printf("Too many leaks! Only the first %zu leaks encountered will be " "reported.\n", kMaxLeaksConsidered); uptr unsuppressed_count = UnsuppressedLeakCount(); if (num_leaks_to_report > 0 && num_leaks_to_report < unsuppressed_count) Printf("The %zu top leak(s):\n", num_leaks_to_report); InternalSort(&leaks_, leaks_.size(), LeakComparator); uptr leaks_reported = 0; for (uptr i = 0; i < leaks_.size(); i++) { if (leaks_[i].is_suppressed) continue; PrintReportForLeak(i); leaks_reported++; if (leaks_reported == num_leaks_to_report) break; } if (leaks_reported < unsuppressed_count) { uptr remaining = unsuppressed_count - leaks_reported; Printf("Omitting %zu more leak(s).\n", remaining); } } void LeakReport::PrintReportForLeak(uptr index) { Decorator d; Printf("%s", d.Leak()); Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n", leaks_[index].is_directly_leaked ? "Direct" : "Indirect", leaks_[index].total_size, leaks_[index].hit_count); Printf("%s", d.End()); PrintStackTraceById(leaks_[index].stack_trace_id); if (flags()->report_objects) { Printf("Objects leaked above:\n"); PrintLeakedObjectsForLeak(index); Printf("\n"); } } void LeakReport::PrintLeakedObjectsForLeak(uptr index) { u32 leak_id = leaks_[index].id; for (uptr j = 0; j < leaked_objects_.size(); j++) { if (leaked_objects_[j].leak_id == leak_id) Printf("%p (%zu bytes)\n", leaked_objects_[j].addr, leaked_objects_[j].size); } } void LeakReport::PrintSummary() { CHECK(leaks_.size() <= kMaxLeaksConsidered); uptr bytes = 0, allocations = 0; for (uptr i = 0; i < leaks_.size(); i++) { if (leaks_[i].is_suppressed) continue; bytes += leaks_[i].total_size; allocations += leaks_[i].hit_count; } InternalScopedString summary(kMaxSummaryLength); summary.append("%zu byte(s) leaked in %zu allocation(s).", bytes, allocations); ReportErrorSummary(summary.data()); } void LeakReport::ApplySuppressions() { for (uptr i = 0; i < leaks_.size(); i++) { Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id); if (s) { s->weight += leaks_[i].total_size; s->hit_count += leaks_[i].hit_count; leaks_[i].is_suppressed = true; } } } uptr LeakReport::UnsuppressedLeakCount() { uptr result = 0; for (uptr i = 0; i < leaks_.size(); i++) if (!leaks_[i].is_suppressed) result++; return result; } } // namespace __lsan #endif // CAN_SANITIZE_LEAKS using namespace __lsan; // NOLINT extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __lsan_ignore_object(const void *p) { #if CAN_SANITIZE_LEAKS if (!common_flags()->detect_leaks) return; // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not // locked. BlockingMutexLock l(&global_mutex); IgnoreObjectResult res = IgnoreObjectLocked(p); if (res == kIgnoreObjectInvalid) VReport(1, "__lsan_ignore_object(): no heap object found at %p", p); if (res == kIgnoreObjectAlreadyIgnored) VReport(1, "__lsan_ignore_object(): " "heap object at %p is already being ignored\n", p); if (res == kIgnoreObjectSuccess) VReport(1, "__lsan_ignore_object(): ignoring heap object at %p\n", p); #endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE void __lsan_register_root_region(const void *begin, uptr size) { #if CAN_SANITIZE_LEAKS BlockingMutexLock l(&global_mutex); CHECK(root_regions); RootRegion region = {begin, size}; root_regions->push_back(region); VReport(1, "Registered root region at %p of size %llu\n", begin, size); #endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE void __lsan_unregister_root_region(const void *begin, uptr size) { #if CAN_SANITIZE_LEAKS BlockingMutexLock l(&global_mutex); CHECK(root_regions); bool removed = false; for (uptr i = 0; i < root_regions->size(); i++) { RootRegion region = (*root_regions)[i]; if (region.begin == begin && region.size == size) { removed = true; uptr last_index = root_regions->size() - 1; (*root_regions)[i] = (*root_regions)[last_index]; root_regions->pop_back(); VReport(1, "Unregistered root region at %p of size %llu\n", begin, size); break; } } if (!removed) { Report( "__lsan_unregister_root_region(): region at %p of size %llu has not " "been registered.\n", begin, size); Die(); } #endif // CAN_SANITIZE_LEAKS } SANITIZER_INTERFACE_ATTRIBUTE void __lsan_disable() { #if CAN_SANITIZE_LEAKS __lsan::disable_counter++; #endif } SANITIZER_INTERFACE_ATTRIBUTE void __lsan_enable() { #if CAN_SANITIZE_LEAKS if (!__lsan::disable_counter && common_flags()->detect_leaks) { Report("Unmatched call to __lsan_enable().\n"); Die(); } __lsan::disable_counter--; #endif } SANITIZER_INTERFACE_ATTRIBUTE void __lsan_do_leak_check() { #if CAN_SANITIZE_LEAKS if (common_flags()->detect_leaks) __lsan::DoLeakCheck(); #endif // CAN_SANITIZE_LEAKS } #if !SANITIZER_SUPPORTS_WEAK_HOOKS SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int __lsan_is_turned_off() { return 0; } #endif } // extern "C" Index: projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_common.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_common.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_common.h (revision 279191) @@ -1,203 +1,177 @@ //=-- lsan_common.h -------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of LeakSanitizer. // Private LSan header. // //===----------------------------------------------------------------------===// #ifndef LSAN_COMMON_H #define LSAN_COMMON_H #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_platform.h" #include "sanitizer_common/sanitizer_symbolizer.h" #if SANITIZER_LINUX && defined(__x86_64__) && (SANITIZER_WORDSIZE == 64) #define CAN_SANITIZE_LEAKS 1 #else #define CAN_SANITIZE_LEAKS 0 #endif namespace __lsan { // Chunk tags. enum ChunkTag { kDirectlyLeaked = 0, // default kIndirectlyLeaked = 1, kReachable = 2, kIgnored = 3 }; struct Flags { +#define LSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "lsan_flags.inc" +#undef LSAN_FLAG + + void SetDefaults(); uptr pointer_alignment() const { return use_unaligned ? 1 : sizeof(uptr); } - - // Print addresses of leaked objects after main leak report. - bool report_objects; - // Aggregate two objects into one leak if this many stack frames match. If - // zero, the entire stack trace must match. - int resolution; - // The number of leaks reported. - int max_leaks; - // If nonzero kill the process with this exit code upon finding leaks. - int exitcode; - - // Flags controlling the root set of reachable memory. - // Global variables (.data and .bss). - bool use_globals; - // Thread stacks. - bool use_stacks; - // Thread registers. - bool use_registers; - // TLS and thread-specific storage. - bool use_tls; - // Regions added via __lsan_register_root_region(). - bool use_root_regions; - - // Consider unaligned pointers valid. - bool use_unaligned; - // Consider pointers found in poisoned memory to be valid. - bool use_poisoned; - - // Debug logging. - bool log_pointers; - bool log_threads; }; extern Flags lsan_flags; inline Flags *flags() { return &lsan_flags; } struct Leak { u32 id; uptr hit_count; uptr total_size; u32 stack_trace_id; bool is_directly_leaked; bool is_suppressed; }; struct LeakedObject { u32 leak_id; uptr addr; uptr size; }; // Aggregates leaks by stack trace prefix. class LeakReport { public: LeakReport() : next_id_(0), leaks_(1), leaked_objects_(1) {} void AddLeakedChunk(uptr chunk, u32 stack_trace_id, uptr leaked_size, ChunkTag tag); void ReportTopLeaks(uptr max_leaks); void PrintSummary(); void ApplySuppressions(); uptr UnsuppressedLeakCount(); private: void PrintReportForLeak(uptr index); void PrintLeakedObjectsForLeak(uptr index); u32 next_id_; InternalMmapVector leaks_; InternalMmapVector leaked_objects_; }; typedef InternalMmapVector Frontier; // Platform-specific functions. void InitializePlatformSpecificModules(); void ProcessGlobalRegions(Frontier *frontier); void ProcessPlatformSpecificAllocations(Frontier *frontier); void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, const char *region_type, ChunkTag tag); enum IgnoreObjectResult { kIgnoreObjectSuccess, kIgnoreObjectAlreadyIgnored, kIgnoreObjectInvalid }; // Functions called from the parent tool. void InitCommonLsan(bool standalone); void DoLeakCheck(); bool DisabledInThisThread(); // Special case for "new T[0]" where T is a type with DTOR. // new T[0] will allocate one word for the array size (0) and store a pointer // to the end of allocated chunk. inline bool IsSpecialCaseOfOperatorNew0(uptr chunk_beg, uptr chunk_size, uptr addr) { return chunk_size == sizeof(uptr) && chunk_beg + chunk_size == addr && *reinterpret_cast(chunk_beg) == 0; } // The following must be implemented in the parent tool. void ForEachChunk(ForEachChunkCallback callback, void *arg); // Returns the address range occupied by the global allocator object. void GetAllocatorGlobalRange(uptr *begin, uptr *end); // Wrappers for allocator's ForceLock()/ForceUnlock(). void LockAllocator(); void UnlockAllocator(); // Returns true if [addr, addr + sizeof(void *)) is poisoned. bool WordIsPoisoned(uptr addr); // Wrappers for ThreadRegistry access. void LockThreadRegistry(); void UnlockThreadRegistry(); bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end); void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, void *arg); // If called from the main thread, updates the main thread's TID in the thread // registry. We need this to handle processes that fork() without a subsequent // exec(), which invalidates the recorded TID. To update it, we must call // gettid() from the main thread. Our solution is to call this function before // leak checking and also before every call to pthread_create() (to handle cases // where leak checking is initiated from a non-main thread). void EnsureMainThreadIDIsCorrect(); // If p points into a chunk that has been allocated to the user, returns its // user-visible address. Otherwise, returns 0. uptr PointsIntoChunk(void *p); // Returns address of user-visible chunk contained in this allocator chunk. uptr GetUserBegin(uptr chunk); // Helper for __lsan_ignore_object(). IgnoreObjectResult IgnoreObjectLocked(const void *p); // Wrapper for chunk metadata operations. class LsanMetadata { public: // Constructor accepts address of user-visible chunk. explicit LsanMetadata(uptr chunk); bool allocated() const; ChunkTag tag() const; void set_tag(ChunkTag value); uptr requested_size() const; u32 stack_trace_id() const; private: void *metadata_; }; } // namespace __lsan extern "C" { SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int __lsan_is_turned_off(); SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *__lsan_default_suppressions(); } // extern "C" #endif // LSAN_COMMON_H Index: projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_flags.inc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_flags.inc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_flags.inc (revision 279191) @@ -0,0 +1,44 @@ +//===-- lsan_flags.inc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// LSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef LSAN_FLAG +# error "Define LSAN_FLAG prior to including this file!" +#endif + +// LSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +LSAN_FLAG(bool, report_objects, false, + "Print addresses of leaked objects after main leak report.") +LSAN_FLAG( + int, resolution, 0, + "Aggregate two objects into one leak if this many stack frames match. If " + "zero, the entire stack trace must match.") +LSAN_FLAG(int, max_leaks, 0, "The number of leaks reported.") +LSAN_FLAG(int, exitcode, 23, + "If nonzero kill the process with this exit code upon finding leaks.") + +// Flags controlling the root set of reachable memory. +LSAN_FLAG(bool, use_globals, true, + "Root set: include global variables (.data and .bss)") +LSAN_FLAG(bool, use_stacks, true, "Root set: include thread stacks") +LSAN_FLAG(bool, use_registers, true, "Root set: include thread registers") +LSAN_FLAG(bool, use_tls, true, + "Root set: include TLS and thread-specific storage") +LSAN_FLAG(bool, use_root_regions, true, + "Root set: include regions added via __lsan_register_root_region().") + +LSAN_FLAG(bool, use_unaligned, false, "Consider unaligned pointers valid.") +LSAN_FLAG(bool, use_poisoned, false, + "Consider pointers found in poisoned memory to be valid.") +LSAN_FLAG(bool, log_pointers, false, "Debug logging") +LSAN_FLAG(bool, log_threads, false, "Debug logging") Property changes on: projects/clang360-import/contrib/compiler-rt/lib/lsan/lsan_flags.inc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan.cc (revision 279191) @@ -1,616 +1,610 @@ //===-- msan.cc -----------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // MemorySanitizer runtime. //===----------------------------------------------------------------------===// #include "msan.h" #include "msan_chained_origin_depot.h" #include "msan_origin.h" #include "msan_thread.h" +#include "msan_poisoning.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "sanitizer_common/sanitizer_stackdepot.h" - // ACHTUNG! No system header includes in this file. using namespace __sanitizer; // Globals. static THREADLOCAL int msan_expect_umr = 0; static THREADLOCAL int msan_expected_umr_found = 0; // Function argument shadow. Each argument starts at the next available 8-byte // aligned address. SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u64 __msan_param_tls[kMsanParamTlsSize / sizeof(u64)]; // Function argument origin. Each argument starts at the same offset as the // corresponding shadow in (__msan_param_tls). Slightly weird, but changing this // would break compatibility with older prebuilt binaries. SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u32 __msan_param_origin_tls[kMsanParamTlsSize / sizeof(u32)]; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u64 __msan_retval_tls[kMsanRetvalTlsSize / sizeof(u64)]; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u32 __msan_retval_origin_tls; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSize / sizeof(u64)]; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u64 __msan_va_arg_overflow_size_tls; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u32 __msan_origin_tls; static THREADLOCAL int is_in_symbolizer; extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_track_origins; int __msan_get_track_origins() { return &__msan_track_origins ? __msan_track_origins : 0; } extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_keep_going; namespace __msan { void EnterSymbolizer() { ++is_in_symbolizer; } void ExitSymbolizer() { --is_in_symbolizer; } bool IsInSymbolizer() { return is_in_symbolizer; } static Flags msan_flags; Flags *flags() { return &msan_flags; } int msan_inited = 0; bool msan_init_is_running; int msan_report_count = 0; void (*death_callback)(void); // Array of stack origins. // FIXME: make it resizable. static const uptr kNumStackOriginDescrs = 1024 * 1024; static const char *StackOriginDescr[kNumStackOriginDescrs]; static uptr StackOriginPC[kNumStackOriginDescrs]; static atomic_uint32_t NumStackOriginDescrs; -static void ParseFlagsFromString(Flags *f, const char *str) { - CommonFlags *cf = common_flags(); - ParseCommonFlagsFromString(cf, str); - ParseFlag(str, &f->poison_heap_with_zeroes, "poison_heap_with_zeroes", ""); - ParseFlag(str, &f->poison_stack_with_zeroes, "poison_stack_with_zeroes", ""); - ParseFlag(str, &f->poison_in_malloc, "poison_in_malloc", ""); - ParseFlag(str, &f->poison_in_free, "poison_in_free", ""); - ParseFlag(str, &f->exit_code, "exit_code", ""); +void Flags::SetDefaults() { +#define MSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "msan_flags.inc" +#undef MSAN_FLAG +} + +// keep_going is an old name for halt_on_error, +// and it has inverse meaning. +class FlagHandlerKeepGoing : public FlagHandlerBase { + bool *halt_on_error_; + + public: + explicit FlagHandlerKeepGoing(bool *halt_on_error) + : halt_on_error_(halt_on_error) {} + bool Parse(const char *value) { + bool tmp; + FlagHandler h(&tmp); + if (!h.Parse(value)) return false; + *halt_on_error_ = !tmp; + return true; + } +}; + +void RegisterMsanFlags(FlagParser *parser, Flags *f) { +#define MSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "msan_flags.inc" +#undef MSAN_FLAG + + FlagHandlerKeepGoing *fh_keep_going = new (FlagParser::Alloc) // NOLINT + FlagHandlerKeepGoing(&f->halt_on_error); + parser->RegisterHandler("keep_going", fh_keep_going, + "deprecated, use halt_on_error"); +} + +static void InitializeFlags(Flags *f, const char *options) { + FlagParser parser; + RegisterMsanFlags(&parser, f); + RegisterCommonFlags(&parser); + + SetCommonFlagsDefaults(); + { + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.external_symbolizer_path = GetEnv("MSAN_SYMBOLIZER_PATH"); + cf.malloc_context_size = 20; + cf.handle_ioctl = true; + // FIXME: test and enable. + cf.check_printf = false; + cf.intercept_tls_get_addr = true; + OverrideCommonFlags(cf); + } + + f->SetDefaults(); + + // Override from user-specified string. + if (__msan_default_options) + parser.ParseString(__msan_default_options()); + + parser.ParseString(options); + + SetVerbosity(common_flags()->verbosity); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) parser.PrintFlagDescriptions(); + + // Check flag values: if (f->exit_code < 0 || f->exit_code > 127) { Printf("Exit code not in [0, 128) range: %d\n", f->exit_code); Die(); } - ParseFlag(str, &f->origin_history_size, "origin_history_size", ""); if (f->origin_history_size < 0 || f->origin_history_size > Origin::kMaxDepth) { Printf( "Origin history size invalid: %d. Must be 0 (unlimited) or in [1, %d] " "range.\n", f->origin_history_size, Origin::kMaxDepth); Die(); } - ParseFlag(str, &f->origin_history_per_stack_limit, - "origin_history_per_stack_limit", ""); // Limiting to kStackDepotMaxUseCount / 2 to avoid overflow in // StackDepotHandle::inc_use_count_unsafe. if (f->origin_history_per_stack_limit < 0 || f->origin_history_per_stack_limit > kStackDepotMaxUseCount / 2) { Printf( "Origin per-stack limit invalid: %d. Must be 0 (unlimited) or in [1, " "%d] range.\n", f->origin_history_per_stack_limit, kStackDepotMaxUseCount / 2); Die(); } - - ParseFlag(str, &f->report_umrs, "report_umrs", ""); - ParseFlag(str, &f->wrap_signals, "wrap_signals", ""); - ParseFlag(str, &f->print_stats, "print_stats", ""); - ParseFlag(str, &f->atexit, "atexit", ""); - ParseFlag(str, &f->store_context_size, "store_context_size", ""); if (f->store_context_size < 1) f->store_context_size = 1; - - // keep_going is an old name for halt_on_error, - // and it has inverse meaning. - f->halt_on_error = !f->halt_on_error; - ParseFlag(str, &f->halt_on_error, "keep_going", ""); - f->halt_on_error = !f->halt_on_error; - ParseFlag(str, &f->halt_on_error, "halt_on_error", ""); } -static void InitializeFlags(Flags *f, const char *options) { - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - cf->external_symbolizer_path = GetEnv("MSAN_SYMBOLIZER_PATH"); - cf->malloc_context_size = 20; - cf->handle_ioctl = true; - // FIXME: test and enable. - cf->check_printf = false; - cf->intercept_tls_get_addr = true; - - internal_memset(f, 0, sizeof(*f)); - f->poison_heap_with_zeroes = false; - f->poison_stack_with_zeroes = false; - f->poison_in_malloc = true; - f->poison_in_free = true; - f->exit_code = 77; - f->origin_history_size = Origin::kMaxDepth; - f->origin_history_per_stack_limit = 20000; - f->report_umrs = true; - f->wrap_signals = true; - f->print_stats = false; - f->atexit = false; - f->halt_on_error = !&__msan_keep_going; - f->store_context_size = 20; - - // Override from user-specified string. - if (__msan_default_options) - ParseFlagsFromString(f, __msan_default_options()); - ParseFlagsFromString(f, options); -} - void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp, bool request_fast_unwind) { MsanThread *t = GetCurrentThread(); if (!t || !StackTrace::WillUseFastUnwind(request_fast_unwind)) { // Block reports from our interceptors during _Unwind_Backtrace. SymbolizerScope sym_scope; return stack->Unwind(max_s, pc, bp, 0, 0, 0, request_fast_unwind); } stack->Unwind(max_s, pc, bp, 0, t->stack_top(), t->stack_bottom(), request_fast_unwind); } void PrintWarning(uptr pc, uptr bp) { PrintWarningWithOrigin(pc, bp, __msan_origin_tls); } void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin) { if (msan_expect_umr) { // Printf("Expected UMR\n"); __msan_origin_tls = origin; msan_expected_umr_found = 1; return; } ++msan_report_count; GET_FATAL_STACK_TRACE_PC_BP(pc, bp); u32 report_origin = (__msan_get_track_origins() && Origin::isValidId(origin)) ? origin : 0; ReportUMR(&stack, report_origin); if (__msan_get_track_origins() && !Origin::isValidId(origin)) { Printf( " ORIGIN: invalid (%x). Might be a bug in MemorySanitizer origin " "tracking.\n This could still be a bug in your code, too!\n", origin); } } void UnpoisonParam(uptr n) { internal_memset(__msan_param_tls, 0, n * sizeof(*__msan_param_tls)); } // Backup MSan runtime TLS state. // Implementation must be async-signal-safe. // Instances of this class may live on the signal handler stack, and data size // may be an issue. void ScopedThreadLocalStateBackup::Backup() { va_arg_overflow_size_tls = __msan_va_arg_overflow_size_tls; } void ScopedThreadLocalStateBackup::Restore() { // A lame implementation that only keeps essential state and resets the rest. __msan_va_arg_overflow_size_tls = va_arg_overflow_size_tls; internal_memset(__msan_param_tls, 0, sizeof(__msan_param_tls)); internal_memset(__msan_retval_tls, 0, sizeof(__msan_retval_tls)); internal_memset(__msan_va_arg_tls, 0, sizeof(__msan_va_arg_tls)); if (__msan_get_track_origins()) { internal_memset(&__msan_retval_origin_tls, 0, sizeof(__msan_retval_origin_tls)); internal_memset(__msan_param_origin_tls, 0, sizeof(__msan_param_origin_tls)); } } void UnpoisonThreadLocalState() { } const char *GetStackOriginDescr(u32 id, uptr *pc) { CHECK_LT(id, kNumStackOriginDescrs); if (pc) *pc = StackOriginPC[id]; return StackOriginDescr[id]; } u32 ChainOrigin(u32 id, StackTrace *stack) { MsanThread *t = GetCurrentThread(); if (t && t->InSignalHandler()) return id; Origin o = Origin::FromRawId(id); + stack->tag = StackTrace::TAG_UNKNOWN; Origin chained = Origin::CreateChainedOrigin(o, stack); return chained.raw_id(); } } // namespace __msan // Interface. using namespace __msan; #define MSAN_MAYBE_WARNING(type, size) \ void __msan_maybe_warning_##size(type s, u32 o) { \ GET_CALLER_PC_BP_SP; \ (void) sp; \ if (UNLIKELY(s)) { \ PrintWarningWithOrigin(pc, bp, o); \ if (__msan::flags()->halt_on_error) { \ Printf("Exiting\n"); \ Die(); \ } \ } \ } MSAN_MAYBE_WARNING(u8, 1) MSAN_MAYBE_WARNING(u16, 2) MSAN_MAYBE_WARNING(u32, 4) MSAN_MAYBE_WARNING(u64, 8) #define MSAN_MAYBE_STORE_ORIGIN(type, size) \ void __msan_maybe_store_origin_##size(type s, void *p, u32 o) { \ if (UNLIKELY(s)) { \ if (__msan_get_track_origins() > 1) { \ GET_CALLER_PC_BP_SP; \ (void) sp; \ GET_STORE_STACK_TRACE_PC_BP(pc, bp); \ o = ChainOrigin(o, &stack); \ } \ *(u32 *)MEM_TO_ORIGIN((uptr)p & ~3UL) = o; \ } \ } MSAN_MAYBE_STORE_ORIGIN(u8, 1) MSAN_MAYBE_STORE_ORIGIN(u16, 2) MSAN_MAYBE_STORE_ORIGIN(u32, 4) MSAN_MAYBE_STORE_ORIGIN(u64, 8) void __msan_warning() { GET_CALLER_PC_BP_SP; (void)sp; PrintWarning(pc, bp); if (__msan::flags()->halt_on_error) { if (__msan::flags()->print_stats) ReportStats(); Printf("Exiting\n"); Die(); } } void __msan_warning_noreturn() { GET_CALLER_PC_BP_SP; (void)sp; PrintWarning(pc, bp); if (__msan::flags()->print_stats) ReportStats(); Printf("Exiting\n"); Die(); } void __msan_init() { CHECK(!msan_init_is_running); if (msan_inited) return; msan_init_is_running = 1; SanitizerToolName = "MemorySanitizer"; SetDieCallback(MsanDie); InitTlsSize(); const char *msan_options = GetEnv("MSAN_OPTIONS"); InitializeFlags(&msan_flags, msan_options); - if (common_flags()->help) PrintFlagDescriptions(); __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"); // A reasonably large stack size. It is bigger than the usual 8Mb, because, // well, the program could have been run with unlimited stack for a reason. SetStackSizeLimitInBytes(32 * 1024 * 1024); ReExec(); } VPrintf(1, "MSAN_OPTIONS: %s\n", msan_options ? msan_options : ""); __msan_clear_on_return(); if (__msan_get_track_origins()) VPrintf(1, "msan_track_origins\n"); if (!InitShadow(/* map_shadow */ true, __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"); Printf("FATAL: If running under GDB, try " "'set disable-randomization off'.\n"); DumpProcessMap(); Die(); } Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer); - if (common_flags()->coverage) { - __sanitizer_cov_init(); - Atexit(__sanitizer_cov_dump); - } + InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); MsanTSDInit(MsanTSDDtor); MsanThread *main_thread = MsanThread::Create(0, 0); SetCurrentThread(main_thread); main_thread->ThreadStart(); VPrintf(1, "MemorySanitizer init done\n"); msan_init_is_running = 0; msan_inited = 1; } void __msan_set_exit_code(int exit_code) { flags()->exit_code = exit_code; } void __msan_set_keep_going(int keep_going) { flags()->halt_on_error = !keep_going; } void __msan_set_expect_umr(int expect_umr) { if (expect_umr) { msan_expected_umr_found = 0; } else if (!msan_expected_umr_found) { GET_CALLER_PC_BP_SP; (void)sp; GET_FATAL_STACK_TRACE_PC_BP(pc, bp); ReportExpectedUMRNotFound(&stack); Die(); } msan_expect_umr = expect_umr; } void __msan_print_shadow(const void *x, uptr size) { if (!MEM_IS_APP(x)) { Printf("Not a valid application address: %p\n", x); return; } DescribeMemoryRange(x, size); } void __msan_dump_shadow(const void *x, uptr size) { if (!MEM_IS_APP(x)) { Printf("Not a valid application address: %p\n", x); return; } unsigned char *s = (unsigned char*)MEM_TO_SHADOW(x); for (uptr i = 0; i < size; i++) { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ Printf("%x%x ", s[i] & 0xf, s[i] >> 4); #else Printf("%x%x ", s[i] >> 4, s[i] & 0xf); #endif } Printf("\n"); } sptr __msan_test_shadow(const void *x, uptr size) { if (!MEM_IS_APP(x)) return -1; unsigned char *s = (unsigned char *)MEM_TO_SHADOW((uptr)x); for (uptr i = 0; i < size; ++i) if (s[i]) return i; return -1; } void __msan_check_mem_is_initialized(const void *x, uptr size) { if (!__msan::flags()->report_umrs) return; sptr offset = __msan_test_shadow(x, size); if (offset < 0) return; GET_CALLER_PC_BP_SP; (void)sp; ReportUMRInsideAddressRange(__func__, x, size, offset); __msan::PrintWarningWithOrigin(pc, bp, __msan_get_origin(((const char *)x) + offset)); if (__msan::flags()->halt_on_error) { Printf("Exiting\n"); Die(); } } int __msan_set_poison_in_malloc(int do_poison) { int old = flags()->poison_in_malloc; flags()->poison_in_malloc = do_poison; return old; } int __msan_has_dynamic_component() { return false; } NOINLINE void __msan_clear_on_return() { __msan_param_tls[0] = 0; } void __msan_partial_poison(const void* data, void* shadow, uptr size) { internal_memcpy((void*)MEM_TO_SHADOW((uptr)data), shadow, size); } void __msan_load_unpoisoned(void *src, uptr size, void *dst) { internal_memcpy(dst, src, size); __msan_unpoison(dst, size); } void __msan_set_origin(const void *a, uptr size, u32 origin) { - // Origin mapping is 4 bytes per 4 bytes of application memory. - // Here we extend the range such that its left and right bounds are both - // 4 byte aligned. - if (!__msan_get_track_origins()) return; - uptr x = MEM_TO_ORIGIN((uptr)a); - uptr beg = x & ~3UL; // align down. - uptr end = (x + size + 3) & ~3UL; // align up. - u64 origin64 = ((u64)origin << 32) | origin; - // This is like memset, but the value is 32-bit. We unroll by 2 to write - // 64 bits at once. May want to unroll further to get 128-bit stores. - if (beg & 7ULL) { - *(u32*)beg = origin; - beg += 4; - } - for (uptr addr = beg; addr < (end & ~7UL); addr += 8) - *(u64*)addr = origin64; - if (end & 7ULL) - *(u32*)(end - 4) = origin; + if (__msan_get_track_origins()) SetOrigin(a, size, origin); } // 'descr' is created at compile time and contains '----' in the beginning. // When we see descr for the first time we replace '----' with a uniq id // and set the origin to (id | (31-th bit)). void __msan_set_alloca_origin(void *a, uptr size, char *descr) { __msan_set_alloca_origin4(a, size, descr, 0); } void __msan_set_alloca_origin4(void *a, uptr size, char *descr, uptr pc) { static const u32 dash = '-'; static const u32 first_timer = dash + (dash << 8) + (dash << 16) + (dash << 24); u32 *id_ptr = (u32*)descr; bool print = false; // internal_strstr(descr + 4, "AllocaTOTest") != 0; u32 id = *id_ptr; if (id == first_timer) { u32 idx = atomic_fetch_add(&NumStackOriginDescrs, 1, memory_order_relaxed); CHECK_LT(idx, kNumStackOriginDescrs); StackOriginDescr[idx] = descr + 4; StackOriginPC[idx] = pc; id = Origin::CreateStackOrigin(idx).raw_id(); *id_ptr = id; if (print) Printf("First time: idx=%d id=%d %s %p \n", idx, id, descr + 4, pc); } if (print) Printf("__msan_set_alloca_origin: descr=%s id=%x\n", descr + 4, id); __msan_set_origin(a, size, id); } u32 __msan_chain_origin(u32 id) { GET_CALLER_PC_BP_SP; (void)sp; GET_STORE_STACK_TRACE_PC_BP(pc, bp); return ChainOrigin(id, &stack); } u32 __msan_get_origin(const void *a) { if (!__msan_get_track_origins()) return 0; uptr x = (uptr)a; uptr aligned = x & ~3ULL; uptr origin_ptr = MEM_TO_ORIGIN(aligned); return *(u32*)origin_ptr; } u32 __msan_get_umr_origin() { return __msan_origin_tls; } u16 __sanitizer_unaligned_load16(const uu16 *p) { __msan_retval_tls[0] = *(uu16 *)MEM_TO_SHADOW((uptr)p); if (__msan_get_track_origins()) __msan_retval_origin_tls = GetOriginIfPoisoned((uptr)p, sizeof(*p)); return *p; } u32 __sanitizer_unaligned_load32(const uu32 *p) { __msan_retval_tls[0] = *(uu32 *)MEM_TO_SHADOW((uptr)p); if (__msan_get_track_origins()) __msan_retval_origin_tls = GetOriginIfPoisoned((uptr)p, sizeof(*p)); return *p; } u64 __sanitizer_unaligned_load64(const uu64 *p) { __msan_retval_tls[0] = *(uu64 *)MEM_TO_SHADOW((uptr)p); if (__msan_get_track_origins()) __msan_retval_origin_tls = GetOriginIfPoisoned((uptr)p, sizeof(*p)); return *p; } void __sanitizer_unaligned_store16(uu16 *p, u16 x) { u16 s = __msan_param_tls[1]; *(uu16 *)MEM_TO_SHADOW((uptr)p) = s; if (s && __msan_get_track_origins()) if (uu32 o = __msan_param_origin_tls[2]) SetOriginIfPoisoned((uptr)p, (uptr)&s, sizeof(s), o); *p = x; } void __sanitizer_unaligned_store32(uu32 *p, u32 x) { u32 s = __msan_param_tls[1]; *(uu32 *)MEM_TO_SHADOW((uptr)p) = s; if (s && __msan_get_track_origins()) if (uu32 o = __msan_param_origin_tls[2]) SetOriginIfPoisoned((uptr)p, (uptr)&s, sizeof(s), o); *p = x; } void __sanitizer_unaligned_store64(uu64 *p, u64 x) { u64 s = __msan_param_tls[1]; *(uu64 *)MEM_TO_SHADOW((uptr)p) = s; if (s && __msan_get_track_origins()) if (uu32 o = __msan_param_origin_tls[2]) SetOriginIfPoisoned((uptr)p, (uptr)&s, sizeof(s), o); *p = x; } void __msan_set_death_callback(void (*callback)(void)) { death_callback = callback; } #if !SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char* __msan_default_options() { return ""; } } // extern "C" #endif extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_print_stack_trace() { GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME()); stack.Print(); } } // extern "C" Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan.h (revision 279191) @@ -1,221 +1,218 @@ //===-- msan.h --------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // Private MSan header. //===----------------------------------------------------------------------===// #ifndef MSAN_H #define MSAN_H #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "msan_interface_internal.h" #include "msan_flags.h" #ifndef MSAN_REPLACE_OPERATORS_NEW_AND_DELETE # define MSAN_REPLACE_OPERATORS_NEW_AND_DELETE 1 #endif -/* -C/C++ on FreeBSD -0000 0000 0000 - 00ff ffff ffff: Low memory: main binary, MAP_32BIT mappings and modules -0100 0000 0000 - 0fff ffff ffff: Bad1 -1000 0000 0000 - 30ff ffff ffff: Shadow -3100 0000 0000 - 37ff ffff ffff: Bad2 -3800 0000 0000 - 58ff ffff ffff: Origins -5900 0000 0000 - 5fff ffff ffff: Bad3 -6000 0000 0000 - 7fff ffff ffff: High memory: heap, modules and main thread stack +struct MappingDesc { + uptr start; + uptr end; + enum Type { + INVALID, APP, SHADOW, ORIGIN + } type; + const char *name; +}; -C/C++ on Linux/PIE -0000 0000 0000 - 1fff ffff ffff: Bad1 -2000 0000 0000 - 3fff ffff ffff: Shadow -4000 0000 0000 - 5fff ffff ffff: Origins -6000 0000 0000 - 7fff ffff ffff: Main memory -C/C++ on Mips -0000 0000 0000 - 009f ffff ffff: Bad1 -00a0 0000 0000 - 00bf ffff ffff: Shadow -00c0 0000 0000 - 00df ffff ffff: Origins -00e0 0000 0000 - 00ff ffff ffff: Main memory -*/ - #if SANITIZER_LINUX && defined(__mips64) -const uptr kLowMemBeg = 0; -const uptr kLowMemSize = 0; -const uptr kHighMemBeg = 0x00e000000000; -const uptr kHighMemSize = 0x002000000000; -const uptr kShadowBeg = 0x00a000000000; -const uptr kShadowSize = 0x002000000000; -const uptr kOriginsBeg = 0x00c000000000; -# define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x4000000000ULL) + +// Everything is above 0x00e000000000. +const MappingDesc kMemoryLayout[] = { + {0x000000000000ULL, 0x00a000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x00a000000000ULL, 0x00c000000000ULL, MappingDesc::SHADOW, "shadow"}, + {0x00c000000000ULL, 0x00e000000000ULL, MappingDesc::ORIGIN, "origin"}, + {0x00e000000000ULL, 0x010000000000ULL, MappingDesc::APP, "app"}}; + +#define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x4000000000ULL) +#define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x002000000000) + #elif SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 64 -const uptr kLowMemBeg = 0x000000000000; -const uptr kLowMemSize = 0x010000000000; -const uptr kHighMemBeg = 0x600000000000; -const uptr kHighMemSize = 0x200000000000; -const uptr kShadowBeg = 0x100000000000; -const uptr kShadowSize = 0x210000000000; -const uptr kOriginsBeg = 0x380000000000; + +// Low memory: main binary, MAP_32BIT mappings and modules +// High memory: heap, modules and main thread stack +const MappingDesc kMemoryLayout[] = { + {0x000000000000ULL, 0x010000000000ULL, MappingDesc::APP, "low memory"}, + {0x010000000000ULL, 0x100000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x100000000000ULL, 0x310000000000ULL, MappingDesc::SHADOW, "shadow"}, + {0x310000000000ULL, 0x380000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x380000000000ULL, 0x590000000000ULL, MappingDesc::ORIGIN, "origin"}, + {0x590000000000ULL, 0x600000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x600000000000ULL, 0x800000000000ULL, MappingDesc::APP, "high memory"}}; + // Maps low and high app ranges to contiguous space with zero base: // Low: 0000 0000 0000 - 00ff ffff ffff -> 2000 0000 0000 - 20ff ffff ffff // High: 6000 0000 0000 - 7fff ffff ffff -> 0000 0000 0000 - 1fff ffff ffff -# define LINEARIZE_MEM(mem) \ - (((uptr)(mem) & ~0xc00000000000ULL) ^ 0x200000000000ULL) -# define MEM_TO_SHADOW(mem) (LINEARIZE_MEM((mem)) + 0x100000000000ULL) +#define LINEARIZE_MEM(mem) \ + (((uptr)(mem) & ~0xc00000000000ULL) ^ 0x200000000000ULL) +#define MEM_TO_SHADOW(mem) (LINEARIZE_MEM((mem)) + 0x100000000000ULL) +#define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x280000000000) + #elif SANITIZER_LINUX && SANITIZER_WORDSIZE == 64 -const uptr kLowMemBeg = 0; -const uptr kLowMemSize = 0; -const uptr kHighMemBeg = 0x600000000000; -const uptr kHighMemSize = 0x200000000000; -const uptr kShadowBeg = 0x200000000000; -const uptr kShadowSize = 0x200000000000; -const uptr kOriginsBeg = 0x400000000000; -# define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x400000000000ULL) + +// Requries PIE binary and ASLR enabled. +// Main thread stack and DSOs at 0x7f0000000000 (sometimes 0x7e0000000000). +// Heap at 0x600000000000. +const MappingDesc kMemoryLayout[] = { + {0x000000000000ULL, 0x200000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x200000000000ULL, 0x400000000000ULL, MappingDesc::SHADOW, "shadow"}, + {0x400000000000ULL, 0x600000000000ULL, MappingDesc::ORIGIN, "origin"}, + {0x600000000000ULL, 0x800000000000ULL, MappingDesc::APP, "app"}}; + +#define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x400000000000ULL) +#define SHADOW_TO_ORIGIN(mem) (((uptr)(mem)) + 0x200000000000ULL) + #else #error "Unsupported platform" #endif -const uptr kBad1Beg = kLowMemBeg + kLowMemSize; -const uptr kBad1Size = kShadowBeg - kBad1Beg; +const uptr kMemoryLayoutSize = sizeof(kMemoryLayout) / sizeof(kMemoryLayout[0]); -const uptr kBad2Beg = kShadowBeg + kShadowSize; -const uptr kBad2Size = kOriginsBeg - kBad2Beg; - -const uptr kOriginsSize = kShadowSize; - -const uptr kBad3Beg = kOriginsBeg + kOriginsSize; -const uptr kBad3Size = kHighMemBeg - kBad3Beg; - -#define SHADOW_TO_ORIGIN(shadow) \ - (((uptr)(shadow)) + (kOriginsBeg - kShadowBeg)) - #define MEM_TO_ORIGIN(mem) (SHADOW_TO_ORIGIN(MEM_TO_SHADOW((mem)))) -#define MEM_IS_APP(mem) \ - ((kLowMemSize > 0 && (uptr)(mem) < kLowMemSize) || \ - (uptr)(mem) >= kHighMemBeg) +#ifndef __clang__ +__attribute__((optimize("unroll-loops"))) +#endif +inline bool addr_is_type(uptr addr, MappingDesc::Type mapping_type) { +// It is critical for performance that this loop is unrolled (because then it is +// simplified into just a few constant comparisons). +#ifdef __clang__ +#pragma unroll +#endif + for (unsigned i = 0; i < kMemoryLayoutSize; ++i) + if (kMemoryLayout[i].type == mapping_type && + addr >= kMemoryLayout[i].start && addr < kMemoryLayout[i].end) + return true; + return false; +} -#define MEM_IS_SHADOW(mem) \ - ((uptr)(mem) >= kShadowBeg && (uptr)(mem) < kShadowBeg + kShadowSize) +#define MEM_IS_APP(mem) addr_is_type((uptr)(mem), MappingDesc::APP) +#define MEM_IS_SHADOW(mem) addr_is_type((uptr)(mem), MappingDesc::SHADOW) +#define MEM_IS_ORIGIN(mem) addr_is_type((uptr)(mem), MappingDesc::ORIGIN) -#define MEM_IS_ORIGIN(mem) \ - ((uptr)(mem) >= kOriginsBeg && (uptr)(mem) < kOriginsBeg + kOriginsSize) - // These constants must be kept in sync with the ones in MemorySanitizer.cc. const int kMsanParamTlsSize = 800; const int kMsanRetvalTlsSize = 800; namespace __msan { extern int msan_inited; 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); char *GetProcSelfMaps(); void InitializeInterceptors(); void MsanAllocatorThreadFinish(); +void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size); void *MsanReallocate(StackTrace *stack, void *oldp, uptr size, uptr alignment, bool zeroise); void MsanDeallocate(StackTrace *stack, void *ptr); void InstallTrapHandler(); void InstallAtExitHandler(); void ReplaceOperatorsNewAndDelete(); const char *GetStackOriginDescr(u32 id, uptr *pc); void EnterSymbolizer(); void ExitSymbolizer(); bool IsInSymbolizer(); struct SymbolizerScope { SymbolizerScope() { EnterSymbolizer(); } ~SymbolizerScope() { ExitSymbolizer(); } }; void MsanDie(); void PrintWarning(uptr pc, uptr bp); void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin); void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp, bool request_fast_unwind); void ReportUMR(StackTrace *stack, u32 origin); void ReportExpectedUMRNotFound(StackTrace *stack); void ReportStats(); void ReportAtExitStatistics(); void DescribeMemoryRange(const void *x, uptr size); void ReportUMRInsideAddressRange(const char *what, const void *start, uptr size, uptr offset); // Unpoison first n function arguments. void UnpoisonParam(uptr n); void UnpoisonThreadLocalState(); -u32 GetOriginIfPoisoned(uptr a, uptr size); -void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, u32 src_origin); -void CopyOrigin(void *dst, const void *src, uptr size, StackTrace *stack); -void MovePoison(void *dst, const void *src, uptr size, StackTrace *stack); -void CopyPoison(void *dst, const void *src, uptr size, StackTrace *stack); - // Returns a "chained" origin id, pointing to the given stack trace followed by // the previous origin id. u32 ChainOrigin(u32 id, StackTrace *stack); + +const int STACK_TRACE_TAG_POISON = StackTrace::TAG_CUSTOM + 1; #define GET_MALLOC_STACK_TRACE \ BufferedStackTrace stack; \ if (__msan_get_track_origins() && msan_inited) \ GetStackTrace(&stack, common_flags()->malloc_context_size, \ StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \ common_flags()->fast_unwind_on_malloc) #define GET_STORE_STACK_TRACE_PC_BP(pc, bp) \ BufferedStackTrace stack; \ if (__msan_get_track_origins() > 1 && msan_inited) \ GetStackTrace(&stack, flags()->store_context_size, pc, bp, \ common_flags()->fast_unwind_on_malloc) #define GET_FATAL_STACK_TRACE_PC_BP(pc, bp) \ BufferedStackTrace stack; \ if (msan_inited) \ GetStackTrace(&stack, kStackTraceMax, pc, bp, \ common_flags()->fast_unwind_on_fatal) #define GET_STORE_STACK_TRACE \ GET_STORE_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME()) class ScopedThreadLocalStateBackup { public: ScopedThreadLocalStateBackup() { Backup(); } ~ScopedThreadLocalStateBackup() { Restore(); } void Backup(); void Restore(); private: u64 va_arg_overflow_size_tls; }; extern void (*death_callback)(void); void MsanTSDInit(void (*destructor)(void *tsd)); void *MsanTSDGet(); void MsanTSDSet(void *tsd); void MsanTSDDtor(void *tsd); } // namespace __msan #define MSAN_MALLOC_HOOK(ptr, size) \ if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size) #define MSAN_FREE_HOOK(ptr) \ if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr) #endif // MSAN_H Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_allocator.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan_allocator.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan_allocator.cc (revision 279191) @@ -1,210 +1,227 @@ //===-- msan_allocator.cc --------------------------- ---------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // MemorySanitizer allocator. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "msan.h" #include "msan_allocator.h" #include "msan_origin.h" #include "msan_thread.h" +#include "msan_poisoning.h" namespace __msan { struct Metadata { uptr requested_size; }; struct MsanMapUnmapCallback { void OnMap(uptr p, uptr size) const {} void OnUnmap(uptr p, uptr size) const { __msan_unpoison((void *)p, size); // We are about to unmap a chunk of user memory. // Mark the corresponding shadow memory as not needed. FlushUnneededShadowMemory(MEM_TO_SHADOW(p), size); if (__msan_get_track_origins()) FlushUnneededShadowMemory(MEM_TO_ORIGIN(p), size); } }; #if defined(__mips64) static const uptr kMaxAllowedMallocSize = 2UL << 30; static const uptr kRegionSizeLog = 20; static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; typedef CompactSizeClassMap SizeClassMap; typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, sizeof(Metadata), SizeClassMap, kRegionSizeLog, ByteMap, MsanMapUnmapCallback> PrimaryAllocator; #elif defined(__x86_64__) static const uptr kAllocatorSpace = 0x600000000000ULL; static const uptr kAllocatorSize = 0x80000000000; // 8T. static const uptr kMetadataSize = sizeof(Metadata); static const uptr kMaxAllowedMallocSize = 8UL << 30; typedef SizeClassAllocator64 PrimaryAllocator; #endif typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator SecondaryAllocator; typedef CombinedAllocator Allocator; static Allocator allocator; static AllocatorCache fallback_allocator_cache; static SpinMutex fallback_mutex; static int inited = 0; static inline void Init() { if (inited) return; __msan_init(); inited = true; // this must happen before any threads are created. - allocator.Init(); + allocator.Init(common_flags()->allocator_may_return_null); } AllocatorCache *GetAllocatorCache(MsanThreadLocalMallocStorage *ms) { CHECK(ms); CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache)); return reinterpret_cast(ms->allocator_cache); } void MsanThreadLocalMallocStorage::CommitBack() { allocator.SwallowCache(GetAllocatorCache(this)); } static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment, bool zeroise) { Init(); if (size > kMaxAllowedMallocSize) { Report("WARNING: MemorySanitizer failed to allocate %p bytes\n", (void *)size); - return AllocatorReturnNull(); + return allocator.ReturnNullOrDie(); } MsanThread *t = GetCurrentThread(); void *allocated; if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); allocated = allocator.Allocate(cache, size, alignment, false); } else { SpinMutexLock l(&fallback_mutex); AllocatorCache *cache = &fallback_allocator_cache; allocated = allocator.Allocate(cache, size, alignment, false); } Metadata *meta = reinterpret_cast(allocator.GetMetaData(allocated)); meta->requested_size = size; if (zeroise) { __msan_clear_and_unpoison(allocated, size); } else if (flags()->poison_in_malloc) { __msan_poison(allocated, size); if (__msan_get_track_origins()) { + stack->tag = StackTrace::TAG_ALLOC; Origin o = Origin::CreateHeapOrigin(stack); __msan_set_origin(allocated, size, o.raw_id()); } } MSAN_MALLOC_HOOK(allocated, size); return allocated; } void MsanDeallocate(StackTrace *stack, void *p) { CHECK(p); Init(); MSAN_FREE_HOOK(p); Metadata *meta = reinterpret_cast(allocator.GetMetaData(p)); uptr size = meta->requested_size; meta->requested_size = 0; // This memory will not be reused by anyone else, so we are free to keep it // poisoned. if (flags()->poison_in_free) { __msan_poison(p, size); if (__msan_get_track_origins()) { + stack->tag = StackTrace::TAG_DEALLOC; Origin o = Origin::CreateHeapOrigin(stack); __msan_set_origin(p, size, o.raw_id()); } } MsanThread *t = GetCurrentThread(); if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); allocator.Deallocate(cache, p); } else { SpinMutexLock l(&fallback_mutex); AllocatorCache *cache = &fallback_allocator_cache; allocator.Deallocate(cache, p); } } +void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size) { + Init(); + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return allocator.ReturnNullOrDie(); + return MsanReallocate(stack, 0, nmemb * size, sizeof(u64), true); +} + void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, uptr alignment, bool zeroise) { if (!old_p) return MsanAllocate(stack, new_size, alignment, zeroise); if (!new_size) { MsanDeallocate(stack, old_p); return 0; } Metadata *meta = reinterpret_cast(allocator.GetMetaData(old_p)); uptr old_size = meta->requested_size; uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p); if (new_size <= actually_allocated_size) { // We are not reallocating here. meta->requested_size = new_size; - if (new_size > old_size) - __msan_poison((char*)old_p + old_size, new_size - old_size); + if (new_size > old_size) { + if (zeroise) { + __msan_clear_and_unpoison((char *)old_p + old_size, + new_size - old_size); + } else if (flags()->poison_in_malloc) { + stack->tag = StackTrace::TAG_ALLOC; + PoisonMemory((char *)old_p + old_size, new_size - old_size, stack); + } + } return old_p; } uptr memcpy_size = Min(new_size, old_size); void *new_p = MsanAllocate(stack, new_size, alignment, zeroise); // Printf("realloc: old_size %zd new_size %zd\n", old_size, new_size); if (new_p) { - __msan_memcpy(new_p, old_p, memcpy_size); + CopyMemory(new_p, old_p, memcpy_size, stack); MsanDeallocate(stack, old_p); } return new_p; } static uptr AllocationSize(const void *p) { if (p == 0) return 0; const void *beg = allocator.GetBlockBegin(p); if (beg != p) return 0; Metadata *b = (Metadata *)allocator.GetMetaData(p); return b->requested_size; } } // namespace __msan using namespace __msan; uptr __sanitizer_get_current_allocated_bytes() { uptr stats[AllocatorStatCount]; allocator.GetStats(stats); return stats[AllocatorStatAllocated]; } uptr __sanitizer_get_heap_size() { uptr stats[AllocatorStatCount]; allocator.GetStats(stats); return stats[AllocatorStatMapped]; } uptr __sanitizer_get_free_bytes() { return 1; } uptr __sanitizer_get_unmapped_bytes() { return 1; } uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; } uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); } Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_flags.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan_flags.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan_flags.h (revision 279191) @@ -1,40 +1,30 @@ //===-- msan_flags.h --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // -// MemorySanitizer allocator. //===----------------------------------------------------------------------===// #ifndef MSAN_FLAGS_H #define MSAN_FLAGS_H namespace __msan { -// Flags. struct Flags { - int exit_code; - int origin_history_size; - int origin_history_per_stack_limit; - bool poison_heap_with_zeroes; // default: false - bool poison_stack_with_zeroes; // default: false - bool poison_in_malloc; // default: true - bool poison_in_free; // default: true - bool report_umrs; - bool wrap_signals; - bool print_stats; - bool halt_on_error; - bool atexit; - int store_context_size; // like malloc_context_size, but for uninit stores +#define MSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "msan_flags.inc" +#undef MSAN_FLAG + + void SetDefaults(); }; Flags *flags(); } // namespace __msan #endif // MSAN_FLAGS_H Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_flags.inc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan_flags.inc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan_flags.inc (revision 279191) @@ -0,0 +1,33 @@ +//===-- msan_flags.inc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// MSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef MSAN_FLAG +# error "Define MSAN_FLAG prior to including this file!" +#endif + +// MSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +MSAN_FLAG(int, exit_code, 77, "") +MSAN_FLAG(int, origin_history_size, Origin::kMaxDepth, "") +MSAN_FLAG(int, origin_history_per_stack_limit, 20000, "") +MSAN_FLAG(bool, poison_heap_with_zeroes, false, "") +MSAN_FLAG(bool, poison_stack_with_zeroes, false, "") +MSAN_FLAG(bool, poison_in_malloc, true, "") +MSAN_FLAG(bool, poison_in_free, true, "") +MSAN_FLAG(bool, report_umrs, true, "") +MSAN_FLAG(bool, wrap_signals, true, "") +MSAN_FLAG(bool, print_stats, false, "") +MSAN_FLAG(bool, halt_on_error, !&__msan_keep_going, "") +MSAN_FLAG(bool, atexit, false, "") +MSAN_FLAG(int, store_context_size, 20, + "Like malloc_context_size, but for uninit stores.") Property changes on: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_flags.inc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_interceptors.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan_interceptors.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan_interceptors.cc (revision 279191) @@ -1,1656 +1,1575 @@ //===-- msan_interceptors.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // Interceptors for standard library functions. // // FIXME: move as many interceptors as possible into // sanitizer_common/sanitizer_common_interceptors.h //===----------------------------------------------------------------------===// #include "interception/interception.h" #include "msan.h" #include "msan_chained_origin_depot.h" #include "msan_origin.h" #include "msan_thread.h" +#include "msan_poisoning.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_linux.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" #include // ACHTUNG! No other system header includes in this file. // Ideally, we should get rid of stdarg.h as well. using namespace __msan; using __sanitizer::memory_order; using __sanitizer::atomic_load; using __sanitizer::atomic_store; using __sanitizer::atomic_uintptr_t; #if SANITIZER_FREEBSD #define __errno_location __error #endif // True if this is a nested interceptor. static THREADLOCAL int in_interceptor_scope; extern "C" int *__errno_location(void); struct InterceptorScope { InterceptorScope() { ++in_interceptor_scope; } ~InterceptorScope() { --in_interceptor_scope; } }; bool IsInInterceptorScope() { return in_interceptor_scope; } #define ENSURE_MSAN_INITED() do { \ CHECK(!msan_init_is_running); \ if (!msan_inited) { \ __msan_init(); \ } \ } while (0) // Check that [x, x+n) range is unpoisoned. #define CHECK_UNPOISONED_0(x, n) \ do { \ sptr offset = __msan_test_shadow(x, n); \ if (__msan::IsInSymbolizer()) \ break; \ if (offset >= 0 && __msan::flags()->report_umrs) { \ GET_CALLER_PC_BP_SP; \ (void) sp; \ ReportUMRInsideAddressRange(__func__, x, n, offset); \ __msan::PrintWarningWithOrigin( \ pc, bp, __msan_get_origin((const char *)x + offset)); \ if (__msan::flags()->halt_on_error) { \ Printf("Exiting\n"); \ Die(); \ } \ } \ } while (0) // Check that [x, x+n) range is unpoisoned unless we are in a nested // interceptor. #define CHECK_UNPOISONED(x, n) \ do { \ if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \ } while (0); 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); if (res > 0) __msan_unpoison(ptr, res *size); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(fread_unlocked)(ptr, size, nmemb, file); if (res > 0) __msan_unpoison(ptr, res *size); return res; } #define MSAN_MAYBE_INTERCEPT_FREAD_UNLOCKED INTERCEPT_FUNCTION(fread_unlocked) #else #define MSAN_MAYBE_INTERCEPT_FREAD_UNLOCKED #endif INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) { ENSURE_MSAN_INITED(); SSIZE_T res = REAL(readlink)(path, buf, bufsiz); if (res > 0) __msan_unpoison(buf, res); return res; } INTERCEPTOR(void *, memcpy, void *dest, const void *src, SIZE_T n) { return __msan_memcpy(dest, src, n); } INTERCEPTOR(void *, mempcpy, void *dest, const void *src, SIZE_T n) { return (char *)__msan_memcpy(dest, src, n) + n; } INTERCEPTOR(void *, memccpy, void *dest, const void *src, int c, SIZE_T n) { ENSURE_MSAN_INITED(); void *res = REAL(memccpy)(dest, src, c, n); CHECK(!res || (res >= dest && res <= (char *)dest + n)); SIZE_T sz = res ? (char *)res - (char *)dest : n; CHECK_UNPOISONED(src, sz); __msan_unpoison(dest, sz); return res; } INTERCEPTOR(void *, memmove, void *dest, const void *src, SIZE_T n) { return __msan_memmove(dest, src, n); } INTERCEPTOR(void *, memset, void *s, int c, SIZE_T n) { return __msan_memset(s, c, n); } INTERCEPTOR(void *, bcopy, const void *src, void *dest, SIZE_T n) { return __msan_memmove(dest, src, n); } INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(alignment & (alignment - 1), 0); CHECK_NE(memptr, 0); *memptr = MsanReallocate(&stack, 0, size, alignment, false); CHECK_NE(*memptr, 0); __msan_unpoison(memptr, sizeof(*memptr)); return 0; } #if !SANITIZER_FREEBSD INTERCEPTOR(void *, memalign, SIZE_T boundary, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(boundary & (boundary - 1), 0); void *ptr = MsanReallocate(&stack, 0, size, boundary, false); return ptr; } #define MSAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign) #else #define MSAN_MAYBE_INTERCEPT_MEMALIGN #endif INTERCEPTOR(void *, aligned_alloc, SIZE_T boundary, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(boundary & (boundary - 1), 0); void *ptr = MsanReallocate(&stack, 0, size, boundary, false); return ptr; } INTERCEPTOR(void *, __libc_memalign, SIZE_T boundary, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(boundary & (boundary - 1), 0); void *ptr = MsanReallocate(&stack, 0, size, boundary, false); DTLS_on_libc_memalign(ptr, size * boundary); return ptr; } INTERCEPTOR(void *, valloc, SIZE_T size) { GET_MALLOC_STACK_TRACE; void *ptr = MsanReallocate(&stack, 0, size, GetPageSizeCached(), false); return ptr; } #if !SANITIZER_FREEBSD INTERCEPTOR(void *, pvalloc, SIZE_T size) { GET_MALLOC_STACK_TRACE; uptr PageSize = GetPageSizeCached(); size = RoundUpTo(size, PageSize); if (size == 0) { // pvalloc(0) should allocate one page. size = PageSize; } void *ptr = MsanReallocate(&stack, 0, size, PageSize, false); return ptr; } #define MSAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc) #else #define MSAN_MAYBE_INTERCEPT_PVALLOC #endif INTERCEPTOR(void, free, void *ptr) { GET_MALLOC_STACK_TRACE; if (ptr == 0) return; MsanDeallocate(&stack, ptr); } #if !SANITIZER_FREEBSD INTERCEPTOR(void, cfree, void *ptr) { GET_MALLOC_STACK_TRACE; if (ptr == 0) return; MsanDeallocate(&stack, ptr); } #define MSAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree) #else #define MSAN_MAYBE_INTERCEPT_CFREE #endif INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { return __sanitizer_get_allocated_size(ptr); } #if !SANITIZER_FREEBSD // This function actually returns a struct by value, but we can't unpoison a // temporary! The following is equivalent on all supported platforms, and we // have a test to confirm that. INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) { REAL(memset)(sret, 0, sizeof(*sret)); __msan_unpoison(sret, sizeof(*sret)); } #define MSAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo) #else #define MSAN_MAYBE_INTERCEPT_MALLINFO #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, mallopt, int cmd, int value) { return -1; } #define MSAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt) #else #define MSAN_MAYBE_INTERCEPT_MALLOPT #endif #if !SANITIZER_FREEBSD INTERCEPTOR(void, malloc_stats, void) { // FIXME: implement, but don't call REAL(malloc_stats)! } #define MSAN_MAYBE_INTERCEPT_MALLOC_STATS INTERCEPT_FUNCTION(malloc_stats) #else #define MSAN_MAYBE_INTERCEPT_MALLOC_STATS #endif INTERCEPTOR(SIZE_T, strlen, const char *s) { if (msan_init_is_running) return REAL(strlen)(s); ENSURE_MSAN_INITED(); SIZE_T res = REAL(strlen)(s); CHECK_UNPOISONED(s, res + 1); return res; } INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T n) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(strnlen)(s, n); SIZE_T scan_size = (res == n) ? res : res + 1; CHECK_UNPOISONED(s, scan_size); 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); char *res = REAL(strcpy)(dest, src); // NOLINT - CopyPoison(dest, src, n + 1, &stack); + CopyShadowAndOrigin(dest, src, n + 1, &stack); return res; } INTERCEPTOR(char *, strncpy, char *dest, const char *src, SIZE_T n) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T copy_size = REAL(strnlen)(src, n); if (copy_size < n) copy_size++; // trailing \0 char *res = REAL(strncpy)(dest, src, n); // NOLINT - CopyPoison(dest, src, copy_size, &stack); + CopyShadowAndOrigin(dest, src, copy_size, &stack); __msan_unpoison(dest + copy_size, n - copy_size); return res; } INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); char *res = REAL(stpcpy)(dest, src); // NOLINT - CopyPoison(dest, src, n + 1, &stack); + CopyShadowAndOrigin(dest, src, n + 1, &stack); return res; } INTERCEPTOR(char *, strdup, char *src) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; + // On FreeBSD strdup() leverages strlen(). + InterceptorScope interceptor_scope; SIZE_T n = REAL(strlen)(src); char *res = REAL(strdup)(src); - CopyPoison(res, src, n + 1, &stack); + CopyShadowAndOrigin(res, src, n + 1, &stack); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(char *, __strdup, char *src) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); char *res = REAL(__strdup)(src); - CopyPoison(res, src, n + 1, &stack); + CopyShadowAndOrigin(res, src, n + 1, &stack); return res; } #define MSAN_MAYBE_INTERCEPT___STRDUP INTERCEPT_FUNCTION(__strdup) #else #define MSAN_MAYBE_INTERCEPT___STRDUP #endif INTERCEPTOR(char *, strndup, char *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; + // On FreeBSD strndup() leverages strnlen(). + InterceptorScope interceptor_scope; SIZE_T copy_size = REAL(strnlen)(src, n); char *res = REAL(strndup)(src, n); - CopyPoison(res, src, copy_size, &stack); + CopyShadowAndOrigin(res, src, copy_size, &stack); __msan_unpoison(res + copy_size, 1); // \0 return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(char *, __strndup, char *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T copy_size = REAL(strnlen)(src, n); char *res = REAL(__strndup)(src, n); - CopyPoison(res, src, copy_size, &stack); + CopyShadowAndOrigin(res, src, copy_size, &stack); __msan_unpoison(res + copy_size, 1); // \0 return res; } #define MSAN_MAYBE_INTERCEPT___STRNDUP INTERCEPT_FUNCTION(__strndup) #else #define MSAN_MAYBE_INTERCEPT___STRNDUP #endif INTERCEPTOR(char *, gcvt, double number, SIZE_T ndigit, char *buf) { ENSURE_MSAN_INITED(); char *res = REAL(gcvt)(number, ndigit, buf); SIZE_T n = REAL(strlen)(buf); __msan_unpoison(buf, n + 1); return res; } INTERCEPTOR(char *, strcat, char *dest, const char *src) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T src_size = REAL(strlen)(src); SIZE_T dest_size = REAL(strlen)(dest); char *res = REAL(strcat)(dest, src); // NOLINT - CopyPoison(dest + dest_size, src, src_size + 1, &stack); + CopyShadowAndOrigin(dest + dest_size, src, src_size + 1, &stack); return res; } INTERCEPTOR(char *, strncat, char *dest, const char *src, SIZE_T n) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T dest_size = REAL(strlen)(dest); SIZE_T copy_size = REAL(strnlen)(src, n); char *res = REAL(strncat)(dest, src, n); // NOLINT - CopyPoison(dest + dest_size, src, copy_size, &stack); + CopyShadowAndOrigin(dest + dest_size, src, copy_size, &stack); __msan_unpoison(dest + dest_size + copy_size, 1); // \0 return res; } // Hack: always pass nptr and endptr as part of __VA_ARGS_ to avoid having to // deal with empty __VA_ARGS__ in the case of INTERCEPTOR_STRTO. #define INTERCEPTOR_STRTO_BODY(ret_type, func, ...) \ ENSURE_MSAN_INITED(); \ ret_type res = REAL(func)(__VA_ARGS__); \ __msan_unpoison(endptr, sizeof(*endptr)); \ return res; #define INTERCEPTOR_STRTO(ret_type, func, char_type) \ INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr) { \ INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr); \ } #define INTERCEPTOR_STRTO_BASE(ret_type, func, char_type) \ INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr, \ int base) { \ INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr, base); \ } #define INTERCEPTOR_STRTO_LOC(ret_type, func, char_type) \ INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr, \ void *loc) { \ INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr, loc); \ } #define INTERCEPTOR_STRTO_BASE_LOC(ret_type, func, char_type) \ INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr, \ int base, void *loc) { \ INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr, base, loc); \ } #define INTERCEPTORS_STRTO(ret_type, func, char_type) \ INTERCEPTOR_STRTO(ret_type, func, char_type) \ INTERCEPTOR_STRTO_LOC(ret_type, func##_l, char_type) \ INTERCEPTOR_STRTO_LOC(ret_type, __##func##_l, char_type) \ INTERCEPTOR_STRTO_LOC(ret_type, __##func##_internal, char_type) #define INTERCEPTORS_STRTO_BASE(ret_type, func, char_type) \ INTERCEPTOR_STRTO_BASE(ret_type, func, char_type) \ INTERCEPTOR_STRTO_BASE_LOC(ret_type, func##_l, char_type) \ INTERCEPTOR_STRTO_BASE_LOC(ret_type, __##func##_l, char_type) \ INTERCEPTOR_STRTO_BASE_LOC(ret_type, __##func##_internal, char_type) INTERCEPTORS_STRTO(double, strtod, char) // NOLINT INTERCEPTORS_STRTO(float, strtof, char) // NOLINT INTERCEPTORS_STRTO(long double, strtold, char) // NOLINT INTERCEPTORS_STRTO_BASE(long, strtol, char) // NOLINT INTERCEPTORS_STRTO_BASE(long long, strtoll, char) // NOLINT INTERCEPTORS_STRTO_BASE(unsigned long, strtoul, char) // NOLINT INTERCEPTORS_STRTO_BASE(unsigned long long, strtoull, char) // NOLINT INTERCEPTORS_STRTO(double, wcstod, wchar_t) // NOLINT INTERCEPTORS_STRTO(float, wcstof, wchar_t) // NOLINT INTERCEPTORS_STRTO(long double, wcstold, wchar_t) // NOLINT INTERCEPTORS_STRTO_BASE(long, wcstol, wchar_t) // NOLINT INTERCEPTORS_STRTO_BASE(long long, wcstoll, wchar_t) // NOLINT INTERCEPTORS_STRTO_BASE(unsigned long, wcstoul, wchar_t) // NOLINT INTERCEPTORS_STRTO_BASE(unsigned long long, wcstoull, wchar_t) // NOLINT #define INTERCEPT_STRTO(func) \ INTERCEPT_FUNCTION(func); \ INTERCEPT_FUNCTION(func##_l); \ INTERCEPT_FUNCTION(__##func##_l); \ INTERCEPT_FUNCTION(__##func##_internal); // FIXME: support *wprintf in common format interceptors. INTERCEPTOR(int, vswprintf, void *str, uptr size, void *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vswprintf)(str, size, format, ap); if (res >= 0) { __msan_unpoison(str, 4 * (res + 1)); } return res; } INTERCEPTOR(int, swprintf, void *str, uptr size, void *format, ...) { ENSURE_MSAN_INITED(); va_list ap; va_start(ap, format); int res = vswprintf(str, size, format, ap); va_end(ap); return res; } INTERCEPTOR(SIZE_T, strxfrm, char *dest, const char *src, SIZE_T n) { ENSURE_MSAN_INITED(); CHECK_UNPOISONED(src, REAL(strlen)(src) + 1); SIZE_T res = REAL(strxfrm)(dest, src, n); if (res < n) __msan_unpoison(dest, res + 1); return res; } INTERCEPTOR(SIZE_T, strxfrm_l, char *dest, const char *src, SIZE_T n, void *loc) { ENSURE_MSAN_INITED(); CHECK_UNPOISONED(src, REAL(strlen)(src) + 1); SIZE_T res = REAL(strxfrm_l)(dest, src, n, loc); if (res < n) __msan_unpoison(dest, res + 1); return res; } #define INTERCEPTOR_STRFTIME_BODY(char_type, ret_type, func, s, ...) \ ENSURE_MSAN_INITED(); \ ret_type res = REAL(func)(s, __VA_ARGS__); \ if (s) __msan_unpoison(s, sizeof(char_type) * (res + 1)); \ return res; INTERCEPTOR(SIZE_T, strftime, char *s, SIZE_T max, const char *format, __sanitizer_tm *tm) { INTERCEPTOR_STRFTIME_BODY(char, SIZE_T, strftime, s, max, format, tm); } INTERCEPTOR(SIZE_T, strftime_l, char *s, SIZE_T max, const char *format, __sanitizer_tm *tm, void *loc) { INTERCEPTOR_STRFTIME_BODY(char, SIZE_T, strftime_l, s, max, format, tm, loc); } #if !SANITIZER_FREEBSD INTERCEPTOR(SIZE_T, __strftime_l, char *s, SIZE_T max, const char *format, __sanitizer_tm *tm, void *loc) { INTERCEPTOR_STRFTIME_BODY(char, SIZE_T, __strftime_l, s, max, format, tm, loc); } #define MSAN_MAYBE_INTERCEPT___STRFTIME_L INTERCEPT_FUNCTION(__strftime_l) #else #define MSAN_MAYBE_INTERCEPT___STRFTIME_L #endif INTERCEPTOR(SIZE_T, wcsftime, wchar_t *s, SIZE_T max, const wchar_t *format, __sanitizer_tm *tm) { INTERCEPTOR_STRFTIME_BODY(wchar_t, SIZE_T, wcsftime, s, max, format, tm); } INTERCEPTOR(SIZE_T, wcsftime_l, wchar_t *s, SIZE_T max, const wchar_t *format, __sanitizer_tm *tm, void *loc) { INTERCEPTOR_STRFTIME_BODY(wchar_t, SIZE_T, wcsftime_l, s, max, format, tm, loc); } #if !SANITIZER_FREEBSD INTERCEPTOR(SIZE_T, __wcsftime_l, wchar_t *s, SIZE_T max, const wchar_t *format, __sanitizer_tm *tm, void *loc) { INTERCEPTOR_STRFTIME_BODY(wchar_t, SIZE_T, __wcsftime_l, s, max, format, tm, loc); } #define MSAN_MAYBE_INTERCEPT___WCSFTIME_L INTERCEPT_FUNCTION(__wcsftime_l) #else #define MSAN_MAYBE_INTERCEPT___WCSFTIME_L #endif INTERCEPTOR(int, mbtowc, wchar_t *dest, const char *src, SIZE_T n) { ENSURE_MSAN_INITED(); int res = REAL(mbtowc)(dest, src, n); if (res != -1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); return res; } INTERCEPTOR(int, mbrtowc, wchar_t *dest, const char *src, SIZE_T n, void *ps) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(mbrtowc)(dest, src, n, ps); if (res != (SIZE_T)-1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); return res; } INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(wcslen)(s); CHECK_UNPOISONED(s, sizeof(wchar_t) * (res + 1)); return res; } // wchar_t *wcschr(const wchar_t *wcs, wchar_t wc); INTERCEPTOR(wchar_t *, wcschr, void *s, wchar_t wc, void *ps) { ENSURE_MSAN_INITED(); wchar_t *res = REAL(wcschr)(s, wc, ps); return res; } // wchar_t *wcscpy(wchar_t *dest, const wchar_t *src); INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dest, const wchar_t *src) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wcscpy)(dest, src); - CopyPoison(dest, src, sizeof(wchar_t) * (REAL(wcslen)(src) + 1), &stack); + CopyShadowAndOrigin(dest, src, sizeof(wchar_t) * (REAL(wcslen)(src) + 1), + &stack); return res; } // wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, SIZE_T n); INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmemcpy)(dest, src, n); - CopyPoison(dest, src, n * sizeof(wchar_t), &stack); + CopyShadowAndOrigin(dest, src, n * sizeof(wchar_t), &stack); return res; } INTERCEPTOR(wchar_t *, wmempcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmempcpy)(dest, src, n); - CopyPoison(dest, src, n * sizeof(wchar_t), &stack); + CopyShadowAndOrigin(dest, src, n * sizeof(wchar_t), &stack); return res; } INTERCEPTOR(wchar_t *, wmemset, wchar_t *s, wchar_t c, SIZE_T n) { CHECK(MEM_IS_APP(s)); ENSURE_MSAN_INITED(); wchar_t *res = REAL(wmemset)(s, c, n); __msan_unpoison(s, n * sizeof(wchar_t)); return res; } INTERCEPTOR(wchar_t *, wmemmove, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmemmove)(dest, src, n); - MovePoison(dest, src, n * sizeof(wchar_t), &stack); + MoveShadowAndOrigin(dest, src, n * sizeof(wchar_t), &stack); return res; } INTERCEPTOR(int, wcscmp, const wchar_t *s1, const wchar_t *s2) { ENSURE_MSAN_INITED(); int res = REAL(wcscmp)(s1, s2); return res; } INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { ENSURE_MSAN_INITED(); int res = REAL(gettimeofday)(tv, tz); if (tv) __msan_unpoison(tv, 16); if (tz) __msan_unpoison(tz, 8); return res; } INTERCEPTOR(char *, fcvt, double x, int a, int *b, int *c) { ENSURE_MSAN_INITED(); char *res = REAL(fcvt)(x, a, b, c); __msan_unpoison(b, sizeof(*b)); __msan_unpoison(c, sizeof(*c)); if (res) __msan_unpoison(res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(char *, getenv, char *name) { if (msan_init_is_running) return REAL(getenv)(name); ENSURE_MSAN_INITED(); char *res = REAL(getenv)(name); if (res) __msan_unpoison(res, REAL(strlen)(res) + 1); return res; } extern char **environ; static void UnpoisonEnviron() { char **envp = environ; for (; *envp; ++envp) { __msan_unpoison(envp, sizeof(*envp)); __msan_unpoison(*envp, REAL(strlen)(*envp) + 1); } // Trailing NULL pointer. __msan_unpoison(envp, sizeof(*envp)); } INTERCEPTOR(int, setenv, const char *name, const char *value, int overwrite) { ENSURE_MSAN_INITED(); int res = REAL(setenv)(name, value, overwrite); if (!res) UnpoisonEnviron(); return res; } INTERCEPTOR(int, putenv, char *string) { ENSURE_MSAN_INITED(); int res = REAL(putenv)(string); if (!res) UnpoisonEnviron(); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(int, __fxstat, int magic, int fd, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__fxstat)(magic, fd, buf); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } #define MSAN_MAYBE_INTERCEPT___FXSTAT INTERCEPT_FUNCTION(__fxstat) #else #define MSAN_MAYBE_INTERCEPT___FXSTAT #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, __fxstat64, int magic, int fd, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__fxstat64)(magic, fd, buf); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); return res; } #define MSAN_MAYBE_INTERCEPT___FXSTAT64 INTERCEPT_FUNCTION(__fxstat64) #else #define MSAN_MAYBE_INTERCEPT___FXSTAT64 #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_FREEBSD +INTERCEPTOR(int, fstatat, int fd, char *pathname, void *buf, int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(fstatat)(fd, pathname, buf, flags); + if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} +# define MSAN_INTERCEPT_FSTATAT INTERCEPT_FUNCTION(fstatat) +#else INTERCEPTOR(int, __fxstatat, int magic, int fd, char *pathname, void *buf, int flags) { ENSURE_MSAN_INITED(); int res = REAL(__fxstatat)(magic, fd, pathname, buf, flags); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } -#define MSAN_MAYBE_INTERCEPT___FXSTATAT INTERCEPT_FUNCTION(__fxstatat) -#else -#define MSAN_MAYBE_INTERCEPT___FXSTATAT +# define MSAN_INTERCEPT_FSTATAT INTERCEPT_FUNCTION(__fxstatat) #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, __fxstatat64, int magic, int fd, char *pathname, void *buf, int flags) { ENSURE_MSAN_INITED(); int res = REAL(__fxstatat64)(magic, fd, pathname, buf, flags); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); return res; } #define MSAN_MAYBE_INTERCEPT___FXSTATAT64 INTERCEPT_FUNCTION(__fxstatat64) #else #define MSAN_MAYBE_INTERCEPT___FXSTATAT64 #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_FREEBSD +INTERCEPTOR(int, stat, char *path, void *buf) { + ENSURE_MSAN_INITED(); + int res = REAL(stat)(path, buf); + if (!res) + __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} +# define MSAN_INTERCEPT_STAT INTERCEPT_FUNCTION(stat) +#else INTERCEPTOR(int, __xstat, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__xstat)(magic, path, buf); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } -#define MSAN_MAYBE_INTERCEPT___XSTAT INTERCEPT_FUNCTION(__xstat) -#else -#define MSAN_MAYBE_INTERCEPT___XSTAT +# define MSAN_INTERCEPT_STAT INTERCEPT_FUNCTION(__xstat) #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, __xstat64, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__xstat64)(magic, path, buf); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); return res; } #define MSAN_MAYBE_INTERCEPT___XSTAT64 INTERCEPT_FUNCTION(__xstat64) #else #define MSAN_MAYBE_INTERCEPT___XSTAT64 #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, __lxstat, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__lxstat)(magic, path, buf); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } #define MSAN_MAYBE_INTERCEPT___LXSTAT INTERCEPT_FUNCTION(__lxstat) #else #define MSAN_MAYBE_INTERCEPT___LXSTAT #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, __lxstat64, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__lxstat64)(magic, path, buf); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); return res; } #define MSAN_MAYBE_INTERCEPT___LXSTAT64 INTERCEPT_FUNCTION(__lxstat64) #else #define MSAN_MAYBE_INTERCEPT___LXSTAT64 #endif INTERCEPTOR(int, pipe, int pipefd[2]) { if (msan_init_is_running) return REAL(pipe)(pipefd); ENSURE_MSAN_INITED(); int res = REAL(pipe)(pipefd); if (!res) __msan_unpoison(pipefd, sizeof(int[2])); return res; } INTERCEPTOR(int, pipe2, int pipefd[2], int flags) { ENSURE_MSAN_INITED(); int res = REAL(pipe2)(pipefd, flags); if (!res) __msan_unpoison(pipefd, sizeof(int[2])); return res; } INTERCEPTOR(int, socketpair, int domain, int type, int protocol, int sv[2]) { ENSURE_MSAN_INITED(); int res = REAL(socketpair)(domain, type, protocol, sv); if (!res) __msan_unpoison(sv, sizeof(int[2])); return res; } INTERCEPTOR(char *, fgets, char *s, int size, void *stream) { ENSURE_MSAN_INITED(); char *res = REAL(fgets)(s, size, stream); if (res) __msan_unpoison(s, REAL(strlen)(s) + 1); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) { ENSURE_MSAN_INITED(); char *res = REAL(fgets_unlocked)(s, size, stream); if (res) __msan_unpoison(s, REAL(strlen)(s) + 1); return res; } #define MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED INTERCEPT_FUNCTION(fgets_unlocked) #else #define MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED #endif INTERCEPTOR(int, getrlimit, int resource, void *rlim) { if (msan_init_is_running) return REAL(getrlimit)(resource, rlim); ENSURE_MSAN_INITED(); int res = REAL(getrlimit)(resource, rlim); if (!res) __msan_unpoison(rlim, __sanitizer::struct_rlimit_sz); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(int, getrlimit64, int resource, void *rlim) { if (msan_init_is_running) return REAL(getrlimit64)(resource, rlim); ENSURE_MSAN_INITED(); int res = REAL(getrlimit64)(resource, rlim); if (!res) __msan_unpoison(rlim, __sanitizer::struct_rlimit64_sz); return res; } #define MSAN_MAYBE_INTERCEPT_GETRLIMIT64 INTERCEPT_FUNCTION(getrlimit64) #else #define MSAN_MAYBE_INTERCEPT_GETRLIMIT64 #endif -INTERCEPTOR(int, uname, void *utsname) { +#if SANITIZER_FREEBSD +// FreeBSD's define uname() as +// static __inline int uname(struct utsname *name) { +// return __xuname(SYS_NMLN, (void*)name); +// } +INTERCEPTOR(int, __xuname, int size, void *utsname) { ENSURE_MSAN_INITED(); + int res = REAL(__xuname)(size, utsname); + if (!res) + __msan_unpoison(utsname, __sanitizer::struct_utsname_sz); + return res; +} +#define MSAN_INTERCEPT_UNAME INTERCEPT_FUNCTION(__xuname) +#else +INTERCEPTOR(int, uname, struct utsname *utsname) { + ENSURE_MSAN_INITED(); int res = REAL(uname)(utsname); - if (!res) { + if (!res) __msan_unpoison(utsname, __sanitizer::struct_utsname_sz); - } return res; } +#define MSAN_INTERCEPT_UNAME INTERCEPT_FUNCTION(uname) +#endif INTERCEPTOR(int, gethostname, char *name, SIZE_T len) { ENSURE_MSAN_INITED(); int res = REAL(gethostname)(name, len); if (!res) { SIZE_T real_len = REAL(strnlen)(name, len); if (real_len < len) ++real_len; __msan_unpoison(name, real_len); } return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(int, epoll_wait, int epfd, void *events, int maxevents, int timeout) { ENSURE_MSAN_INITED(); int res = REAL(epoll_wait)(epfd, events, maxevents, timeout); if (res > 0) { __msan_unpoison(events, __sanitizer::struct_epoll_event_sz * res); } return res; } #define MSAN_MAYBE_INTERCEPT_EPOLL_WAIT INTERCEPT_FUNCTION(epoll_wait) #else #define MSAN_MAYBE_INTERCEPT_EPOLL_WAIT #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, epoll_pwait, int epfd, void *events, int maxevents, int timeout, void *sigmask) { ENSURE_MSAN_INITED(); int res = REAL(epoll_pwait)(epfd, events, maxevents, timeout, sigmask); if (res > 0) { __msan_unpoison(events, __sanitizer::struct_epoll_event_sz * res); } return res; } #define MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT INTERCEPT_FUNCTION(epoll_pwait) #else #define MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT #endif INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) { ENSURE_MSAN_INITED(); SSIZE_T res = REAL(recv)(fd, buf, len, flags); if (res > 0) __msan_unpoison(buf, res); return res; } INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags, void *srcaddr, int *addrlen) { ENSURE_MSAN_INITED(); SIZE_T srcaddr_sz; if (srcaddr) srcaddr_sz = *addrlen; SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen); if (res > 0) { __msan_unpoison(buf, res); if (srcaddr) { SIZE_T sz = *addrlen; - __msan_unpoison(srcaddr, (sz < srcaddr_sz) ? sz : srcaddr_sz); + __msan_unpoison(srcaddr, Min(sz, srcaddr_sz)); } } return res; } INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) - return AllocatorReturnNull(); GET_MALLOC_STACK_TRACE; - if (!msan_inited) { + if (UNLIKELY(!msan_inited)) { // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. const SIZE_T kCallocPoolSize = 1024; static uptr calloc_memory_for_dlsym[kCallocPoolSize]; static SIZE_T allocated; SIZE_T size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; void *mem = (void*)&calloc_memory_for_dlsym[allocated]; allocated += size_in_words; CHECK(allocated < kCallocPoolSize); return mem; } - return MsanReallocate(&stack, 0, nmemb * size, sizeof(u64), true); + return MsanCalloc(&stack, nmemb, size); } INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) { GET_MALLOC_STACK_TRACE; return MsanReallocate(&stack, ptr, size, sizeof(u64), false); } INTERCEPTOR(void *, malloc, SIZE_T size) { GET_MALLOC_STACK_TRACE; return MsanReallocate(&stack, 0, size, sizeof(u64), false); } -void __msan_allocated_memory(const void* data, uptr size) { +void __msan_allocated_memory(const void *data, uptr size) { GET_MALLOC_STACK_TRACE; - if (flags()->poison_in_malloc) - __msan_poison(data, size); - if (__msan_get_track_origins()) { - Origin o = Origin::CreateHeapOrigin(&stack); - __msan_set_origin(data, size, o.raw_id()); + if (flags()->poison_in_malloc) { + stack.tag = STACK_TRACE_TAG_POISON; + PoisonMemory(data, size, &stack); } } INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, int fd, OFF_T offset) { if (msan_init_is_running) return REAL(mmap)(addr, length, prot, flags, fd, offset); ENSURE_MSAN_INITED(); if (addr && !MEM_IS_APP(addr)) { if (flags & map_fixed) { *__errno_location() = errno_EINVAL; return (void *)-1; } else { addr = 0; } } void *res = REAL(mmap)(addr, length, prot, flags, fd, offset); if (res != (void*)-1) __msan_unpoison(res, RoundUpTo(length, GetPageSize())); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags, int fd, OFF64_T offset) { ENSURE_MSAN_INITED(); if (addr && !MEM_IS_APP(addr)) { if (flags & map_fixed) { *__errno_location() = errno_EINVAL; return (void *)-1; } else { addr = 0; } } void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset); if (res != (void*)-1) __msan_unpoison(res, RoundUpTo(length, GetPageSize())); return res; } #define MSAN_MAYBE_INTERCEPT_MMAP64 INTERCEPT_FUNCTION(mmap64) #else #define MSAN_MAYBE_INTERCEPT_MMAP64 #endif struct dlinfo { char *dli_fname; void *dli_fbase; char *dli_sname; void *dli_saddr; }; INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) { ENSURE_MSAN_INITED(); int res = REAL(dladdr)(addr, info); if (res != 0) { __msan_unpoison(info, sizeof(*info)); if (info->dli_fname) __msan_unpoison(info->dli_fname, REAL(strlen)(info->dli_fname) + 1); if (info->dli_sname) __msan_unpoison(info->dli_sname, REAL(strlen)(info->dli_sname) + 1); } return res; } INTERCEPTOR(char *, dlerror, int fake) { ENSURE_MSAN_INITED(); char *res = REAL(dlerror)(fake); if (res != 0) __msan_unpoison(res, REAL(strlen)(res) + 1); return res; } typedef int (*dl_iterate_phdr_cb)(__sanitizer_dl_phdr_info *info, SIZE_T size, void *data); struct dl_iterate_phdr_data { dl_iterate_phdr_cb callback; void *data; }; static int msan_dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size, void *data) { if (info) { __msan_unpoison(info, size); if (info->dlpi_name) __msan_unpoison(info->dlpi_name, REAL(strlen)(info->dlpi_name) + 1); } dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data; UnpoisonParam(3); return cbdata->callback(info, size, cbdata->data); } INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb callback, void *data) { ENSURE_MSAN_INITED(); dl_iterate_phdr_data cbdata; cbdata.callback = callback; cbdata.data = data; int res = REAL(dl_iterate_phdr)(msan_dl_iterate_phdr_cb, (void *)&cbdata); return res; } INTERCEPTOR(int, getrusage, int who, void *usage) { ENSURE_MSAN_INITED(); int res = REAL(getrusage)(who, usage); if (res == 0) { __msan_unpoison(usage, __sanitizer::struct_rusage_sz); } return res; } class SignalHandlerScope { public: SignalHandlerScope() { if (MsanThread *t = GetCurrentThread()) t->EnterSignalHandler(); } ~SignalHandlerScope() { if (MsanThread *t = GetCurrentThread()) t->LeaveSignalHandler(); } }; // sigactions_mu guarantees atomicity of sigaction() and signal() calls. // Access to sigactions[] is gone with relaxed atomics to avoid data race with // the signal handler. const int kMaxSignals = 1024; static atomic_uintptr_t sigactions[kMaxSignals]; static StaticSpinMutex sigactions_mu; static void SignalHandler(int signo) { SignalHandlerScope signal_handler_scope; ScopedThreadLocalStateBackup stlsb; UnpoisonParam(1); typedef void (*signal_cb)(int x); signal_cb cb = (signal_cb)atomic_load(&sigactions[signo], memory_order_relaxed); cb(signo); } static void SignalAction(int signo, void *si, void *uc) { SignalHandlerScope signal_handler_scope; ScopedThreadLocalStateBackup stlsb; UnpoisonParam(3); __msan_unpoison(si, sizeof(__sanitizer_sigaction)); __msan_unpoison(uc, __sanitizer::ucontext_t_sz); typedef void (*sigaction_cb)(int, void *, void *); sigaction_cb cb = (sigaction_cb)atomic_load(&sigactions[signo], memory_order_relaxed); cb(signo, si, uc); } INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, __sanitizer_sigaction *oldact) { ENSURE_MSAN_INITED(); // FIXME: check that *act is unpoisoned. // That requires intercepting all of sigemptyset, sigfillset, etc. int res; if (flags()->wrap_signals) { SpinMutexLock lock(&sigactions_mu); CHECK_LT(signo, kMaxSignals); uptr old_cb = atomic_load(&sigactions[signo], memory_order_relaxed); __sanitizer_sigaction new_act; __sanitizer_sigaction *pnew_act = act ? &new_act : 0; if (act) { REAL(memcpy)(pnew_act, act, sizeof(__sanitizer_sigaction)); uptr cb = (uptr)pnew_act->sigaction; uptr new_cb = (pnew_act->sa_flags & __sanitizer::sa_siginfo) ? (uptr)SignalAction : (uptr)SignalHandler; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { atomic_store(&sigactions[signo], cb, memory_order_relaxed); pnew_act->sigaction = (void (*)(int, void *, void *))new_cb; } } res = REAL(sigaction)(signo, pnew_act, oldact); if (res == 0 && oldact) { uptr cb = (uptr)oldact->sigaction; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { oldact->sigaction = (void (*)(int, void *, void *))old_cb; } } } else { res = REAL(sigaction)(signo, act, oldact); } if (res == 0 && oldact) { __msan_unpoison(oldact, sizeof(__sanitizer_sigaction)); } return res; } INTERCEPTOR(int, signal, int signo, uptr cb) { ENSURE_MSAN_INITED(); if (flags()->wrap_signals) { CHECK_LT(signo, kMaxSignals); SpinMutexLock lock(&sigactions_mu); if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { atomic_store(&sigactions[signo], cb, memory_order_relaxed); cb = (uptr) SignalHandler; } return REAL(signal)(signo, cb); } else { return REAL(signal)(signo, cb); } } extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); static void *MsanThreadStartFunc(void *arg) { MsanThread *t = (MsanThread *)arg; SetCurrentThread(t); return t->ThreadStart(); } INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { ENSURE_MSAN_INITED(); // for GetTlsSize() __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); attr = &myattr; } AdjustStackSize(attr); MsanThread *t = MsanThread::Create(callback, param); int res = REAL(pthread_create)(th, attr, MsanThreadStartFunc, t); if (attr == &myattr) pthread_attr_destroy(&myattr); if (!res) { __msan_unpoison(th, __sanitizer::pthread_t_sz); } return res; } INTERCEPTOR(int, pthread_key_create, __sanitizer_pthread_key_t *key, void (*dtor)(void *value)) { if (msan_init_is_running) return REAL(pthread_key_create)(key, dtor); ENSURE_MSAN_INITED(); int res = REAL(pthread_key_create)(key, dtor); if (!res && key) __msan_unpoison(key, sizeof(*key)); return res; } INTERCEPTOR(int, pthread_join, void *th, void **retval) { ENSURE_MSAN_INITED(); int res = REAL(pthread_join)(th, retval); if (!res && retval) __msan_unpoison(retval, sizeof(*retval)); return res; } extern char *tzname[2]; INTERCEPTOR(void, tzset, int fake) { ENSURE_MSAN_INITED(); REAL(tzset)(fake); if (tzname[0]) __msan_unpoison(tzname[0], REAL(strlen)(tzname[0]) + 1); if (tzname[1]) __msan_unpoison(tzname[1], REAL(strlen)(tzname[1]) + 1); return; } struct MSanAtExitRecord { void (*func)(void *arg); void *arg; }; void MSanAtExitWrapper(void *arg) { UnpoisonParam(1); MSanAtExitRecord *r = (MSanAtExitRecord *)arg; r->func(r->arg); InternalFree(r); } // Unpoison argument shadow for C++ module destructors. INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, void *dso_handle) { if (msan_init_is_running) return REAL(__cxa_atexit)(func, arg, dso_handle); ENSURE_MSAN_INITED(); MSanAtExitRecord *r = (MSanAtExitRecord *)InternalAlloc(sizeof(MSanAtExitRecord)); r->func = func; r->arg = arg; return REAL(__cxa_atexit)(MSanAtExitWrapper, r, dso_handle); } DECLARE_REAL(int, shmctl, int shmid, int cmd, void *buf) INTERCEPTOR(void *, shmat, int shmid, const void *shmaddr, int shmflg) { ENSURE_MSAN_INITED(); void *p = REAL(shmat)(shmid, shmaddr, shmflg); if (p != (void *)-1) { __sanitizer_shmid_ds ds; int res = REAL(shmctl)(shmid, shmctl_ipc_stat, &ds); if (!res) { __msan_unpoison(p, ds.shm_segsz); } } return p; } static void BeforeFork() { StackDepotLockAll(); ChainedOriginDepotLockAll(); } static void AfterFork() { ChainedOriginDepotUnlockAll(); StackDepotUnlockAll(); } INTERCEPTOR(int, fork, void) { ENSURE_MSAN_INITED(); BeforeFork(); int pid = REAL(fork)(); AfterFork(); return pid; } struct MSanInterceptorContext { bool in_interceptor_scope; }; namespace __msan { int OnExit() { // FIXME: ask frontend whether we need to return failure. return 0; } } // namespace __msan // A version of CHECK_UNPOISONED using a saved scope value. Used in common // interceptors. #define CHECK_UNPOISONED_CTX(ctx, x, n) \ do { \ if (!((MSanInterceptorContext *)ctx)->in_interceptor_scope) \ CHECK_UNPOISONED_0(x, n); \ } while (0) #define MSAN_INTERCEPT_FUNC(name) \ do { \ if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \ VReport(1, "MemorySanitizer: failed to intercept '" #name "'\n"); \ } while (0) #define COMMON_INTERCEPT_FUNCTION(name) MSAN_INTERCEPT_FUNC(name) #define COMMON_INTERCEPTOR_UNPOISON_PARAM(count) \ UnpoisonParam(count) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ __msan_unpoison(ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ CHECK_UNPOISONED_CTX(ctx, ptr, size) #define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \ __msan_unpoison(ptr, size) #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ if (msan_init_is_running) return REAL(func)(__VA_ARGS__); \ MSanInterceptorContext msan_ctx = {IsInInterceptorScope()}; \ ctx = (void *)&msan_ctx; \ (void)ctx; \ InterceptorScope interceptor_scope; \ __msan_unpoison(__errno_location(), sizeof(int)); /* NOLINT */ \ ENSURE_MSAN_INITED(); +#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ + do { \ + } while (false) #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ do { \ } while (false) // FIXME #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ do { \ } while (false) // FIXME #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() -#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, map) \ - if (map) ForEachMappedRegion((link_map *)map, __msan_unpoison); +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ + do { \ + link_map *map = GET_LINK_MAP_BY_DLOPEN_HANDLE((handle)); \ + if (map) ForEachMappedRegion(map, __msan_unpoison); \ + } while (false) #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s) #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ do { \ } while (false) #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ do { \ } while (false) #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) __msan_unpoison(p, s) #include "sanitizer_common/sanitizer_common_syscalls.inc" -static void PoisonShadow(uptr ptr, uptr size, u8 value) { - uptr PageSize = GetPageSizeCached(); - uptr shadow_beg = MEM_TO_SHADOW(ptr); - uptr shadow_end = MEM_TO_SHADOW(ptr + size); - if (value || - shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { - REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); - } else { - uptr page_beg = RoundUpTo(shadow_beg, PageSize); - uptr page_end = RoundDownTo(shadow_end, PageSize); - - if (page_beg >= page_end) { - REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg); - } else { - if (page_beg != shadow_beg) { - REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg); - } - if (page_end != shadow_end) { - REAL(memset)((void *)page_end, 0, shadow_end - page_end); - } - MmapFixedNoReserve(page_beg, page_end - page_beg); - } - } -} - // These interface functions reside here so that they can use // REAL(memset), etc. void __msan_unpoison(const void *a, uptr size) { if (!MEM_IS_APP(a)) return; - PoisonShadow((uptr)a, size, 0); + SetShadow(a, size, 0); } void __msan_poison(const void *a, uptr size) { if (!MEM_IS_APP(a)) return; - PoisonShadow((uptr)a, size, - __msan::flags()->poison_heap_with_zeroes ? 0 : -1); + SetShadow(a, size, __msan::flags()->poison_heap_with_zeroes ? 0 : -1); } void __msan_poison_stack(void *a, uptr size) { if (!MEM_IS_APP(a)) return; - PoisonShadow((uptr)a, size, - __msan::flags()->poison_stack_with_zeroes ? 0 : -1); + SetShadow(a, size, __msan::flags()->poison_stack_with_zeroes ? 0 : -1); } void __msan_clear_and_unpoison(void *a, uptr size) { REAL(memset)(a, 0, size); - PoisonShadow((uptr)a, size, 0); + SetShadow(a, size, 0); } 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); ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; void *res = REAL(memcpy)(dest, src, n); - CopyPoison(dest, src, n, &stack); + CopyShadowAndOrigin(dest, src, n, &stack); return res; } void *__msan_memset(void *s, int c, SIZE_T n) { if (!msan_inited) return internal_memset(s, c, n); if (msan_init_is_running) return REAL(memset)(s, c, n); ENSURE_MSAN_INITED(); void *res = REAL(memset)(s, c, n); __msan_unpoison(s, n); return res; } void *__msan_memmove(void *dest, const void *src, SIZE_T n) { if (!msan_inited) return internal_memmove(dest, src, n); if (msan_init_is_running) return REAL(memmove)(dest, src, n); ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; void *res = REAL(memmove)(dest, src, n); - MovePoison(dest, src, n, &stack); + MoveShadowAndOrigin(dest, src, n, &stack); return res; } void __msan_unpoison_string(const char* s) { if (!MEM_IS_APP(s)) return; __msan_unpoison(s, REAL(strlen)(s) + 1); } namespace __msan { -u32 GetOriginIfPoisoned(uptr addr, uptr size) { - unsigned char *s = (unsigned char *)MEM_TO_SHADOW(addr); - for (uptr i = 0; i < size; ++i) - if (s[i]) - return *(u32 *)SHADOW_TO_ORIGIN(((uptr)s + i) & ~3UL); - return 0; -} - -void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, - u32 src_origin) { - uptr dst_s = MEM_TO_SHADOW(addr); - uptr src_s = src_shadow; - uptr src_s_end = src_s + size; - - for (; src_s < src_s_end; ++dst_s, ++src_s) - if (*(u8 *)src_s) *(u32 *)SHADOW_TO_ORIGIN(dst_s &~3UL) = src_origin; -} - -void CopyOrigin(void *dst, const void *src, uptr size, StackTrace *stack) { - if (!__msan_get_track_origins()) return; - if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; - - uptr d = (uptr)dst; - uptr beg = d & ~3UL; - // Copy left unaligned origin if that memory is poisoned. - if (beg < d) { - u32 o = GetOriginIfPoisoned((uptr)src, d - beg); - if (o) { - if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); - *(u32 *)MEM_TO_ORIGIN(beg) = o; - } - beg += 4; - } - - uptr end = (d + size) & ~3UL; - // If both ends fall into the same 4-byte slot, we are done. - if (end < beg) return; - - // Copy right unaligned origin if that memory is poisoned. - if (end < d + size) { - u32 o = GetOriginIfPoisoned((uptr)src + (end - d), (d + size) - end); - if (o) { - if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); - *(u32 *)MEM_TO_ORIGIN(end) = o; - } - } - - if (beg < end) { - // Align src up. - uptr s = ((uptr)src + 3) & ~3UL; - // FIXME: factor out to msan_copy_origin_aligned - if (__msan_get_track_origins() > 1) { - u32 *src = (u32 *)MEM_TO_ORIGIN(s); - u32 *src_s = (u32 *)MEM_TO_SHADOW(s); - u32 *src_end = (u32 *)MEM_TO_ORIGIN(s + (end - beg)); - u32 *dst = (u32 *)MEM_TO_ORIGIN(beg); - u32 src_o = 0; - u32 dst_o = 0; - for (; src < src_end; ++src, ++src_s, ++dst) { - if (!*src_s) continue; - if (*src != src_o) { - src_o = *src; - dst_o = ChainOrigin(src_o, stack); - } - *dst = dst_o; - } - } else { - REAL(memcpy)((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s), - end - beg); - } - } -} - -void MovePoison(void *dst, const void *src, uptr size, StackTrace *stack) { - if (!MEM_IS_APP(dst)) return; - if (!MEM_IS_APP(src)) return; - if (src == dst) return; - REAL(memmove)((void *)MEM_TO_SHADOW((uptr)dst), - (void *)MEM_TO_SHADOW((uptr)src), size); - CopyOrigin(dst, src, size, stack); -} - -void CopyPoison(void *dst, const void *src, uptr size, StackTrace *stack) { - if (!MEM_IS_APP(dst)) return; - if (!MEM_IS_APP(src)) return; - REAL(memcpy)((void *)MEM_TO_SHADOW((uptr)dst), - (void *)MEM_TO_SHADOW((uptr)src), size); - CopyOrigin(dst, src, size, stack); -} - void InitializeInterceptors() { static int inited = 0; CHECK_EQ(inited, 0); InitializeCommonInterceptors(); INTERCEPT_FUNCTION(mmap); MSAN_MAYBE_INTERCEPT_MMAP64; INTERCEPT_FUNCTION(posix_memalign); MSAN_MAYBE_INTERCEPT_MEMALIGN; INTERCEPT_FUNCTION(__libc_memalign); INTERCEPT_FUNCTION(valloc); MSAN_MAYBE_INTERCEPT_PVALLOC; INTERCEPT_FUNCTION(malloc); INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(realloc); INTERCEPT_FUNCTION(free); MSAN_MAYBE_INTERCEPT_CFREE; INTERCEPT_FUNCTION(malloc_usable_size); MSAN_MAYBE_INTERCEPT_MALLINFO; MSAN_MAYBE_INTERCEPT_MALLOPT; MSAN_MAYBE_INTERCEPT_MALLOC_STATS; INTERCEPT_FUNCTION(fread); MSAN_MAYBE_INTERCEPT_FREAD_UNLOCKED; INTERCEPT_FUNCTION(readlink); INTERCEPT_FUNCTION(memcpy); INTERCEPT_FUNCTION(memccpy); INTERCEPT_FUNCTION(mempcpy); INTERCEPT_FUNCTION(memset); INTERCEPT_FUNCTION(memmove); INTERCEPT_FUNCTION(bcopy); INTERCEPT_FUNCTION(wmemset); INTERCEPT_FUNCTION(wmemcpy); INTERCEPT_FUNCTION(wmempcpy); INTERCEPT_FUNCTION(wmemmove); INTERCEPT_FUNCTION(strcpy); // NOLINT INTERCEPT_FUNCTION(stpcpy); // NOLINT INTERCEPT_FUNCTION(strdup); MSAN_MAYBE_INTERCEPT___STRDUP; INTERCEPT_FUNCTION(strndup); MSAN_MAYBE_INTERCEPT___STRNDUP; INTERCEPT_FUNCTION(strncpy); // NOLINT INTERCEPT_FUNCTION(strlen); INTERCEPT_FUNCTION(strnlen); INTERCEPT_FUNCTION(gcvt); INTERCEPT_FUNCTION(strcat); // NOLINT INTERCEPT_FUNCTION(strncat); // NOLINT INTERCEPT_STRTO(strtod); INTERCEPT_STRTO(strtof); INTERCEPT_STRTO(strtold); INTERCEPT_STRTO(strtol); INTERCEPT_STRTO(strtoul); INTERCEPT_STRTO(strtoll); INTERCEPT_STRTO(strtoull); INTERCEPT_STRTO(wcstod); INTERCEPT_STRTO(wcstof); INTERCEPT_STRTO(wcstold); INTERCEPT_STRTO(wcstol); INTERCEPT_STRTO(wcstoul); INTERCEPT_STRTO(wcstoll); INTERCEPT_STRTO(wcstoull); INTERCEPT_FUNCTION(vswprintf); INTERCEPT_FUNCTION(swprintf); INTERCEPT_FUNCTION(strxfrm); INTERCEPT_FUNCTION(strxfrm_l); INTERCEPT_FUNCTION(strftime); INTERCEPT_FUNCTION(strftime_l); MSAN_MAYBE_INTERCEPT___STRFTIME_L; INTERCEPT_FUNCTION(wcsftime); INTERCEPT_FUNCTION(wcsftime_l); MSAN_MAYBE_INTERCEPT___WCSFTIME_L; INTERCEPT_FUNCTION(mbtowc); INTERCEPT_FUNCTION(mbrtowc); INTERCEPT_FUNCTION(wcslen); INTERCEPT_FUNCTION(wcschr); INTERCEPT_FUNCTION(wcscpy); INTERCEPT_FUNCTION(wcscmp); INTERCEPT_FUNCTION(getenv); INTERCEPT_FUNCTION(setenv); INTERCEPT_FUNCTION(putenv); INTERCEPT_FUNCTION(gettimeofday); INTERCEPT_FUNCTION(fcvt); MSAN_MAYBE_INTERCEPT___FXSTAT; - MSAN_MAYBE_INTERCEPT___FXSTATAT; - MSAN_MAYBE_INTERCEPT___XSTAT; + MSAN_INTERCEPT_FSTATAT; + MSAN_INTERCEPT_STAT; MSAN_MAYBE_INTERCEPT___LXSTAT; MSAN_MAYBE_INTERCEPT___FXSTAT64; MSAN_MAYBE_INTERCEPT___FXSTATAT64; MSAN_MAYBE_INTERCEPT___XSTAT64; MSAN_MAYBE_INTERCEPT___LXSTAT64; INTERCEPT_FUNCTION(pipe); INTERCEPT_FUNCTION(pipe2); INTERCEPT_FUNCTION(socketpair); INTERCEPT_FUNCTION(fgets); MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED; INTERCEPT_FUNCTION(getrlimit); MSAN_MAYBE_INTERCEPT_GETRLIMIT64; - INTERCEPT_FUNCTION(uname); + MSAN_INTERCEPT_UNAME; INTERCEPT_FUNCTION(gethostname); MSAN_MAYBE_INTERCEPT_EPOLL_WAIT; MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT; INTERCEPT_FUNCTION(recv); INTERCEPT_FUNCTION(recvfrom); INTERCEPT_FUNCTION(dladdr); INTERCEPT_FUNCTION(dlerror); INTERCEPT_FUNCTION(dl_iterate_phdr); INTERCEPT_FUNCTION(getrusage); INTERCEPT_FUNCTION(sigaction); INTERCEPT_FUNCTION(signal); INTERCEPT_FUNCTION(pthread_create); INTERCEPT_FUNCTION(pthread_key_create); INTERCEPT_FUNCTION(pthread_join); INTERCEPT_FUNCTION(tzset); INTERCEPT_FUNCTION(__cxa_atexit); INTERCEPT_FUNCTION(shmat); INTERCEPT_FUNCTION(fork); inited = 1; } } // namespace __msan Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_linux.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan_linux.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan_linux.cc (revision 279191) @@ -1,188 +1,195 @@ //===-- msan_linux.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // Linux- and FreeBSD-specific code. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX #include "msan.h" #include "msan_thread.h" #include #include #include #include #include #include #include #include #include #include #include #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_procmaps.h" namespace __msan { void ReportMapRange(const char *descr, uptr beg, uptr size) { if (size > 0) { uptr end = beg + size - 1; VPrintf(1, "%s : %p - %p\n", descr, beg, end); } } static bool CheckMemoryRangeAvailability(uptr beg, uptr size) { if (size > 0) { uptr end = beg + size - 1; if (!MemoryRangeIsAvailable(beg, end)) { Printf("FATAL: Memory range %p - %p is not available.\n", beg, end); return false; } } return true; } static bool ProtectMemoryRange(uptr beg, uptr size) { if (size > 0) { uptr end = beg + size - 1; if (!Mprotect(beg, size)) { Printf("FATAL: Cannot protect memory range %p - %p.\n", beg, end); return false; } } return true; } +static void CheckMemoryLayoutSanity() { + uptr prev_end = 0; + for (unsigned i = 0; i < kMemoryLayoutSize; ++i) { + uptr start = kMemoryLayout[i].start; + uptr end = kMemoryLayout[i].end; + MappingDesc::Type type = kMemoryLayout[i].type; + CHECK_LT(start, end); + CHECK_EQ(prev_end, start); + CHECK(addr_is_type(start, type)); + CHECK(addr_is_type((start + end) / 2, type)); + CHECK(addr_is_type(end - 1, type)); + if (type == MappingDesc::APP) { + uptr addr = start; + CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr))); + CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr))); + CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr))); + + addr = (start + end) / 2; + CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr))); + CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr))); + CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr))); + + addr = end - 1; + CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr))); + CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr))); + CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr))); + } + prev_end = end; + } +} + bool InitShadow(bool map_shadow, bool init_origins) { // Let user know mapping parameters first. VPrintf(1, "__msan_init %p\n", &__msan_init); - ReportMapRange("Low Memory ", kLowMemBeg, kLowMemSize); - ReportMapRange("Bad1 ", kBad1Beg, kBad1Size); - ReportMapRange("Shadow ", kShadowBeg, kShadowSize); - ReportMapRange("Bad2 ", kBad2Beg, kBad2Size); - ReportMapRange("Origins ", kOriginsBeg, kOriginsSize); - ReportMapRange("Bad3 ", kBad3Beg, kBad3Size); - ReportMapRange("High Memory", kHighMemBeg, kHighMemSize); + for (unsigned i = 0; i < kMemoryLayoutSize; ++i) + VPrintf(1, "%s: %zx - %zx\n", kMemoryLayout[i].name, kMemoryLayout[i].start, + kMemoryLayout[i].end - 1); - // Check mapping sanity (the invariant). - CHECK_EQ(kLowMemBeg, 0); - CHECK_EQ(kBad1Beg, kLowMemBeg + kLowMemSize); - CHECK_EQ(kShadowBeg, kBad1Beg + kBad1Size); - CHECK_GT(kShadowSize, 0); - CHECK_GE(kShadowSize, kLowMemSize + kHighMemSize); - CHECK_EQ(kBad2Beg, kShadowBeg + kShadowSize); - CHECK_EQ(kOriginsBeg, kBad2Beg + kBad2Size); - CHECK_EQ(kOriginsSize, kShadowSize); - CHECK_EQ(kBad3Beg, kOriginsBeg + kOriginsSize); - CHECK_EQ(kHighMemBeg, kBad3Beg + kBad3Size); - CHECK_GT(kHighMemSize, 0); - CHECK_GE(kHighMemBeg + kHighMemSize, kHighMemBeg); // Tests for no overflow. + CheckMemoryLayoutSanity(); - if (kLowMemSize > 0) { - CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kLowMemBeg))); - CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kLowMemBeg + kLowMemSize - 1))); - CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kLowMemBeg))); - CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kLowMemBeg + kLowMemSize - 1))); - } - CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kHighMemBeg))); - CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kHighMemBeg + kHighMemSize - 1))); - CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kHighMemBeg))); - CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kHighMemBeg + kHighMemSize - 1))); - if (!MEM_IS_APP(&__msan_init)) { Printf("FATAL: Code %p is out of application range. Non-PIE build?\n", (uptr)&__msan_init); return false; } - if (!CheckMemoryRangeAvailability(kShadowBeg, kShadowSize) || - (init_origins && - !CheckMemoryRangeAvailability(kOriginsBeg, kOriginsSize)) || - !CheckMemoryRangeAvailability(kBad1Beg, kBad1Size) || - !CheckMemoryRangeAvailability(kBad2Beg, kBad2Size) || - !CheckMemoryRangeAvailability(kBad3Beg, kBad3Size)) { - return false; + for (unsigned i = 0; i < kMemoryLayoutSize; ++i) { + uptr start = kMemoryLayout[i].start; + 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; + 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 (!ProtectMemoryRange(kBad1Beg, kBad1Size) || - !ProtectMemoryRange(kBad2Beg, kBad2Size) || - !ProtectMemoryRange(kBad3Beg, kBad3Size)) { - return false; - } - - if (map_shadow) { - void *shadow = MmapFixedNoReserve(kShadowBeg, kShadowSize); - if (shadow != (void*)kShadowBeg) return false; - } - if (init_origins) { - void *origins = MmapFixedNoReserve(kOriginsBeg, kOriginsSize); - if (origins != (void*)kOriginsBeg) return false; - } return true; } void MsanDie() { if (common_flags()->coverage) __sanitizer_cov_dump(); if (death_callback) death_callback(); - _exit(flags()->exit_code); + internal__exit(flags()->exit_code); } static void MsanAtExit(void) { if (flags()->print_stats && (flags()->atexit || msan_report_count > 0)) ReportStats(); if (msan_report_count > 0) { ReportAtExitStatistics(); if (flags()->exit_code) _exit(flags()->exit_code); } } void InstallAtExitHandler() { atexit(MsanAtExit); } // ---------------------- TSD ---------------- {{{1 static pthread_key_t tsd_key; static bool tsd_key_inited = false; + void MsanTSDInit(void (*destructor)(void *tsd)) { CHECK(!tsd_key_inited); tsd_key_inited = true; CHECK_EQ(0, pthread_key_create(&tsd_key, destructor)); } -void *MsanTSDGet() { - CHECK(tsd_key_inited); - return pthread_getspecific(tsd_key); +static THREADLOCAL MsanThread* msan_current_thread; + +MsanThread *GetCurrentThread() { + return msan_current_thread; } -void MsanTSDSet(void *tsd) { +void SetCurrentThread(MsanThread *t) { + // Make sure we do not reset the current MsanThread. + CHECK_EQ(0, msan_current_thread); + msan_current_thread = t; + // Make sure that MsanTSDDtor gets called at the end. CHECK(tsd_key_inited); - pthread_setspecific(tsd_key, tsd); + pthread_setspecific(tsd_key, (void *)t); } void MsanTSDDtor(void *tsd) { MsanThread *t = (MsanThread*)tsd; if (t->destructor_iterations_ > 1) { t->destructor_iterations_--; CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); return; } + msan_current_thread = nullptr; + // Make sure that signal handler can not see a stale current thread pointer. + atomic_signal_fence(memory_order_seq_cst); MsanThread::TSDDtor(tsd); } } // namespace __msan #endif // SANITIZER_FREEBSD || SANITIZER_LINUX Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_poisoning.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan_poisoning.cc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan_poisoning.cc (revision 279191) @@ -0,0 +1,174 @@ +//===-- msan_poisoning.cc ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +//===----------------------------------------------------------------------===// + +#include "msan_poisoning.h" + +#include "interception/interception.h" +#include "msan_origin.h" +#include "sanitizer_common/sanitizer_common.h" + +DECLARE_REAL(void *, memset, void *dest, int c, uptr n) +DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n) +DECLARE_REAL(void *, memmove, void *dest, const void *src, uptr n) + +namespace __msan { + +u32 GetOriginIfPoisoned(uptr addr, uptr size) { + unsigned char *s = (unsigned char *)MEM_TO_SHADOW(addr); + for (uptr i = 0; i < size; ++i) + if (s[i]) return *(u32 *)SHADOW_TO_ORIGIN(((uptr)s + i) & ~3UL); + return 0; +} + +void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, + u32 src_origin) { + uptr dst_s = MEM_TO_SHADOW(addr); + uptr src_s = src_shadow; + uptr src_s_end = src_s + size; + + for (; src_s < src_s_end; ++dst_s, ++src_s) + if (*(u8 *)src_s) *(u32 *)SHADOW_TO_ORIGIN(dst_s & ~3UL) = src_origin; +} + +void CopyOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack) { + if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; + + uptr d = (uptr)dst; + uptr beg = d & ~3UL; + // Copy left unaligned origin if that memory is poisoned. + if (beg < d) { + u32 o = GetOriginIfPoisoned((uptr)src, d - beg); + if (o) { + if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); + *(u32 *)MEM_TO_ORIGIN(beg) = o; + } + beg += 4; + } + + uptr end = (d + size) & ~3UL; + // If both ends fall into the same 4-byte slot, we are done. + if (end < beg) return; + + // Copy right unaligned origin if that memory is poisoned. + if (end < d + size) { + u32 o = GetOriginIfPoisoned((uptr)src + (end - d), (d + size) - end); + if (o) { + if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); + *(u32 *)MEM_TO_ORIGIN(end) = o; + } + } + + if (beg < end) { + // Align src up. + uptr s = ((uptr)src + 3) & ~3UL; + // FIXME: factor out to msan_copy_origin_aligned + if (__msan_get_track_origins() > 1) { + u32 *src = (u32 *)MEM_TO_ORIGIN(s); + u32 *src_s = (u32 *)MEM_TO_SHADOW(s); + u32 *src_end = (u32 *)MEM_TO_ORIGIN(s + (end - beg)); + u32 *dst = (u32 *)MEM_TO_ORIGIN(beg); + u32 src_o = 0; + u32 dst_o = 0; + for (; src < src_end; ++src, ++src_s, ++dst) { + if (!*src_s) continue; + if (*src != src_o) { + src_o = *src; + dst_o = ChainOrigin(src_o, stack); + } + *dst = dst_o; + } + } else { + REAL(memcpy)((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s), + end - beg); + } + } +} + +void MoveShadowAndOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack) { + if (!MEM_IS_APP(dst)) return; + if (!MEM_IS_APP(src)) return; + if (src == dst) return; + REAL(memmove)((void *)MEM_TO_SHADOW((uptr)dst), + (void *)MEM_TO_SHADOW((uptr)src), size); + if (__msan_get_track_origins()) CopyOrigin(dst, src, size, stack); +} + +void CopyShadowAndOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack) { + if (!MEM_IS_APP(dst)) return; + if (!MEM_IS_APP(src)) return; + REAL(memcpy)((void *)MEM_TO_SHADOW((uptr)dst), + (void *)MEM_TO_SHADOW((uptr)src), size); + if (__msan_get_track_origins()) CopyOrigin(dst, src, size, stack); +} + +void CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack) { + REAL(memcpy)(dst, src, size); + CopyShadowAndOrigin(dst, src, size, 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); + if (value || + shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { + REAL(memset)((void *)shadow_beg, value, shadow_end - shadow_beg); + } else { + uptr page_beg = RoundUpTo(shadow_beg, PageSize); + uptr page_end = RoundDownTo(shadow_end, PageSize); + + if (page_beg >= page_end) { + REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg); + } else { + if (page_beg != shadow_beg) { + REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg); + } + if (page_end != shadow_end) { + REAL(memset)((void *)page_end, 0, shadow_end - page_end); + } + MmapFixedNoReserve(page_beg, page_end - page_beg); + } + } +} + +void SetOrigin(const void *dst, uptr size, u32 origin) { + // Origin mapping is 4 bytes per 4 bytes of application memory. + // Here we extend the range such that its left and right bounds are both + // 4 byte aligned. + uptr x = MEM_TO_ORIGIN((uptr)dst); + uptr beg = x & ~3UL; // align down. + uptr end = (x + size + 3) & ~3UL; // align up. + u64 origin64 = ((u64)origin << 32) | origin; + // This is like memset, but the value is 32-bit. We unroll by 2 to write + // 64 bits at once. May want to unroll further to get 128-bit stores. + if (beg & 7ULL) { + *(u32 *)beg = origin; + beg += 4; + } + for (uptr addr = beg; addr < (end & ~7UL); addr += 8) *(u64 *)addr = origin64; + if (end & 7ULL) *(u32 *)(end - 4) = origin; +} + +void PoisonMemory(const void *dst, uptr size, StackTrace *stack) { + SetShadow(dst, size, (u8)-1); + + if (__msan_get_track_origins()) { + Origin o = Origin::CreateHeapOrigin(stack); + SetOrigin(dst, size, o.raw_id()); + } +} + +} // namespace __msan Property changes on: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_poisoning.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_poisoning.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan_poisoning.h (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan_poisoning.h (revision 279191) @@ -0,0 +1,59 @@ +//===-- msan_poisoning.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +//===----------------------------------------------------------------------===// + +#ifndef MSAN_POISONING_H +#define MSAN_POISONING_H + +#include "msan.h" + +namespace __msan { + +// Return origin for the first poisoned byte in the memory range, or 0. +u32 GetOriginIfPoisoned(uptr addr, uptr size); + +// Walk [addr, addr+size) app memory region, copying origin tags from the +// corresponding positions in [src_origin, src_origin+size) where the +// corresponding shadow in [src_shadow, src_shadow+size) is non-zero. +void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, u32 src_origin); + +// Copy origin from src (app address) to dst (app address), creating chained +// origin ids as necessary, without overriding origin for fully initialized +// quads. +void CopyOrigin(const void *dst, const void *src, uptr size, StackTrace *stack); + +// memmove() shadow and origin. Dst and src are application addresses. +// See CopyOrigin() for the origin copying logic. +void MoveShadowAndOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack); + +// memcpy() shadow and origin. Dst and src are application addresses. +// See CopyOrigin() for the origin copying logic. +void CopyShadowAndOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack); + +// memcpy() app memory, and do "the right thing" to the corresponding shadow and +// origin regions. +void CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack); + +// Fill shadow will value. Ptr is an application address. +void SetShadow(const void *ptr, uptr size, u8 value); + +// Set origin for the memory region. +void SetOrigin(const void *dst, uptr size, u32 origin); + +// Mark memory region uninitialized, with origins. +void PoisonMemory(const void *dst, uptr size, StackTrace *stack); + +} // namespace __msan + +#endif // MSAN_POISONING_H Property changes on: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_poisoning.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_report.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan_report.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan_report.cc (revision 279191) @@ -1,262 +1,277 @@ //===-- msan_report.cc ----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // Error reporting. //===----------------------------------------------------------------------===// #include "msan.h" #include "msan_chained_origin_depot.h" #include "msan_origin.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" using namespace __sanitizer; namespace __msan { class Decorator: public __sanitizer::SanitizerCommonDecorator { public: Decorator() : SanitizerCommonDecorator() { } const char *Warning() { return Red(); } const char *Origin() { return Magenta(); } const char *Name() { return Green(); } const char *End() { return Default(); } }; static void DescribeStackOrigin(const char *so, uptr pc) { Decorator d; char *s = internal_strdup(so); char *sep = internal_strchr(s, '@'); CHECK(sep); *sep = '\0'; Printf("%s", d.Origin()); Printf( " %sUninitialized value was created by an allocation of '%s%s%s'" " in the stack frame of function '%s%s%s'%s\n", d.Origin(), d.Name(), s, d.Origin(), d.Name(), sep + 1, d.Origin(), d.End()); InternalFree(s); if (pc) { // For some reason function address in LLVM IR is 1 less then the address // of the first instruction. pc = StackTrace::GetNextInstructionPc(pc); StackTrace(&pc, 1).Print(); } } static void DescribeOrigin(u32 id) { VPrintf(1, " raw origin id: %d\n", id); Decorator d; Origin o = Origin::FromRawId(id); while (o.isChainedOrigin()) { StackTrace stack; o = o.getNextChainedOrigin(&stack); Printf(" %sUninitialized value was stored to memory at%s\n", d.Origin(), d.End()); stack.Print(); } if (o.isStackOrigin()) { uptr pc; const char *so = GetStackOriginDescr(o.getStackId(), &pc); DescribeStackOrigin(so, pc); } else { StackTrace stack = o.getStackTraceForHeapOrigin(); - Printf(" %sUninitialized value was created by a heap allocation%s\n", - d.Origin(), d.End()); + switch (stack.tag) { + case StackTrace::TAG_ALLOC: + Printf(" %sUninitialized value was created by a heap allocation%s\n", + d.Origin(), d.End()); + break; + case StackTrace::TAG_DEALLOC: + Printf(" %sUninitialized value was created by a heap deallocation%s\n", + d.Origin(), d.End()); + break; + case STACK_TRACE_TAG_POISON: + Printf(" %sMemory was marked as uninitialized%s\n", d.Origin(), + d.End()); + break; + default: + Printf(" %sUninitialized value was created%s\n", d.Origin(), d.End()); + break; + } stack.Print(); } } void ReportUMR(StackTrace *stack, u32 origin) { if (!__msan::flags()->report_umrs) return; SpinMutexLock l(&CommonSanitizerReportMutex); Decorator d; Printf("%s", d.Warning()); Report(" WARNING: MemorySanitizer: use-of-uninitialized-value\n"); Printf("%s", d.End()); stack->Print(); if (origin) { DescribeOrigin(origin); } ReportErrorSummary("use-of-uninitialized-value", stack); } void ReportExpectedUMRNotFound(StackTrace *stack) { SpinMutexLock l(&CommonSanitizerReportMutex); Printf(" WARNING: Expected use of uninitialized value not found\n"); stack->Print(); } void ReportStats() { SpinMutexLock l(&CommonSanitizerReportMutex); if (__msan_get_track_origins() > 0) { StackDepotStats *stack_depot_stats = StackDepotGetStats(); // FIXME: we want this at normal exit, too! // FIXME: but only with verbosity=1 or something Printf("Unique heap origins: %zu\n", stack_depot_stats->n_uniq_ids); Printf("Stack depot allocated bytes: %zu\n", stack_depot_stats->allocated); StackDepotStats *chained_origin_depot_stats = ChainedOriginDepotGetStats(); Printf("Unique origin histories: %zu\n", chained_origin_depot_stats->n_uniq_ids); Printf("History depot allocated bytes: %zu\n", chained_origin_depot_stats->allocated); } } void ReportAtExitStatistics() { SpinMutexLock l(&CommonSanitizerReportMutex); if (msan_report_count > 0) { Decorator d; Printf("%s", d.Warning()); Printf("MemorySanitizer: %d warnings reported.\n", msan_report_count); Printf("%s", d.End()); } } class OriginSet { public: OriginSet() : next_id_(0) {} int insert(u32 o) { // Scan from the end for better locality. for (int i = next_id_ - 1; i >= 0; --i) if (origins_[i] == o) return i; if (next_id_ == kMaxSize_) return OVERFLOW; int id = next_id_++; origins_[id] = o; return id; } int size() { return next_id_; } u32 get(int id) { return origins_[id]; } static char asChar(int id) { switch (id) { case MISSING: return '.'; case OVERFLOW: return '*'; default: return 'A' + id; } } static const int OVERFLOW = -1; static const int MISSING = -2; private: static const int kMaxSize_ = 'Z' - 'A' + 1; u32 origins_[kMaxSize_]; int next_id_; }; void DescribeMemoryRange(const void *x, uptr size) { // Real limits. uptr start = MEM_TO_SHADOW(x); uptr end = start + size; // Scan limits: align start down to 4; align size up to 16. uptr s = start & ~3UL; size = end - s; size = (size + 15) & ~15UL; uptr e = s + size; // Single letter names to origin id mapping. OriginSet origin_set; uptr pos = 0; // Offset from aligned start. bool with_origins = __msan_get_track_origins(); // True if there is at least 1 poisoned bit in the last 4-byte group. bool last_quad_poisoned; int origin_ids[4]; // Single letter origin ids for the current line. Decorator d; Printf("%s", d.Warning()); Printf("Shadow map of [%p, %p), %zu bytes:\n", start, end, end - start); Printf("%s", d.End()); while (s < e) { // Line start. if (pos % 16 == 0) { for (int i = 0; i < 4; ++i) origin_ids[i] = -1; Printf("%p:", s); } // Group start. if (pos % 4 == 0) { Printf(" "); last_quad_poisoned = false; } // Print shadow byte. if (s < start || s >= end) { Printf(".."); } else { unsigned char v = *(unsigned char *)s; if (v) last_quad_poisoned = true; #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ Printf("%x%x", v & 0xf, v >> 4); #else Printf("%x%x", v >> 4, v & 0xf); #endif } // Group end. if (pos % 4 == 3 && with_origins) { int id = OriginSet::MISSING; if (last_quad_poisoned) { u32 o = *(u32 *)SHADOW_TO_ORIGIN(s - 3); id = origin_set.insert(o); } origin_ids[(pos % 16) / 4] = id; } // Line end. if (pos % 16 == 15) { if (with_origins) { Printf(" |"); for (int i = 0; i < 4; ++i) { char c = OriginSet::asChar(origin_ids[i]); Printf("%c", c); if (i != 3) Printf(" "); } Printf("|"); } Printf("\n"); } size--; s++; pos++; } Printf("\n"); for (int i = 0; i < origin_set.size(); ++i) { u32 o = origin_set.get(i); Printf("Origin %c (origin_id %x):\n", OriginSet::asChar(i), o); DescribeOrigin(o); } } void ReportUMRInsideAddressRange(const char *what, const void *start, uptr size, uptr offset) { Decorator d; Printf("%s", d.Warning()); Printf("%sUninitialized bytes in %s%s%s at offset %zu inside [%p, %zu)%s\n", d.Warning(), d.Name(), what, d.Warning(), offset, start, size, d.End()); - if (__sanitizer::common_flags()->verbosity > 0) + if (__sanitizer::Verbosity()) DescribeMemoryRange(start, size); } } // namespace __msan Index: projects/clang360-import/contrib/compiler-rt/lib/msan/msan_thread.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/msan_thread.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/msan_thread.cc (revision 279191) @@ -1,93 +1,82 @@ #include "msan.h" #include "msan_thread.h" #include "msan_interface_internal.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" namespace __msan { MsanThread *MsanThread::Create(thread_callback_t start_routine, void *arg) { uptr PageSize = GetPageSizeCached(); uptr size = RoundUpTo(sizeof(MsanThread), PageSize); MsanThread *thread = (MsanThread*)MmapOrDie(size, __func__); thread->start_routine_ = start_routine; thread->arg_ = arg; thread->destructor_iterations_ = kPthreadDestructorIterations; return thread; } void MsanThread::SetThreadStackAndTls() { uptr tls_size = 0; uptr stack_size = 0; GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, &tls_begin_, &tls_size); stack_top_ = stack_bottom_ + stack_size; tls_end_ = tls_begin_ + tls_size; int local; CHECK(AddrIsInStack((uptr)&local)); } void MsanThread::ClearShadowForThreadStackAndTLS() { __msan_unpoison((void *)stack_bottom_, stack_top_ - stack_bottom_); if (tls_begin_ != tls_end_) __msan_unpoison((void *)tls_begin_, tls_end_ - tls_begin_); DTLS *dtls = DTLS_Get(); CHECK_NE(dtls, 0); for (uptr i = 0; i < dtls->dtv_size; ++i) __msan_unpoison((void *)(dtls->dtv[i].beg), dtls->dtv[i].size); } void MsanThread::Init() { SetThreadStackAndTls(); CHECK(MEM_IS_APP(stack_bottom_)); CHECK(MEM_IS_APP(stack_top_ - 1)); ClearShadowForThreadStackAndTLS(); } void MsanThread::TSDDtor(void *tsd) { MsanThread *t = (MsanThread*)tsd; t->Destroy(); } void MsanThread::Destroy() { malloc_storage().CommitBack(); // We also clear the shadow on thread destruction because // some code may still be executing in later TSD destructors // and we don't want it to have any poisoned stack. ClearShadowForThreadStackAndTLS(); uptr size = RoundUpTo(sizeof(MsanThread), GetPageSizeCached()); UnmapOrDie(this, size); DTLS_Destroy(); } thread_return_t MsanThread::ThreadStart() { Init(); if (!start_routine_) { // start_routine_ == 0 if we're on the main thread or on one of the // OS X libdispatch worker threads. But nobody is supposed to call // ThreadStart() for the worker threads. return 0; } thread_return_t res = start_routine_(arg_); return res; } -MsanThread *GetCurrentThread() { - return reinterpret_cast(MsanTSDGet()); -} - -void SetCurrentThread(MsanThread *t) { - // Make sure we do not reset the current MsanThread. - CHECK_EQ(0, MsanTSDGet()); - MsanTSDSet(t); - CHECK_EQ(t, MsanTSDGet()); -} - } // namespace __msan Index: projects/clang360-import/contrib/compiler-rt/lib/msan/tests/msan_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/msan/tests/msan_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/msan/tests/msan_test.cc (revision 279191) @@ -1,4143 +1,4247 @@ //===-- msan_test.cc ------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // MemorySanitizer unit tests. //===----------------------------------------------------------------------===// #ifndef MSAN_EXTERNAL_TEST_CONFIG #include "msan_test_config.h" #endif // MSAN_EXTERNAL_TEST_CONFIG #include "sanitizer_common/tests/sanitizer_test_utils.h" #include "sanitizer/allocator_interface.h" #include "sanitizer/msan_interface.h" +#if defined(__FreeBSD__) +# define _KERNEL // To declare 'shminfo' structure. +# include +# undef _KERNEL +extern "C" { +// doesn't declare these functions in _KERNEL mode. +void *shmat(int, const void *, int); +int shmget(key_t, size_t, int); +int shmctl(int, int, struct shmid_ds *); +int shmdt(const void *); +} +#endif + #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include -#include #include #include #include #include #include -#include -#include #include #include +#if !defined(__FreeBSD__) +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +# define f_namelen f_namemax // FreeBSD names this statfs field so. +# define cpu_set_t cpuset_t +#endif + #if defined(__i386__) || defined(__x86_64__) # include # define MSAN_HAS_M128 1 #else # define MSAN_HAS_M128 0 #endif #ifdef __AVX2__ # include #endif +// On FreeBSD procfs is not enabled by default. +#if defined(__FreeBSD__) +# define FILE_TO_READ "/bin/cat" +# define DIR_TO_READ "/bin" +# define SUBFILE_TO_READ "cat" +# define SYMLINK_TO_READ "/usr/bin/tar" +#else +# define FILE_TO_READ "/proc/self/stat" +# define DIR_TO_READ "/proc/self" +# define SUBFILE_TO_READ "stat" +# define SYMLINK_TO_READ "/proc/self/exe" +#endif + static const size_t kPageSize = 4096; typedef unsigned char U1; typedef unsigned short U2; // NOLINT typedef unsigned int U4; typedef unsigned long long U8; // NOLINT typedef signed char S1; typedef signed short S2; // NOLINT typedef signed int S4; typedef signed long long S8; // NOLINT #define NOINLINE __attribute__((noinline)) #define INLINE __attribute__((always_inline)) static bool TrackingOrigins() { S8 x; __msan_set_origin(&x, sizeof(x), 0x1234); U4 origin = __msan_get_origin(&x); __msan_set_origin(&x, sizeof(x), 0); return origin == 0x1234; } #define EXPECT_UMR(action) \ do { \ __msan_set_expect_umr(1); \ action; \ __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_POISONED(x) ExpectPoisoned(x) template void ExpectPoisoned(const T& t) { EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t))); } #define EXPECT_POISONED_O(x, origin) \ ExpectPoisonedWithOrigin(x, origin) template 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)); } #define EXPECT_NOT_POISONED(x) ExpectNotPoisoned(x) template void ExpectNotPoisoned(const T& t) { EXPECT_EQ(-1, __msan_test_shadow((void*)&t, sizeof(t))); } static U8 poisoned_array[100]; template T *GetPoisoned(int i = 0, T val = 0) { T *res = (T*)&poisoned_array[i]; *res = val; __msan_poison(&poisoned_array[i], sizeof(T)); return res; } template T *GetPoisonedO(int i, U4 origin, T val = 0) { T *res = (T*)&poisoned_array[i]; *res = val; __msan_poison(&poisoned_array[i], sizeof(T)); __msan_set_origin(&poisoned_array[i], sizeof(T), origin); return res; } template T Poisoned(T v = 0, T s = (T)(-1)) { __msan_partial_poison(&v, &s, sizeof(T)); return v; } template NOINLINE T ReturnPoisoned() { return *GetPoisoned(); } static volatile int g_one = 1; static volatile int g_zero = 0; static volatile int g_0 = 0; static volatile int g_1 = 1; S4 a_s4[100]; S8 a_s8[100]; // Check that malloc poisons memory. // A lot of tests below depend on this. TEST(MemorySanitizerSanity, PoisonInMalloc) { int *x = (int*)malloc(sizeof(int)); EXPECT_POISONED(*x); free(x); } TEST(MemorySanitizer, NegativeTest1) { S4 *x = GetPoisoned(); if (g_one) *x = 0; EXPECT_NOT_POISONED(*x); } TEST(MemorySanitizer, PositiveTest1) { // Load to store. EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); // S->S conversions. EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); // ZExt EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); // Unary ops. EXPECT_POISONED(- *GetPoisoned()); EXPECT_UMR(a_s4[g_zero] = 100 / *GetPoisoned(0, 1)); a_s4[g_zero] = 1 - *GetPoisoned(); a_s4[g_zero] = 1 + *GetPoisoned(); } TEST(MemorySanitizer, Phi1) { S4 c; if (g_one) { c = *GetPoisoned(); } else { break_optimization(0); c = 0; } EXPECT_POISONED(c); } TEST(MemorySanitizer, Phi2) { S4 i = *GetPoisoned(); S4 n = g_one; EXPECT_UMR(for (; i < g_one; i++);); EXPECT_POISONED(i); } NOINLINE void Arg1ExpectUMR(S4 a1) { EXPECT_POISONED(a1); } NOINLINE void Arg2ExpectUMR(S4 a1, S4 a2) { EXPECT_POISONED(a2); } NOINLINE void Arg3ExpectUMR(S1 a1, S4 a2, S8 a3) { EXPECT_POISONED(a3); } TEST(MemorySanitizer, ArgTest) { Arg1ExpectUMR(*GetPoisoned()); Arg2ExpectUMR(0, *GetPoisoned()); Arg3ExpectUMR(0, 1, *GetPoisoned()); } TEST(MemorySanitizer, CallAndRet) { ReturnPoisoned(); ReturnPoisoned(); ReturnPoisoned(); ReturnPoisoned(); EXPECT_POISONED(ReturnPoisoned()); EXPECT_POISONED(ReturnPoisoned()); EXPECT_POISONED(ReturnPoisoned()); EXPECT_POISONED(ReturnPoisoned()); } // malloc() in the following test may be optimized to produce a compile-time // undef value. Check that we trap on the volatile assignment anyway. TEST(MemorySanitizer, DISABLED_MallocNoIdent) { S4 *x = (int*)malloc(sizeof(S4)); EXPECT_POISONED(*x); free(x); } TEST(MemorySanitizer, Malloc) { S4 *x = (int*)Ident(malloc(sizeof(S4))); EXPECT_POISONED(*x); free(x); } TEST(MemorySanitizer, Realloc) { S4 *x = (int*)Ident(realloc(0, sizeof(S4))); EXPECT_POISONED(x[0]); x[0] = 1; x = (int*)Ident(realloc(x, 2 * sizeof(S4))); EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. EXPECT_POISONED(x[1]); x = (int*)Ident(realloc(x, 3 * sizeof(S4))); EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. EXPECT_POISONED(x[2]); EXPECT_POISONED(x[1]); x[2] = 1; // Init this here. Check that after realloc it is poisoned again. x = (int*)Ident(realloc(x, 2 * sizeof(S4))); EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. EXPECT_POISONED(x[1]); x = (int*)Ident(realloc(x, 3 * sizeof(S4))); EXPECT_POISONED(x[1]); EXPECT_POISONED(x[2]); free(x); } TEST(MemorySanitizer, Calloc) { S4 *x = (int*)Ident(calloc(1, sizeof(S4))); EXPECT_NOT_POISONED(*x); // Should not be poisoned. EXPECT_EQ(0, *x); free(x); } TEST(MemorySanitizer, CallocReturnsZeroMem) { size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; for (size_t s = 0; s < sizeof(sizes)/sizeof(sizes[0]); s++) { size_t size = sizes[s]; for (size_t iter = 0; iter < 5; iter++) { char *x = Ident((char*)calloc(1, size)); EXPECT_EQ(x[0], 0); EXPECT_EQ(x[size - 1], 0); EXPECT_EQ(x[size / 2], 0); EXPECT_EQ(x[size / 3], 0); EXPECT_EQ(x[size / 4], 0); memset(x, 0x42, size); free(Ident(x)); } } } TEST(MemorySanitizer, AndOr) { U4 *p = GetPoisoned(); // We poison two bytes in the midle of a 4-byte word to make the test // correct regardless of endianness. ((U1*)p)[1] = 0; ((U1*)p)[2] = 0xff; EXPECT_NOT_POISONED(*p & 0x00ffff00); EXPECT_NOT_POISONED(*p & 0x00ff0000); EXPECT_NOT_POISONED(*p & 0x0000ff00); EXPECT_POISONED(*p & 0xff000000); EXPECT_POISONED(*p & 0x000000ff); EXPECT_POISONED(*p & 0x0000ffff); EXPECT_POISONED(*p & 0xffff0000); EXPECT_NOT_POISONED(*p | 0xff0000ff); EXPECT_NOT_POISONED(*p | 0xff00ffff); EXPECT_NOT_POISONED(*p | 0xffff00ff); EXPECT_POISONED(*p | 0xff000000); EXPECT_POISONED(*p | 0x000000ff); EXPECT_POISONED(*p | 0x0000ffff); EXPECT_POISONED(*p | 0xffff0000); EXPECT_POISONED(*GetPoisoned() & *GetPoisoned()); } template static bool applyNot(T value, T shadow) { __msan_partial_poison(&value, &shadow, sizeof(T)); return !value; } TEST(MemorySanitizer, Not) { EXPECT_NOT_POISONED(applyNot(0x0, 0x0)); EXPECT_NOT_POISONED(applyNot(0xFFFFFFFF, 0x0)); EXPECT_POISONED(applyNot(0xFFFFFFFF, 0xFFFFFFFF)); EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x0FFFFFFF)); EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x00FFFFFF)); EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x0000FFFF)); EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x00000000)); EXPECT_POISONED(applyNot(0xFF000000, 0xFF000000)); EXPECT_NOT_POISONED(applyNot(0xFF800000, 0xFF000000)); EXPECT_POISONED(applyNot(0x00008000, 0x00008000)); EXPECT_NOT_POISONED(applyNot(0x0, 0x0)); EXPECT_NOT_POISONED(applyNot(0xFF, 0xFE)); EXPECT_NOT_POISONED(applyNot(0xFF, 0x0)); EXPECT_POISONED(applyNot(0xFF, 0xFF)); EXPECT_POISONED(applyNot((void*)0xFFFFFF, (void*)(-1))); EXPECT_NOT_POISONED(applyNot((void*)0xFFFFFF, (void*)(-2))); } TEST(MemorySanitizer, Shift) { U4 *up = GetPoisoned(); ((U1*)up)[0] = 0; ((U1*)up)[3] = 0xff; EXPECT_NOT_POISONED(*up >> 30); EXPECT_NOT_POISONED(*up >> 24); EXPECT_POISONED(*up >> 23); EXPECT_POISONED(*up >> 10); EXPECT_NOT_POISONED(*up << 30); EXPECT_NOT_POISONED(*up << 24); EXPECT_POISONED(*up << 23); EXPECT_POISONED(*up << 10); S4 *sp = (S4*)up; EXPECT_NOT_POISONED(*sp >> 30); EXPECT_NOT_POISONED(*sp >> 24); EXPECT_POISONED(*sp >> 23); EXPECT_POISONED(*sp >> 10); sp = GetPoisoned(); ((S1*)sp)[1] = 0; ((S1*)sp)[2] = 0; EXPECT_POISONED(*sp >> 31); EXPECT_POISONED(100 >> *GetPoisoned()); EXPECT_POISONED(100U >> *GetPoisoned()); } NOINLINE static int GetPoisonedZero() { int *zero = new int; *zero = 0; __msan_poison(zero, sizeof(*zero)); int res = *zero; delete zero; return res; } TEST(MemorySanitizer, LoadFromDirtyAddress) { int *a = new int; *a = 0; EXPECT_UMR(break_optimization((void*)(U8)a[GetPoisonedZero()])); delete a; } TEST(MemorySanitizer, StoreToDirtyAddress) { int *a = new int; EXPECT_UMR(a[GetPoisonedZero()] = 0); break_optimization(a); delete a; } NOINLINE void StackTestFunc() { S4 p4; S4 ok4 = 1; S2 p2; S2 ok2 = 1; S1 p1; S1 ok1 = 1; break_optimization(&p4); break_optimization(&ok4); break_optimization(&p2); break_optimization(&ok2); break_optimization(&p1); break_optimization(&ok1); EXPECT_POISONED(p4); EXPECT_POISONED(p2); EXPECT_POISONED(p1); EXPECT_NOT_POISONED(ok1); EXPECT_NOT_POISONED(ok2); EXPECT_NOT_POISONED(ok4); } TEST(MemorySanitizer, StackTest) { StackTestFunc(); } NOINLINE void StackStressFunc() { int foo[10000]; break_optimization(foo); } TEST(MemorySanitizer, DISABLED_StackStressTest) { for (int i = 0; i < 1000000; i++) StackStressFunc(); } template void TestFloatingPoint() { static volatile T v; static T g[100]; break_optimization(&g); T *x = GetPoisoned(); T *y = GetPoisoned(1); EXPECT_POISONED(*x); EXPECT_POISONED((long long)*x); EXPECT_POISONED((int)*x); g[0] = *x; g[1] = *x + *y; g[2] = *x - *y; g[3] = *x * *y; } TEST(MemorySanitizer, FloatingPointTest) { TestFloatingPoint(); TestFloatingPoint(); } TEST(MemorySanitizer, DynMem) { S4 x = 0; S4 *y = GetPoisoned(); memcpy(y, &x, g_one * sizeof(S4)); EXPECT_NOT_POISONED(*y); } static char *DynRetTestStr; TEST(MemorySanitizer, DynRet) { ReturnPoisoned(); - EXPECT_NOT_POISONED(clearenv()); + EXPECT_NOT_POISONED(atoi("0")); } - TEST(MemorySanitizer, DynRet1) { ReturnPoisoned(); } struct LargeStruct { S4 x[10]; }; NOINLINE LargeStruct LargeRetTest() { LargeStruct res; res.x[0] = *GetPoisoned(); res.x[1] = *GetPoisoned(); res.x[2] = *GetPoisoned(); res.x[3] = *GetPoisoned(); res.x[4] = *GetPoisoned(); res.x[5] = *GetPoisoned(); res.x[6] = *GetPoisoned(); res.x[7] = *GetPoisoned(); res.x[8] = *GetPoisoned(); res.x[9] = *GetPoisoned(); return res; } TEST(MemorySanitizer, strcmp) { char s1[10]; char s2[10]; strncpy(s1, "foo", 10); s2[0] = 'f'; s2[1] = 'n'; EXPECT_GT(strcmp(s1, s2), 0); s2[1] = 'o'; int res; EXPECT_UMR(res = strcmp(s1, s2)); EXPECT_NOT_POISONED(res); EXPECT_EQ(strncmp(s1, s2, 1), 0); } TEST(MemorySanitizer, LargeRet) { LargeStruct a = LargeRetTest(); EXPECT_POISONED(a.x[0]); EXPECT_POISONED(a.x[9]); } TEST(MemorySanitizer, strerror) { char *buf = strerror(EINVAL); EXPECT_NOT_POISONED(strlen(buf)); buf = strerror(123456); EXPECT_NOT_POISONED(strlen(buf)); } TEST(MemorySanitizer, strerror_r) { errno = 0; char buf[1000]; - char *res = strerror_r(EINVAL, buf, sizeof(buf)); + char *res = (char*) (size_t) strerror_r(EINVAL, buf, sizeof(buf)); ASSERT_EQ(0, errno); if (!res) res = buf; // POSIX version success. EXPECT_NOT_POISONED(strlen(res)); } TEST(MemorySanitizer, fread) { char *x = new char[32]; - FILE *f = fopen("/proc/self/stat", "r"); + FILE *f = fopen(FILE_TO_READ, "r"); ASSERT_TRUE(f != NULL); fread(x, 1, 32, f); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[16]); EXPECT_NOT_POISONED(x[31]); fclose(f); delete[] x; } TEST(MemorySanitizer, read) { char *x = new char[32]; - int fd = open("/proc/self/stat", O_RDONLY); + int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = read(fd, x, 32); ASSERT_EQ(sz, 32); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[16]); EXPECT_NOT_POISONED(x[31]); close(fd); delete[] x; } TEST(MemorySanitizer, pread) { char *x = new char[32]; - int fd = open("/proc/self/stat", O_RDONLY); + int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = pread(fd, x, 32, 0); ASSERT_EQ(sz, 32); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[16]); EXPECT_NOT_POISONED(x[31]); close(fd); delete[] x; } TEST(MemorySanitizer, readv) { char buf[2011]; struct iovec iov[2]; iov[0].iov_base = buf + 1; iov[0].iov_len = 5; iov[1].iov_base = buf + 10; iov[1].iov_len = 2000; - int fd = open("/proc/self/stat", O_RDONLY); + int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = readv(fd, iov, 2); ASSERT_GE(sz, 0); - ASSERT_LT(sz, 5 + 2000); + ASSERT_LE(sz, 5 + 2000); ASSERT_GT((size_t)sz, iov[0].iov_len); EXPECT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[1]); EXPECT_NOT_POISONED(buf[5]); EXPECT_POISONED(buf[6]); EXPECT_POISONED(buf[9]); EXPECT_NOT_POISONED(buf[10]); EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); EXPECT_POISONED(buf[11 + (sz - 1) - 5]); close(fd); } TEST(MemorySanitizer, preadv) { char buf[2011]; struct iovec iov[2]; iov[0].iov_base = buf + 1; iov[0].iov_len = 5; iov[1].iov_base = buf + 10; iov[1].iov_len = 2000; - int fd = open("/proc/self/stat", O_RDONLY); + int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = preadv(fd, iov, 2, 3); ASSERT_GE(sz, 0); - ASSERT_LT(sz, 5 + 2000); + ASSERT_LE(sz, 5 + 2000); ASSERT_GT((size_t)sz, iov[0].iov_len); EXPECT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[1]); EXPECT_NOT_POISONED(buf[5]); EXPECT_POISONED(buf[6]); EXPECT_POISONED(buf[9]); EXPECT_NOT_POISONED(buf[10]); EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); EXPECT_POISONED(buf[11 + (sz - 1) - 5]); close(fd); } // FIXME: fails now. TEST(MemorySanitizer, DISABLED_ioctl) { struct winsize ws; EXPECT_EQ(ioctl(2, TIOCGWINSZ, &ws), 0); EXPECT_NOT_POISONED(ws.ws_col); } TEST(MemorySanitizer, readlink) { char *x = new char[1000]; - readlink("/proc/self/exe", x, 1000); + readlink(SYMLINK_TO_READ, x, 1000); EXPECT_NOT_POISONED(x[0]); delete [] x; } - TEST(MemorySanitizer, stat) { struct stat* st = new struct stat; - int res = stat("/proc/self/stat", st); + int res = stat(FILE_TO_READ, st); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st->st_dev); EXPECT_NOT_POISONED(st->st_mode); EXPECT_NOT_POISONED(st->st_size); } TEST(MemorySanitizer, fstatat) { struct stat* st = new struct stat; - int dirfd = open("/proc/self", O_RDONLY); + int dirfd = open(DIR_TO_READ, O_RDONLY); ASSERT_GT(dirfd, 0); - int res = fstatat(dirfd, "stat", st, 0); + int res = fstatat(dirfd, SUBFILE_TO_READ, st, 0); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st->st_dev); EXPECT_NOT_POISONED(st->st_mode); EXPECT_NOT_POISONED(st->st_size); close(dirfd); } TEST(MemorySanitizer, statfs) { struct statfs st; int res = statfs("/", &st); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st.f_type); EXPECT_NOT_POISONED(st.f_bfree); EXPECT_NOT_POISONED(st.f_namelen); } TEST(MemorySanitizer, statvfs) { struct statvfs st; int res = statvfs("/", &st); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st.f_bsize); EXPECT_NOT_POISONED(st.f_blocks); EXPECT_NOT_POISONED(st.f_bfree); EXPECT_NOT_POISONED(st.f_namemax); } TEST(MemorySanitizer, fstatvfs) { struct statvfs st; int fd = open("/", O_RDONLY | O_DIRECTORY); int res = fstatvfs(fd, &st); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st.f_bsize); EXPECT_NOT_POISONED(st.f_blocks); EXPECT_NOT_POISONED(st.f_bfree); EXPECT_NOT_POISONED(st.f_namemax); close(fd); } TEST(MemorySanitizer, pipe) { int* pipefd = new int[2]; int res = pipe(pipefd); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pipefd[0]); EXPECT_NOT_POISONED(pipefd[1]); close(pipefd[0]); close(pipefd[1]); } TEST(MemorySanitizer, pipe2) { int* pipefd = new int[2]; int res = pipe2(pipefd, O_NONBLOCK); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pipefd[0]); EXPECT_NOT_POISONED(pipefd[1]); close(pipefd[0]); close(pipefd[1]); } TEST(MemorySanitizer, socketpair) { int sv[2]; int res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(sv[0]); EXPECT_NOT_POISONED(sv[1]); close(sv[0]); close(sv[1]); } TEST(MemorySanitizer, poll) { int* pipefd = new int[2]; int res = pipe(pipefd); ASSERT_EQ(0, res); char data = 42; res = write(pipefd[1], &data, 1); ASSERT_EQ(1, res); pollfd fds[2]; fds[0].fd = pipefd[0]; fds[0].events = POLLIN; fds[1].fd = pipefd[1]; fds[1].events = POLLIN; res = poll(fds, 2, 500); ASSERT_EQ(1, res); EXPECT_NOT_POISONED(fds[0].revents); EXPECT_NOT_POISONED(fds[1].revents); close(pipefd[0]); close(pipefd[1]); } +// There is no ppoll() on FreeBSD. +#if !defined (__FreeBSD__) TEST(MemorySanitizer, ppoll) { int* pipefd = new int[2]; int res = pipe(pipefd); ASSERT_EQ(0, res); char data = 42; res = write(pipefd[1], &data, 1); ASSERT_EQ(1, res); pollfd fds[2]; fds[0].fd = pipefd[0]; fds[0].events = POLLIN; fds[1].fd = pipefd[1]; fds[1].events = POLLIN; sigset_t ss; sigemptyset(&ss); res = ppoll(fds, 2, NULL, &ss); ASSERT_EQ(1, res); EXPECT_NOT_POISONED(fds[0].revents); EXPECT_NOT_POISONED(fds[1].revents); close(pipefd[0]); close(pipefd[1]); } +#endif TEST(MemorySanitizer, poll_positive) { int* pipefd = new int[2]; int res = pipe(pipefd); ASSERT_EQ(0, res); pollfd fds[2]; fds[0].fd = pipefd[0]; fds[0].events = POLLIN; // fds[1].fd uninitialized fds[1].events = POLLIN; EXPECT_UMR(poll(fds, 2, 0)); close(pipefd[0]); close(pipefd[1]); } TEST(MemorySanitizer, bind_getsockname) { int sock = socket(AF_UNIX, SOCK_STREAM, 0); struct sockaddr_in sai; memset(&sai, 0, sizeof(sai)); sai.sin_family = AF_UNIX; int res = bind(sock, (struct sockaddr *)&sai, sizeof(sai)); ASSERT_EQ(0, res); char buf[200]; socklen_t addrlen; EXPECT_UMR(getsockname(sock, (struct sockaddr *)&buf, &addrlen)); addrlen = sizeof(buf); res = getsockname(sock, (struct sockaddr *)&buf, &addrlen); EXPECT_NOT_POISONED(addrlen); EXPECT_NOT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[addrlen - 1]); EXPECT_POISONED(buf[addrlen]); close(sock); } TEST(MemorySanitizer, accept) { int listen_socket = socket(AF_INET, SOCK_STREAM, 0); ASSERT_LT(0, listen_socket); struct sockaddr_in sai; memset(&sai, 0, sizeof(sai)); sai.sin_family = AF_INET; sai.sin_port = 0; sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); int res = bind(listen_socket, (struct sockaddr *)&sai, sizeof(sai)); ASSERT_EQ(0, res); res = listen(listen_socket, 1); ASSERT_EQ(0, res); socklen_t sz = sizeof(sai); res = getsockname(listen_socket, (struct sockaddr *)&sai, &sz); ASSERT_EQ(0, res); ASSERT_EQ(sizeof(sai), sz); int connect_socket = socket(AF_INET, SOCK_STREAM, 0); ASSERT_LT(0, connect_socket); res = fcntl(connect_socket, F_SETFL, O_NONBLOCK); ASSERT_EQ(0, res); res = connect(connect_socket, (struct sockaddr *)&sai, sizeof(sai)); - ASSERT_EQ(-1, res); - ASSERT_EQ(EINPROGRESS, errno); + // On FreeBSD this connection completes immediately. + if (res != 0) { + ASSERT_EQ(-1, res); + ASSERT_EQ(EINPROGRESS, errno); + } __msan_poison(&sai, sizeof(sai)); int new_sock = accept(listen_socket, (struct sockaddr *)&sai, &sz); ASSERT_LT(0, new_sock); ASSERT_EQ(sizeof(sai), sz); EXPECT_NOT_POISONED(sai); __msan_poison(&sai, sizeof(sai)); res = getpeername(new_sock, (struct sockaddr *)&sai, &sz); ASSERT_EQ(0, res); ASSERT_EQ(sizeof(sai), sz); EXPECT_NOT_POISONED(sai); close(new_sock); close(connect_socket); close(listen_socket); } TEST(MemorySanitizer, getaddrinfo) { struct addrinfo *ai; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; int res = getaddrinfo("localhost", NULL, &hints, &ai); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(*ai); ASSERT_EQ(sizeof(sockaddr_in), ai->ai_addrlen); EXPECT_NOT_POISONED(*(sockaddr_in*)ai->ai_addr); } TEST(MemorySanitizer, getnameinfo) { struct sockaddr_in sai; memset(&sai, 0, sizeof(sai)); sai.sin_family = AF_INET; sai.sin_port = 80; sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); char host[500]; char serv[500]; int res = getnameinfo((struct sockaddr *)&sai, sizeof(sai), host, sizeof(host), serv, sizeof(serv), 0); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(host[0]); EXPECT_POISONED(host[sizeof(host) - 1]); ASSERT_NE(0U, strlen(host)); EXPECT_NOT_POISONED(serv[0]); EXPECT_POISONED(serv[sizeof(serv) - 1]); ASSERT_NE(0U, strlen(serv)); } #define EXPECT_HOSTENT_NOT_POISONED(he) \ do { \ EXPECT_NOT_POISONED(*(he)); \ ASSERT_NE((void *) 0, (he)->h_name); \ ASSERT_NE((void *) 0, (he)->h_aliases); \ ASSERT_NE((void *) 0, (he)->h_addr_list); \ EXPECT_NOT_POISONED(strlen((he)->h_name)); \ char **p = (he)->h_aliases; \ while (*p) { \ EXPECT_NOT_POISONED(strlen(*p)); \ ++p; \ } \ char **q = (he)->h_addr_list; \ while (*q) { \ EXPECT_NOT_POISONED(*q[0]); \ ++q; \ } \ EXPECT_NOT_POISONED(*q); \ } while (0) TEST(MemorySanitizer, gethostent) { struct hostent *he = gethostent(); ASSERT_NE((void *)NULL, he); EXPECT_HOSTENT_NOT_POISONED(he); } #ifndef MSAN_TEST_DISABLE_GETHOSTBYNAME TEST(MemorySanitizer, gethostbyname) { struct hostent *he = gethostbyname("localhost"); ASSERT_NE((void *)NULL, he); EXPECT_HOSTENT_NOT_POISONED(he); } #endif // MSAN_TEST_DISABLE_GETHOSTBYNAME TEST(MemorySanitizer, recvmsg) { int server_socket = socket(AF_INET, SOCK_DGRAM, 0); ASSERT_LT(0, server_socket); struct sockaddr_in sai; memset(&sai, 0, sizeof(sai)); sai.sin_family = AF_INET; sai.sin_port = 0; sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); int res = bind(server_socket, (struct sockaddr *)&sai, sizeof(sai)); ASSERT_EQ(0, res); socklen_t sz = sizeof(sai); res = getsockname(server_socket, (struct sockaddr *)&sai, &sz); ASSERT_EQ(0, res); ASSERT_EQ(sizeof(sai), sz); int client_socket = socket(AF_INET, SOCK_DGRAM, 0); ASSERT_LT(0, client_socket); struct sockaddr_in client_sai; memset(&client_sai, 0, sizeof(client_sai)); client_sai.sin_family = AF_INET; client_sai.sin_port = 0; client_sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); res = bind(client_socket, (struct sockaddr *)&client_sai, sizeof(client_sai)); ASSERT_EQ(0, res); sz = sizeof(client_sai); res = getsockname(client_socket, (struct sockaddr *)&client_sai, &sz); ASSERT_EQ(0, res); ASSERT_EQ(sizeof(client_sai), sz); - const char *s = "message text"; struct iovec iov; iov.iov_base = (void *)s; iov.iov_len = strlen(s) + 1; struct msghdr msg; memset(&msg, 0, sizeof(msg)); msg.msg_name = &sai; msg.msg_namelen = sizeof(sai); msg.msg_iov = &iov; msg.msg_iovlen = 1; res = sendmsg(client_socket, &msg, 0); ASSERT_LT(0, res); char buf[1000]; struct iovec recv_iov; recv_iov.iov_base = (void *)&buf; recv_iov.iov_len = sizeof(buf); struct sockaddr_in recv_sai; struct msghdr recv_msg; memset(&recv_msg, 0, sizeof(recv_msg)); recv_msg.msg_name = &recv_sai; recv_msg.msg_namelen = sizeof(recv_sai); recv_msg.msg_iov = &recv_iov; recv_msg.msg_iovlen = 1; res = recvmsg(server_socket, &recv_msg, 0); ASSERT_LT(0, res); ASSERT_EQ(sizeof(recv_sai), recv_msg.msg_namelen); EXPECT_NOT_POISONED(*(struct sockaddr_in *)recv_msg.msg_name); EXPECT_STREQ(s, buf); close(server_socket); close(client_socket); } TEST(MemorySanitizer, gethostbyname2) { struct hostent *he = gethostbyname2("localhost", AF_INET); ASSERT_NE((void *)NULL, he); EXPECT_HOSTENT_NOT_POISONED(he); } TEST(MemorySanitizer, gethostbyaddr) { in_addr_t addr = inet_addr("127.0.0.1"); EXPECT_NOT_POISONED(addr); struct hostent *he = gethostbyaddr(&addr, sizeof(addr), AF_INET); ASSERT_NE((void *)NULL, he); EXPECT_HOSTENT_NOT_POISONED(he); } TEST(MemorySanitizer, gethostent_r) { char buf[2000]; struct hostent he; struct hostent *result; int err; int res = gethostent_r(&he, buf, sizeof(buf), &result, &err); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(result); ASSERT_NE((void *)NULL, result); EXPECT_HOSTENT_NOT_POISONED(result); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, gethostbyname_r) { char buf[2000]; struct hostent he; struct hostent *result; int err; int res = gethostbyname_r("localhost", &he, buf, sizeof(buf), &result, &err); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(result); ASSERT_NE((void *)NULL, result); EXPECT_HOSTENT_NOT_POISONED(result); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, gethostbyname_r_bad_host_name) { char buf[2000]; struct hostent he; struct hostent *result; int err; int res = gethostbyname_r("bad-host-name", &he, buf, sizeof(buf), &result, &err); ASSERT_EQ((struct hostent *)0, result); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, gethostbyname_r_erange) { char buf[5]; struct hostent he; struct hostent *result; int err; int res = gethostbyname_r("localhost", &he, buf, sizeof(buf), &result, &err); ASSERT_EQ(ERANGE, res); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, gethostbyname2_r) { char buf[2000]; struct hostent he; struct hostent *result; int err; int res = gethostbyname2_r("localhost", AF_INET, &he, buf, sizeof(buf), &result, &err); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(result); ASSERT_NE((void *)NULL, result); EXPECT_HOSTENT_NOT_POISONED(result); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, gethostbyaddr_r) { char buf[2000]; struct hostent he; struct hostent *result; int err; in_addr_t addr = inet_addr("127.0.0.1"); EXPECT_NOT_POISONED(addr); int res = gethostbyaddr_r(&addr, sizeof(addr), AF_INET, &he, buf, sizeof(buf), &result, &err); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(result); ASSERT_NE((void *)NULL, result); EXPECT_HOSTENT_NOT_POISONED(result); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, getsockopt) { int sock = socket(AF_UNIX, SOCK_STREAM, 0); struct linger l[2]; socklen_t sz = sizeof(l[0]); int res = getsockopt(sock, SOL_SOCKET, SO_LINGER, &l[0], &sz); ASSERT_EQ(0, res); ASSERT_EQ(sizeof(l[0]), sz); EXPECT_NOT_POISONED(l[0]); EXPECT_POISONED(*(char *)(l + 1)); } TEST(MemorySanitizer, getcwd) { char path[PATH_MAX + 1]; char* res = getcwd(path, sizeof(path)); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(path[0]); } TEST(MemorySanitizer, getcwd_gnu) { char* res = getcwd(NULL, 0); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } +// There's no get_current_dir_name() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, get_current_dir_name) { char* res = get_current_dir_name(); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } +#endif TEST(MemorySanitizer, shmctl) { int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); ASSERT_GT(id, -1); struct shmid_ds ds; int res = shmctl(id, IPC_STAT, &ds); ASSERT_GT(res, -1); EXPECT_NOT_POISONED(ds); + // FreeBSD does not support shmctl(IPC_INFO) and shmctl(SHM_INFO). +#if !defined(__FreeBSD__) struct shminfo si; res = shmctl(id, IPC_INFO, (struct shmid_ds *)&si); ASSERT_GT(res, -1); EXPECT_NOT_POISONED(si); struct shm_info s_i; res = shmctl(id, SHM_INFO, (struct shmid_ds *)&s_i); ASSERT_GT(res, -1); EXPECT_NOT_POISONED(s_i); +#endif res = shmctl(id, IPC_RMID, 0); ASSERT_GT(res, -1); } TEST(MemorySanitizer, shmat) { void *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_NE(MAP_FAILED, p); ((char *)p)[10] = *GetPoisoned(); ((char *)p)[4095] = *GetPoisoned(); int res = munmap(p, 4096); ASSERT_EQ(0, res); int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); ASSERT_GT(id, -1); void *q = shmat(id, p, 0); ASSERT_EQ(p, q); EXPECT_NOT_POISONED(((char *)q)[0]); EXPECT_NOT_POISONED(((char *)q)[10]); EXPECT_NOT_POISONED(((char *)q)[4095]); res = shmdt(q); ASSERT_EQ(0, res); res = shmctl(id, IPC_RMID, 0); ASSERT_GT(res, -1); } +// There's no random_r() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, random_r) { int32_t x; char z[64]; memset(z, 0, sizeof(z)); struct random_data buf; memset(&buf, 0, sizeof(buf)); int res = initstate_r(0, z, sizeof(z), &buf); ASSERT_EQ(0, res); res = random_r(&buf, &x); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(x); } +#endif TEST(MemorySanitizer, confstr) { char buf[3]; size_t res = confstr(_CS_PATH, buf, sizeof(buf)); ASSERT_GT(res, sizeof(buf)); EXPECT_NOT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[sizeof(buf) - 1]); char buf2[1000]; res = confstr(_CS_PATH, buf2, sizeof(buf2)); ASSERT_LT(res, sizeof(buf2)); EXPECT_NOT_POISONED(buf2[0]); EXPECT_NOT_POISONED(buf2[res - 1]); EXPECT_POISONED(buf2[res]); ASSERT_EQ(res, strlen(buf2) + 1); } +TEST(MemorySanitizer, opendir) { + DIR *dir = opendir("."); + closedir(dir); + + char name[10] = "."; + __msan_poison(name, sizeof(name)); + EXPECT_UMR(dir = opendir(name)); + closedir(dir); +} + TEST(MemorySanitizer, readdir) { DIR *dir = opendir("."); struct dirent *d = readdir(dir); ASSERT_TRUE(d != NULL); EXPECT_NOT_POISONED(d->d_name[0]); closedir(dir); } TEST(MemorySanitizer, readdir_r) { DIR *dir = opendir("."); struct dirent d; struct dirent *pd; int res = readdir_r(dir, &d, &pd); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pd); EXPECT_NOT_POISONED(d.d_name[0]); closedir(dir); } TEST(MemorySanitizer, realpath) { const char* relpath = "."; char path[PATH_MAX + 1]; char* res = realpath(relpath, path); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(path[0]); } TEST(MemorySanitizer, realpath_null) { const char* relpath = "."; char* res = realpath(relpath, NULL); printf("%d, %s\n", errno, strerror(errno)); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } +// There's no canonicalize_file_name() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, canonicalize_file_name) { const char* relpath = "."; char* res = canonicalize_file_name(relpath); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } +#endif extern char **environ; TEST(MemorySanitizer, setenv) { setenv("AAA", "BBB", 1); for (char **envp = environ; *envp; ++envp) { EXPECT_NOT_POISONED(*envp); EXPECT_NOT_POISONED(*envp[0]); } } TEST(MemorySanitizer, putenv) { char s[] = "AAA=BBB"; putenv(s); for (char **envp = environ; *envp; ++envp) { EXPECT_NOT_POISONED(*envp); EXPECT_NOT_POISONED(*envp[0]); } } TEST(MemorySanitizer, memcpy) { char* x = new char[2]; char* y = new char[2]; x[0] = 1; x[1] = *GetPoisoned(); memcpy(y, x, 2); EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); } void TestUnalignedMemcpy(unsigned left, unsigned right, bool src_is_aligned, bool src_is_poisoned, bool dst_is_poisoned) { fprintf(stderr, "%s(%d, %d, %d, %d, %d)\n", __func__, left, right, src_is_aligned, src_is_poisoned, dst_is_poisoned); const unsigned sz = 20; U4 dst_origin, src_origin; char *dst = (char *)malloc(sz); if (dst_is_poisoned) dst_origin = __msan_get_origin(dst); else memset(dst, 0, sz); char *src = (char *)malloc(sz); if (src_is_poisoned) src_origin = __msan_get_origin(src); else memset(src, 0, sz); memcpy(dst + left, src_is_aligned ? src + left : src, sz - left - right); for (unsigned i = 0; i < (left & (~3U)); ++i) if (dst_is_poisoned) EXPECT_POISONED_O(dst[i], dst_origin); else EXPECT_NOT_POISONED(dst[i]); for (unsigned i = 0; i < (right & (~3U)); ++i) if (dst_is_poisoned) EXPECT_POISONED_O(dst[sz - i - 1], dst_origin); else EXPECT_NOT_POISONED(dst[sz - i - 1]); for (unsigned i = left; i < sz - right; ++i) if (src_is_poisoned) EXPECT_POISONED_O(dst[i], src_origin); else EXPECT_NOT_POISONED(dst[i]); free(dst); free(src); } TEST(MemorySanitizer, memcpy_unaligned) { for (int i = 0; i < 10; ++i) for (int j = 0; j < 10; ++j) for (int aligned = 0; aligned < 2; ++aligned) for (int srcp = 0; srcp < 2; ++srcp) for (int dstp = 0; dstp < 2; ++dstp) TestUnalignedMemcpy(i, j, aligned, srcp, dstp); } TEST(MemorySanitizer, memmove) { char* x = new char[2]; char* y = new char[2]; x[0] = 1; x[1] = *GetPoisoned(); memmove(y, x, 2); EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); } TEST(MemorySanitizer, memccpy_nomatch) { char* x = new char[5]; char* y = new char[5]; strcpy(x, "abc"); memccpy(y, x, 'd', 4); EXPECT_NOT_POISONED(y[0]); EXPECT_NOT_POISONED(y[1]); EXPECT_NOT_POISONED(y[2]); EXPECT_NOT_POISONED(y[3]); EXPECT_POISONED(y[4]); delete[] x; delete[] y; } TEST(MemorySanitizer, memccpy_match) { char* x = new char[5]; char* y = new char[5]; strcpy(x, "abc"); memccpy(y, x, 'b', 4); EXPECT_NOT_POISONED(y[0]); EXPECT_NOT_POISONED(y[1]); EXPECT_POISONED(y[2]); EXPECT_POISONED(y[3]); EXPECT_POISONED(y[4]); delete[] x; delete[] y; } TEST(MemorySanitizer, memccpy_nomatch_positive) { char* x = new char[5]; char* y = new char[5]; strcpy(x, "abc"); EXPECT_UMR(memccpy(y, x, 'd', 5)); delete[] x; delete[] y; } TEST(MemorySanitizer, memccpy_match_positive) { char* x = new char[5]; char* y = new char[5]; x[0] = 'a'; x[2] = 'b'; EXPECT_UMR(memccpy(y, x, 'b', 5)); delete[] x; delete[] y; } TEST(MemorySanitizer, bcopy) { char* x = new char[2]; char* y = new char[2]; x[0] = 1; x[1] = *GetPoisoned(); bcopy(x, y, 2); EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); } TEST(MemorySanitizer, strdup) { char buf[4] = "abc"; __msan_poison(buf + 2, sizeof(*buf)); char *x = strdup(buf); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[1]); EXPECT_POISONED(x[2]); EXPECT_NOT_POISONED(x[3]); free(x); } TEST(MemorySanitizer, strndup) { char buf[4] = "abc"; __msan_poison(buf + 2, sizeof(*buf)); char *x = strndup(buf, 3); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[1]); EXPECT_POISONED(x[2]); EXPECT_NOT_POISONED(x[3]); free(x); } TEST(MemorySanitizer, strndup_short) { char buf[4] = "abc"; __msan_poison(buf + 1, sizeof(*buf)); __msan_poison(buf + 2, sizeof(*buf)); char *x = strndup(buf, 2); EXPECT_NOT_POISONED(x[0]); EXPECT_POISONED(x[1]); EXPECT_NOT_POISONED(x[2]); free(x); } template void TestOverlapMemmove() { T *x = new T[size]; ASSERT_GE(size, 3); x[2] = 0; memmove(x, x + 1, (size - 1) * sizeof(T)); EXPECT_NOT_POISONED(x[1]); EXPECT_POISONED(x[0]); EXPECT_POISONED(x[2]); delete [] x; } TEST(MemorySanitizer, overlap_memmove) { TestOverlapMemmove(); TestOverlapMemmove(); TestOverlapMemmove(); TestOverlapMemmove(); } TEST(MemorySanitizer, strcpy) { // NOLINT char* x = new char[3]; char* y = new char[3]; x[0] = 'a'; x[1] = *GetPoisoned(1, 1); x[2] = 0; strcpy(y, x); // NOLINT EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); EXPECT_NOT_POISONED(y[2]); } TEST(MemorySanitizer, strncpy) { // NOLINT char* x = new char[3]; char* y = new char[5]; x[0] = 'a'; x[1] = *GetPoisoned(1, 1); x[2] = '\0'; strncpy(y, x, 4); // NOLINT EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); EXPECT_NOT_POISONED(y[2]); EXPECT_NOT_POISONED(y[3]); EXPECT_POISONED(y[4]); } TEST(MemorySanitizer, stpcpy) { // NOLINT char* x = new char[3]; char* y = new char[3]; x[0] = 'a'; x[1] = *GetPoisoned(1, 1); x[2] = 0; char *res = stpcpy(y, x); // NOLINT ASSERT_EQ(res, y + 2); EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); EXPECT_NOT_POISONED(y[2]); } TEST(MemorySanitizer, strcat) { // NOLINT char a[10]; char b[] = "def"; strcpy(a, "abc"); __msan_poison(b + 1, 1); strcat(a, b); EXPECT_NOT_POISONED(a[3]); EXPECT_POISONED(a[4]); EXPECT_NOT_POISONED(a[5]); EXPECT_NOT_POISONED(a[6]); EXPECT_POISONED(a[7]); } TEST(MemorySanitizer, strncat) { // NOLINT char a[10]; char b[] = "def"; strcpy(a, "abc"); __msan_poison(b + 1, 1); strncat(a, b, 5); EXPECT_NOT_POISONED(a[3]); EXPECT_POISONED(a[4]); EXPECT_NOT_POISONED(a[5]); EXPECT_NOT_POISONED(a[6]); EXPECT_POISONED(a[7]); } TEST(MemorySanitizer, strncat_overflow) { // NOLINT char a[10]; char b[] = "def"; strcpy(a, "abc"); __msan_poison(b + 1, 1); strncat(a, b, 2); EXPECT_NOT_POISONED(a[3]); EXPECT_POISONED(a[4]); EXPECT_NOT_POISONED(a[5]); EXPECT_POISONED(a[6]); EXPECT_POISONED(a[7]); } #define TEST_STRTO_INT(func_name, char_type, str_prefix) \ TEST(MemorySanitizer, func_name) { \ char_type *e; \ EXPECT_EQ(1U, func_name(str_prefix##"1", &e, 10)); \ EXPECT_NOT_POISONED((S8)e); \ } #define TEST_STRTO_FLOAT(func_name, char_type, str_prefix) \ TEST(MemorySanitizer, func_name) { \ char_type *e; \ EXPECT_NE(0, func_name(str_prefix##"1.5", &e)); \ EXPECT_NOT_POISONED((S8)e); \ } #define TEST_STRTO_FLOAT_LOC(func_name, char_type, str_prefix) \ TEST(MemorySanitizer, func_name) { \ locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); \ char_type *e; \ EXPECT_NE(0, func_name(str_prefix##"1.5", &e, loc)); \ EXPECT_NOT_POISONED((S8)e); \ freelocale(loc); \ } #define TEST_STRTO_INT_LOC(func_name, char_type, str_prefix) \ TEST(MemorySanitizer, func_name) { \ locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); \ char_type *e; \ ASSERT_EQ(1U, func_name(str_prefix##"1", &e, 10, loc)); \ EXPECT_NOT_POISONED((S8)e); \ freelocale(loc); \ } TEST_STRTO_INT(strtol, char, ) TEST_STRTO_INT(strtoll, char, ) TEST_STRTO_INT(strtoul, char, ) TEST_STRTO_INT(strtoull, char, ) TEST_STRTO_FLOAT(strtof, char, ) TEST_STRTO_FLOAT(strtod, char, ) TEST_STRTO_FLOAT(strtold, char, ) TEST_STRTO_FLOAT_LOC(strtof_l, char, ) TEST_STRTO_FLOAT_LOC(strtod_l, char, ) TEST_STRTO_FLOAT_LOC(strtold_l, char, ) TEST_STRTO_INT_LOC(strtol_l, char, ) TEST_STRTO_INT_LOC(strtoll_l, char, ) TEST_STRTO_INT_LOC(strtoul_l, char, ) TEST_STRTO_INT_LOC(strtoull_l, char, ) TEST_STRTO_INT(wcstol, wchar_t, L) TEST_STRTO_INT(wcstoll, wchar_t, L) TEST_STRTO_INT(wcstoul, wchar_t, L) TEST_STRTO_INT(wcstoull, wchar_t, L) TEST_STRTO_FLOAT(wcstof, wchar_t, L) TEST_STRTO_FLOAT(wcstod, wchar_t, L) TEST_STRTO_FLOAT(wcstold, wchar_t, L) TEST_STRTO_FLOAT_LOC(wcstof_l, wchar_t, L) TEST_STRTO_FLOAT_LOC(wcstod_l, wchar_t, L) TEST_STRTO_FLOAT_LOC(wcstold_l, wchar_t, L) TEST_STRTO_INT_LOC(wcstol_l, wchar_t, L) TEST_STRTO_INT_LOC(wcstoll_l, wchar_t, L) TEST_STRTO_INT_LOC(wcstoul_l, wchar_t, L) TEST_STRTO_INT_LOC(wcstoull_l, wchar_t, L) TEST(MemorySanitizer, strtoimax) { char *e; ASSERT_EQ(1, strtoimax("1", &e, 10)); EXPECT_NOT_POISONED((S8) e); } TEST(MemorySanitizer, strtoumax) { char *e; ASSERT_EQ(1U, strtoumax("1", &e, 10)); EXPECT_NOT_POISONED((S8) e); } #ifdef __GLIBC__ extern "C" float __strtof_l(const char *nptr, char **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__strtof_l, char, ) extern "C" double __strtod_l(const char *nptr, char **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__strtod_l, char, ) extern "C" long double __strtold_l(const char *nptr, char **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__strtold_l, char, ) extern "C" float __wcstof_l(const wchar_t *nptr, wchar_t **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__wcstof_l, wchar_t, L) extern "C" double __wcstod_l(const wchar_t *nptr, wchar_t **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__wcstod_l, wchar_t, L) extern "C" long double __wcstold_l(const wchar_t *nptr, wchar_t **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__wcstold_l, wchar_t, L) #endif // __GLIBC__ TEST(MemorySanitizer, modf) { double x, y; x = modf(2.1, &y); EXPECT_NOT_POISONED(y); } TEST(MemorySanitizer, modff) { float x, y; x = modff(2.1, &y); EXPECT_NOT_POISONED(y); } TEST(MemorySanitizer, modfl) { long double x, y; x = modfl(2.1, &y); EXPECT_NOT_POISONED(y); } +// There's no sincos() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, sincos) { double s, c; sincos(0.2, &s, &c); EXPECT_NOT_POISONED(s); EXPECT_NOT_POISONED(c); } +#endif +// There's no sincosf() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, sincosf) { float s, c; sincosf(0.2, &s, &c); EXPECT_NOT_POISONED(s); EXPECT_NOT_POISONED(c); } +#endif +// There's no sincosl() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, sincosl) { long double s, c; sincosl(0.2, &s, &c); EXPECT_NOT_POISONED(s); EXPECT_NOT_POISONED(c); } +#endif TEST(MemorySanitizer, remquo) { int quo; double res = remquo(29.0, 3.0, &quo); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(quo); } TEST(MemorySanitizer, remquof) { int quo; float res = remquof(29.0, 3.0, &quo); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(quo); } TEST(MemorySanitizer, remquol) { int quo; long double res = remquof(29.0, 3.0, &quo); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(quo); } TEST(MemorySanitizer, lgamma) { double res = lgamma(1.1); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(signgam); } TEST(MemorySanitizer, lgammaf) { float res = lgammaf(1.1); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(signgam); } TEST(MemorySanitizer, lgammal) { long double res = lgammal(1.1); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(signgam); } TEST(MemorySanitizer, lgamma_r) { int sgn; double res = lgamma_r(1.1, &sgn); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(sgn); } TEST(MemorySanitizer, lgammaf_r) { int sgn; float res = lgammaf_r(1.1, &sgn); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(sgn); } +// There's no lgammal_r() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, lgammal_r) { int sgn; long double res = lgammal_r(1.1, &sgn); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(sgn); } +#endif +// There's no drand48_r() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, drand48_r) { struct drand48_data buf; srand48_r(0, &buf); double d; drand48_r(&buf, &d); EXPECT_NOT_POISONED(d); } +#endif +// There's no lrand48_r() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, lrand48_r) { struct drand48_data buf; srand48_r(0, &buf); long d; lrand48_r(&buf, &d); EXPECT_NOT_POISONED(d); } +#endif TEST(MemorySanitizer, sprintf) { // NOLINT char buff[10]; break_optimization(buff); EXPECT_POISONED(buff[0]); int res = sprintf(buff, "%d", 1234567); // NOLINT ASSERT_EQ(res, 7); ASSERT_EQ(buff[0], '1'); ASSERT_EQ(buff[1], '2'); ASSERT_EQ(buff[2], '3'); ASSERT_EQ(buff[6], '7'); ASSERT_EQ(buff[7], 0); EXPECT_POISONED(buff[8]); } TEST(MemorySanitizer, snprintf) { char buff[10]; break_optimization(buff); EXPECT_POISONED(buff[0]); int res = snprintf(buff, sizeof(buff), "%d", 1234567); ASSERT_EQ(res, 7); ASSERT_EQ(buff[0], '1'); ASSERT_EQ(buff[1], '2'); ASSERT_EQ(buff[2], '3'); ASSERT_EQ(buff[6], '7'); ASSERT_EQ(buff[7], 0); EXPECT_POISONED(buff[8]); } TEST(MemorySanitizer, swprintf) { wchar_t buff[10]; ASSERT_EQ(4U, sizeof(wchar_t)); break_optimization(buff); EXPECT_POISONED(buff[0]); int res = swprintf(buff, 9, L"%d", 1234567); ASSERT_EQ(res, 7); ASSERT_EQ(buff[0], '1'); ASSERT_EQ(buff[1], '2'); ASSERT_EQ(buff[2], '3'); ASSERT_EQ(buff[6], '7'); ASSERT_EQ(buff[7], 0); EXPECT_POISONED(buff[8]); } TEST(MemorySanitizer, asprintf) { // NOLINT char *pbuf; EXPECT_POISONED(pbuf); int res = asprintf(&pbuf, "%d", 1234567); // NOLINT ASSERT_EQ(res, 7); EXPECT_NOT_POISONED(pbuf); ASSERT_EQ(pbuf[0], '1'); ASSERT_EQ(pbuf[1], '2'); ASSERT_EQ(pbuf[2], '3'); ASSERT_EQ(pbuf[6], '7'); ASSERT_EQ(pbuf[7], 0); free(pbuf); } TEST(MemorySanitizer, mbstowcs) { const char *x = "abc"; wchar_t buff[10]; int res = mbstowcs(buff, x, 2); EXPECT_EQ(2, res); EXPECT_EQ(L'a', buff[0]); EXPECT_EQ(L'b', buff[1]); EXPECT_POISONED(buff[2]); res = mbstowcs(buff, x, 10); EXPECT_EQ(3, res); EXPECT_NOT_POISONED(buff[3]); } TEST(MemorySanitizer, wcstombs) { const wchar_t *x = L"abc"; char buff[10]; int res = wcstombs(buff, x, 4); EXPECT_EQ(res, 3); EXPECT_EQ(buff[0], 'a'); EXPECT_EQ(buff[1], 'b'); EXPECT_EQ(buff[2], 'c'); } TEST(MemorySanitizer, wcsrtombs) { const wchar_t *x = L"abc"; const wchar_t *p = x; char buff[10]; mbstate_t mbs; memset(&mbs, 0, sizeof(mbs)); int res = wcsrtombs(buff, &p, 4, &mbs); EXPECT_EQ(res, 3); EXPECT_EQ(buff[0], 'a'); EXPECT_EQ(buff[1], 'b'); EXPECT_EQ(buff[2], 'c'); EXPECT_EQ(buff[3], '\0'); EXPECT_POISONED(buff[4]); } TEST(MemorySanitizer, wcsnrtombs) { const wchar_t *x = L"abc"; const wchar_t *p = x; char buff[10]; mbstate_t mbs; memset(&mbs, 0, sizeof(mbs)); int res = wcsnrtombs(buff, &p, 2, 4, &mbs); EXPECT_EQ(res, 2); EXPECT_EQ(buff[0], 'a'); EXPECT_EQ(buff[1], 'b'); EXPECT_POISONED(buff[2]); } TEST(MemorySanitizer, wmemset) { wchar_t x[25]; break_optimization(x); EXPECT_POISONED(x[0]); wmemset(x, L'A', 10); EXPECT_EQ(x[0], L'A'); EXPECT_EQ(x[9], L'A'); EXPECT_POISONED(x[10]); } TEST(MemorySanitizer, mbtowc) { const char *x = "abc"; wchar_t wx; int res = mbtowc(&wx, x, 3); EXPECT_GT(res, 0); EXPECT_NOT_POISONED(wx); } TEST(MemorySanitizer, mbrtowc) { const char *x = "abc"; wchar_t wx; mbstate_t mbs; memset(&mbs, 0, sizeof(mbs)); int res = mbrtowc(&wx, x, 3, &mbs); EXPECT_GT(res, 0); EXPECT_NOT_POISONED(wx); } TEST(MemorySanitizer, wcsftime) { wchar_t x[100]; time_t t = time(NULL); struct tm tms; struct tm *tmres = localtime_r(&t, &tms); ASSERT_NE((void *)0, tmres); size_t res = wcsftime(x, sizeof(x) / sizeof(x[0]), L"%Y-%m-%d", tmres); EXPECT_GT(res, 0UL); EXPECT_EQ(res, wcslen(x)); } TEST(MemorySanitizer, gettimeofday) { struct timeval tv; struct timezone tz; break_optimization(&tv); break_optimization(&tz); ASSERT_EQ(16U, sizeof(tv)); ASSERT_EQ(8U, sizeof(tz)); EXPECT_POISONED(tv.tv_sec); EXPECT_POISONED(tv.tv_usec); EXPECT_POISONED(tz.tz_minuteswest); EXPECT_POISONED(tz.tz_dsttime); ASSERT_EQ(0, gettimeofday(&tv, &tz)); EXPECT_NOT_POISONED(tv.tv_sec); EXPECT_NOT_POISONED(tv.tv_usec); EXPECT_NOT_POISONED(tz.tz_minuteswest); EXPECT_NOT_POISONED(tz.tz_dsttime); } TEST(MemorySanitizer, clock_gettime) { struct timespec tp; EXPECT_POISONED(tp.tv_sec); EXPECT_POISONED(tp.tv_nsec); ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &tp)); EXPECT_NOT_POISONED(tp.tv_sec); EXPECT_NOT_POISONED(tp.tv_nsec); } TEST(MemorySanitizer, clock_getres) { struct timespec tp; EXPECT_POISONED(tp.tv_sec); EXPECT_POISONED(tp.tv_nsec); ASSERT_EQ(0, clock_getres(CLOCK_REALTIME, 0)); EXPECT_POISONED(tp.tv_sec); EXPECT_POISONED(tp.tv_nsec); ASSERT_EQ(0, clock_getres(CLOCK_REALTIME, &tp)); EXPECT_NOT_POISONED(tp.tv_sec); EXPECT_NOT_POISONED(tp.tv_nsec); } TEST(MemorySanitizer, getitimer) { struct itimerval it1, it2; int res; EXPECT_POISONED(it1.it_interval.tv_sec); EXPECT_POISONED(it1.it_interval.tv_usec); EXPECT_POISONED(it1.it_value.tv_sec); EXPECT_POISONED(it1.it_value.tv_usec); res = getitimer(ITIMER_VIRTUAL, &it1); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(it1.it_interval.tv_sec); EXPECT_NOT_POISONED(it1.it_interval.tv_usec); EXPECT_NOT_POISONED(it1.it_value.tv_sec); EXPECT_NOT_POISONED(it1.it_value.tv_usec); it1.it_interval.tv_sec = it1.it_value.tv_sec = 10000; it1.it_interval.tv_usec = it1.it_value.tv_usec = 0; res = setitimer(ITIMER_VIRTUAL, &it1, &it2); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(it2.it_interval.tv_sec); EXPECT_NOT_POISONED(it2.it_interval.tv_usec); EXPECT_NOT_POISONED(it2.it_value.tv_sec); EXPECT_NOT_POISONED(it2.it_value.tv_usec); // Check that old_value can be 0, and disable the timer. memset(&it1, 0, sizeof(it1)); res = setitimer(ITIMER_VIRTUAL, &it1, 0); ASSERT_EQ(0, res); } TEST(MemorySanitizer, setitimer_null) { setitimer(ITIMER_VIRTUAL, 0, 0); // Not testing the return value, since it the behaviour seems to differ // between libc implementations and POSIX. // Should never crash, though. } TEST(MemorySanitizer, time) { time_t t; EXPECT_POISONED(t); time_t t2 = time(&t); ASSERT_NE(t2, (time_t)-1); EXPECT_NOT_POISONED(t); } TEST(MemorySanitizer, strptime) { struct tm time; char *p = strptime("11/1/2013-05:39", "%m/%d/%Y-%H:%M", &time); ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(time.tm_sec); EXPECT_NOT_POISONED(time.tm_hour); EXPECT_NOT_POISONED(time.tm_year); } TEST(MemorySanitizer, localtime) { time_t t = 123; struct tm *time = localtime(&t); ASSERT_TRUE(time != NULL); EXPECT_NOT_POISONED(time->tm_sec); EXPECT_NOT_POISONED(time->tm_hour); EXPECT_NOT_POISONED(time->tm_year); EXPECT_NOT_POISONED(time->tm_isdst); EXPECT_NE(0U, strlen(time->tm_zone)); } TEST(MemorySanitizer, localtime_r) { time_t t = 123; struct tm time; struct tm *res = localtime_r(&t, &time); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(time.tm_sec); EXPECT_NOT_POISONED(time.tm_hour); EXPECT_NOT_POISONED(time.tm_year); EXPECT_NOT_POISONED(time.tm_isdst); EXPECT_NE(0U, strlen(time.tm_zone)); } +// There's no getmntent() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, getmntent) { FILE *fp = setmntent("/etc/fstab", "r"); struct mntent *mnt = getmntent(fp); ASSERT_TRUE(mnt != NULL); ASSERT_NE(0U, strlen(mnt->mnt_fsname)); ASSERT_NE(0U, strlen(mnt->mnt_dir)); ASSERT_NE(0U, strlen(mnt->mnt_type)); ASSERT_NE(0U, strlen(mnt->mnt_opts)); EXPECT_NOT_POISONED(mnt->mnt_freq); EXPECT_NOT_POISONED(mnt->mnt_passno); fclose(fp); } +#endif +// There's no getmntent_r() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, getmntent_r) { FILE *fp = setmntent("/etc/fstab", "r"); struct mntent mntbuf; char buf[1000]; struct mntent *mnt = getmntent_r(fp, &mntbuf, buf, sizeof(buf)); ASSERT_TRUE(mnt != NULL); ASSERT_NE(0U, strlen(mnt->mnt_fsname)); ASSERT_NE(0U, strlen(mnt->mnt_dir)); ASSERT_NE(0U, strlen(mnt->mnt_type)); ASSERT_NE(0U, strlen(mnt->mnt_opts)); EXPECT_NOT_POISONED(mnt->mnt_freq); EXPECT_NOT_POISONED(mnt->mnt_passno); fclose(fp); } +#endif TEST(MemorySanitizer, ether) { const char *asc = "11:22:33:44:55:66"; struct ether_addr *paddr = ether_aton(asc); EXPECT_NOT_POISONED(*paddr); struct ether_addr addr; paddr = ether_aton_r(asc, &addr); ASSERT_EQ(paddr, &addr); EXPECT_NOT_POISONED(addr); char *s = ether_ntoa(&addr); ASSERT_NE(0U, strlen(s)); char buf[100]; s = ether_ntoa_r(&addr, buf); ASSERT_EQ(s, buf); ASSERT_NE(0U, strlen(buf)); } TEST(MemorySanitizer, mmap) { const int size = 4096; void *p1, *p2; p1 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); __msan_poison(p1, size); munmap(p1, size); for (int i = 0; i < 1000; i++) { p2 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (p2 == p1) break; else munmap(p2, size); } if (p1 == p2) { EXPECT_NOT_POISONED(*(char*)p2); munmap(p2, size); } } // FIXME: enable and add ecvt. // FIXME: check why msandr does nt handle fcvt. TEST(MemorySanitizer, fcvt) { int a, b; break_optimization(&a); break_optimization(&b); EXPECT_POISONED(a); EXPECT_POISONED(b); char *str = fcvt(12345.6789, 10, &a, &b); EXPECT_NOT_POISONED(a); EXPECT_NOT_POISONED(b); ASSERT_NE(nullptr, str); EXPECT_NOT_POISONED(str[0]); ASSERT_NE(0U, strlen(str)); } TEST(MemorySanitizer, fcvt_long) { int a, b; break_optimization(&a); break_optimization(&b); EXPECT_POISONED(a); EXPECT_POISONED(b); char *str = fcvt(111111112345.6789, 10, &a, &b); EXPECT_NOT_POISONED(a); EXPECT_NOT_POISONED(b); ASSERT_NE(nullptr, str); EXPECT_NOT_POISONED(str[0]); ASSERT_NE(0U, strlen(str)); } TEST(MemorySanitizer, memchr) { char x[10]; break_optimization(x); EXPECT_POISONED(x[0]); x[2] = '2'; void *res; EXPECT_UMR(res = memchr(x, '2', 10)); EXPECT_NOT_POISONED(res); x[0] = '0'; x[1] = '1'; res = memchr(x, '2', 10); EXPECT_EQ(&x[2], res); EXPECT_UMR(res = memchr(x, '3', 10)); EXPECT_NOT_POISONED(res); } TEST(MemorySanitizer, memrchr) { char x[10]; break_optimization(x); EXPECT_POISONED(x[0]); x[9] = '9'; void *res; EXPECT_UMR(res = memrchr(x, '9', 10)); EXPECT_NOT_POISONED(res); x[0] = '0'; x[1] = '1'; res = memrchr(x, '0', 2); EXPECT_EQ(&x[0], res); EXPECT_UMR(res = memrchr(x, '7', 10)); EXPECT_NOT_POISONED(res); } TEST(MemorySanitizer, frexp) { int x; x = *GetPoisoned(); double r = frexp(1.1, &x); EXPECT_NOT_POISONED(r); EXPECT_NOT_POISONED(x); x = *GetPoisoned(); float rf = frexpf(1.1, &x); EXPECT_NOT_POISONED(rf); EXPECT_NOT_POISONED(x); x = *GetPoisoned(); double rl = frexpl(1.1, &x); EXPECT_NOT_POISONED(rl); EXPECT_NOT_POISONED(x); } namespace { static int cnt; void SigactionHandler(int signo, siginfo_t* si, void* uc) { ASSERT_EQ(signo, SIGPROF); ASSERT_TRUE(si != NULL); EXPECT_NOT_POISONED(si->si_errno); EXPECT_NOT_POISONED(si->si_pid); #if __linux__ # if defined(__x86_64__) EXPECT_NOT_POISONED(((ucontext_t*)uc)->uc_mcontext.gregs[REG_RIP]); # elif defined(__i386__) EXPECT_NOT_POISONED(((ucontext_t*)uc)->uc_mcontext.gregs[REG_EIP]); # endif #endif ++cnt; } TEST(MemorySanitizer, sigaction) { struct sigaction act = {}; struct sigaction oldact = {}; struct sigaction origact = {}; sigaction(SIGPROF, 0, &origact); act.sa_flags |= SA_SIGINFO; act.sa_sigaction = &SigactionHandler; sigaction(SIGPROF, &act, 0); kill(getpid(), SIGPROF); act.sa_flags &= ~SA_SIGINFO; act.sa_handler = SIG_DFL; sigaction(SIGPROF, &act, 0); act.sa_flags &= ~SA_SIGINFO; act.sa_handler = SIG_IGN; sigaction(SIGPROF, &act, &oldact); EXPECT_FALSE(oldact.sa_flags & SA_SIGINFO); EXPECT_EQ(SIG_DFL, oldact.sa_handler); kill(getpid(), SIGPROF); act.sa_flags |= SA_SIGINFO; act.sa_sigaction = &SigactionHandler; sigaction(SIGPROF, &act, &oldact); EXPECT_FALSE(oldact.sa_flags & SA_SIGINFO); EXPECT_EQ(SIG_IGN, oldact.sa_handler); kill(getpid(), SIGPROF); act.sa_flags &= ~SA_SIGINFO; act.sa_handler = SIG_DFL; sigaction(SIGPROF, &act, &oldact); EXPECT_TRUE(oldact.sa_flags & SA_SIGINFO); EXPECT_EQ(&SigactionHandler, oldact.sa_sigaction); EXPECT_EQ(2, cnt); sigaction(SIGPROF, &origact, 0); } } // namespace TEST(MemorySanitizer, sigemptyset) { sigset_t s; EXPECT_POISONED(s); int res = sigemptyset(&s); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(s); } TEST(MemorySanitizer, sigfillset) { sigset_t s; EXPECT_POISONED(s); int res = sigfillset(&s); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(s); } TEST(MemorySanitizer, sigpending) { sigset_t s; EXPECT_POISONED(s); int res = sigpending(&s); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(s); } TEST(MemorySanitizer, sigprocmask) { sigset_t s; EXPECT_POISONED(s); int res = sigprocmask(SIG_BLOCK, 0, &s); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(s); } struct StructWithDtor { ~StructWithDtor(); }; NOINLINE StructWithDtor::~StructWithDtor() { break_optimization(0); } TEST(MemorySanitizer, Invoke) { StructWithDtor s; // Will cause the calls to become invokes. EXPECT_NOT_POISONED(0); EXPECT_POISONED(*GetPoisoned()); EXPECT_NOT_POISONED(0); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(ReturnPoisoned()); } TEST(MemorySanitizer, ptrtoint) { // Test that shadow is propagated through pointer-to-integer conversion. void* p = (void*)0xABCD; __msan_poison(((char*)&p) + 1, sizeof(p)); EXPECT_NOT_POISONED((((uintptr_t)p) & 0xFF) == 0); void* q = (void*)0xABCD; __msan_poison(&q, sizeof(q) - 1); EXPECT_POISONED((((uintptr_t)q) & 0xFF) == 0); } static void vaargsfn2(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, double)); va_end(vl); } static void vaargsfn(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); // The following call will overwrite __msan_param_tls. // Checks after it test that arg shadow was somehow saved across the call. vaargsfn2(1, 2, 3, 4, *GetPoisoned()); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgTest) { int* x = GetPoisoned(); int* y = GetPoisoned(4); vaargsfn(1, 13, *x, 42, *y); } static void vaargsfn_many(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgManyTest) { int* x = GetPoisoned(); int* y = GetPoisoned(4); vaargsfn_many(1, 2, *x, 3, 4, 5, 6, 7, 8, 9, *y); } static void vaargsfn_pass2(va_list vl) { EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); } static void vaargsfn_pass(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_POISONED(va_arg(vl, int)); vaargsfn_pass2(vl); va_end(vl); } TEST(MemorySanitizer, VAArgPass) { int* x = GetPoisoned(); int* y = GetPoisoned(4); vaargsfn_pass(1, *x, 2, 3, *y); } static void vaargsfn_copy2(va_list vl) { EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); } static void vaargsfn_copy(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); va_list vl2; va_copy(vl2, vl); vaargsfn_copy2(vl2); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgCopy) { int* x = GetPoisoned(); int* y = GetPoisoned(4); vaargsfn_copy(1, 2, *x, 3, *y); } static void vaargsfn_ptr(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int*)); EXPECT_POISONED(va_arg(vl, int*)); EXPECT_NOT_POISONED(va_arg(vl, int*)); EXPECT_POISONED(va_arg(vl, double*)); va_end(vl); } TEST(MemorySanitizer, VAArgPtr) { int** x = GetPoisoned(); double** y = GetPoisoned(8); int z; vaargsfn_ptr(1, &z, *x, &z, *y); } static void vaargsfn_overflow(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_POISONED(va_arg(vl, double)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_POISONED(va_arg(vl, int*)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, double)); EXPECT_POISONED(va_arg(vl, int*)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_NOT_POISONED(va_arg(vl, int*)); EXPECT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, double)); EXPECT_POISONED(va_arg(vl, int*)); va_end(vl); } TEST(MemorySanitizer, VAArgOverflow) { int* x = GetPoisoned(); double* y = GetPoisoned(8); int** p = GetPoisoned(16); int z; vaargsfn_overflow(1, 1, 2, *x, 4, 5, 6, 1.1, 2.2, 3.3, *y, 5.5, *p, 7.7, 8.8, // the following args will overflow for sure *x, *y, *p, 7, 9.9, &z, *x, *y, *p); } static void vaargsfn_tlsoverwrite2(int guard, ...) { va_list vl; va_start(vl, guard); for (int i = 0; i < 20; ++i) EXPECT_NOT_POISONED(va_arg(vl, int)); va_end(vl); } static void vaargsfn_tlsoverwrite(int guard, ...) { // This call will overwrite TLS contents unless it's backed up somewhere. vaargsfn_tlsoverwrite2(2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42); // 20x va_list vl; va_start(vl, guard); for (int i = 0; i < 20; ++i) EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgTLSOverwrite) { int* x = GetPoisoned(); vaargsfn_tlsoverwrite(1, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x); // 20x } struct StructByVal { int a, b, c, d, e, f; }; static void vaargsfn_structbyval(int guard, ...) { va_list vl; va_start(vl, guard); { StructByVal s = va_arg(vl, StructByVal); EXPECT_NOT_POISONED(s.a); EXPECT_POISONED(s.b); EXPECT_NOT_POISONED(s.c); EXPECT_POISONED(s.d); EXPECT_NOT_POISONED(s.e); EXPECT_POISONED(s.f); } { StructByVal s = va_arg(vl, StructByVal); EXPECT_NOT_POISONED(s.a); EXPECT_POISONED(s.b); EXPECT_NOT_POISONED(s.c); EXPECT_POISONED(s.d); EXPECT_NOT_POISONED(s.e); EXPECT_POISONED(s.f); } va_end(vl); } TEST(MemorySanitizer, VAArgStructByVal) { StructByVal s; s.a = 1; s.b = *GetPoisoned(); s.c = 2; s.d = *GetPoisoned(); s.e = 3; s.f = *GetPoisoned(); vaargsfn_structbyval(0, s, s); } NOINLINE void StructByValTestFunc(struct StructByVal s) { EXPECT_NOT_POISONED(s.a); EXPECT_POISONED(s.b); EXPECT_NOT_POISONED(s.c); EXPECT_POISONED(s.d); EXPECT_NOT_POISONED(s.e); EXPECT_POISONED(s.f); } NOINLINE void StructByValTestFunc1(struct StructByVal s) { StructByValTestFunc(s); } NOINLINE void StructByValTestFunc2(int z, struct StructByVal s) { StructByValTestFunc(s); } TEST(MemorySanitizer, StructByVal) { // Large aggregates are passed as "byval" pointer argument in LLVM. struct StructByVal s; s.a = 1; s.b = *GetPoisoned(); s.c = 2; s.d = *GetPoisoned(); s.e = 3; s.f = *GetPoisoned(); StructByValTestFunc(s); StructByValTestFunc1(s); StructByValTestFunc2(0, s); } #if MSAN_HAS_M128 NOINLINE __m128i m128Eq(__m128i *a, __m128i *b) { return _mm_cmpeq_epi16(*a, *b); } NOINLINE __m128i m128Lt(__m128i *a, __m128i *b) { return _mm_cmplt_epi16(*a, *b); } TEST(MemorySanitizer, m128) { __m128i a = _mm_set1_epi16(0x1234); __m128i b = _mm_set1_epi16(0x7890); EXPECT_NOT_POISONED(m128Eq(&a, &b)); EXPECT_NOT_POISONED(m128Lt(&a, &b)); } // FIXME: add more tests for __m128i. #endif // MSAN_HAS_M128 // We should not complain when copying this poisoned hole. struct StructWithHole { U4 a; // 4-byte hole. U8 b; }; NOINLINE StructWithHole ReturnStructWithHole() { StructWithHole res; __msan_poison(&res, sizeof(res)); res.a = 1; res.b = 2; return res; } TEST(MemorySanitizer, StructWithHole) { StructWithHole a = ReturnStructWithHole(); break_optimization(&a); } template NOINLINE T ReturnStruct() { T res; __msan_poison(&res, sizeof(res)); res.a = 1; return res; } template NOINLINE void TestReturnStruct() { T s1 = ReturnStruct(); EXPECT_NOT_POISONED(s1.a); EXPECT_POISONED(s1.b); } struct SSS1 { int a, b, c; }; struct SSS2 { int b, a, c; }; struct SSS3 { int b, c, a; }; struct SSS4 { int c, b, a; }; struct SSS5 { int a; float b; }; struct SSS6 { int a; double b; }; struct SSS7 { S8 b; int a; }; struct SSS8 { S2 b; S8 a; }; TEST(MemorySanitizer, IntStruct3) { TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); } struct LongStruct { U1 a1, b1; U2 a2, b2; U4 a4, b4; U8 a8, b8; }; NOINLINE LongStruct ReturnLongStruct1() { LongStruct res; __msan_poison(&res, sizeof(res)); res.a1 = res.a2 = res.a4 = res.a8 = 111; // leaves b1, .., b8 poisoned. return res; } NOINLINE LongStruct ReturnLongStruct2() { LongStruct res; __msan_poison(&res, sizeof(res)); res.b1 = res.b2 = res.b4 = res.b8 = 111; // leaves a1, .., a8 poisoned. return res; } TEST(MemorySanitizer, LongStruct) { LongStruct s1 = ReturnLongStruct1(); __msan_print_shadow(&s1, sizeof(s1)); EXPECT_NOT_POISONED(s1.a1); EXPECT_NOT_POISONED(s1.a2); EXPECT_NOT_POISONED(s1.a4); EXPECT_NOT_POISONED(s1.a8); EXPECT_POISONED(s1.b1); EXPECT_POISONED(s1.b2); EXPECT_POISONED(s1.b4); EXPECT_POISONED(s1.b8); LongStruct s2 = ReturnLongStruct2(); __msan_print_shadow(&s2, sizeof(s2)); EXPECT_NOT_POISONED(s2.b1); EXPECT_NOT_POISONED(s2.b2); EXPECT_NOT_POISONED(s2.b4); EXPECT_NOT_POISONED(s2.b8); EXPECT_POISONED(s2.a1); EXPECT_POISONED(s2.a2); EXPECT_POISONED(s2.a4); EXPECT_POISONED(s2.a8); } TEST(MemorySanitizer, getrlimit) { struct rlimit limit; __msan_poison(&limit, sizeof(limit)); int result = getrlimit(RLIMIT_DATA, &limit); ASSERT_EQ(result, 0); EXPECT_NOT_POISONED(limit.rlim_cur); EXPECT_NOT_POISONED(limit.rlim_max); } TEST(MemorySanitizer, getrusage) { struct rusage usage; __msan_poison(&usage, sizeof(usage)); int result = getrusage(RUSAGE_SELF, &usage); ASSERT_EQ(result, 0); EXPECT_NOT_POISONED(usage.ru_utime.tv_sec); EXPECT_NOT_POISONED(usage.ru_utime.tv_usec); EXPECT_NOT_POISONED(usage.ru_stime.tv_sec); EXPECT_NOT_POISONED(usage.ru_stime.tv_usec); EXPECT_NOT_POISONED(usage.ru_maxrss); EXPECT_NOT_POISONED(usage.ru_minflt); EXPECT_NOT_POISONED(usage.ru_majflt); EXPECT_NOT_POISONED(usage.ru_inblock); EXPECT_NOT_POISONED(usage.ru_oublock); EXPECT_NOT_POISONED(usage.ru_nvcsw); EXPECT_NOT_POISONED(usage.ru_nivcsw); } #ifdef __GLIBC__ extern char *program_invocation_name; #else // __GLIBC__ # error "TODO: port this" #endif static void dladdr_testfn() {} TEST(MemorySanitizer, dladdr) { Dl_info info; __msan_poison(&info, sizeof(info)); int result = dladdr((const void*)dladdr_testfn, &info); ASSERT_NE(result, 0); EXPECT_NOT_POISONED((unsigned long)info.dli_fname); if (info.dli_fname) EXPECT_NOT_POISONED(strlen(info.dli_fname)); EXPECT_NOT_POISONED((unsigned long)info.dli_fbase); EXPECT_NOT_POISONED((unsigned long)info.dli_sname); if (info.dli_sname) EXPECT_NOT_POISONED(strlen(info.dli_sname)); EXPECT_NOT_POISONED((unsigned long)info.dli_saddr); } #ifndef MSAN_TEST_DISABLE_DLOPEN static int dl_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) { (*(int *)data)++; EXPECT_NOT_POISONED(info->dlpi_addr); EXPECT_NOT_POISONED(strlen(info->dlpi_name)); EXPECT_NOT_POISONED(info->dlpi_phnum); for (int i = 0; i < info->dlpi_phnum; ++i) EXPECT_NOT_POISONED(info->dlpi_phdr[i]); return 0; } // Compute the path to our loadable DSO. We assume it's in the same // directory. Only use string routines that we intercept so far to do this. static int PathToLoadable(char *buf, size_t sz) { const char *basename = "libmsan_loadable.x86_64.so"; char *argv0 = program_invocation_name; char *last_slash = strrchr(argv0, '/'); assert(last_slash); int res = snprintf(buf, sz, "%.*s/%s", int(last_slash - argv0), argv0, basename); assert(res >= 0); return (size_t)res < sz ? 0 : res; } TEST(MemorySanitizer, dl_iterate_phdr) { char path[4096]; int res = PathToLoadable(path, sizeof(path)); ASSERT_EQ(0, res); // Having at least one dlopen'ed library in the process makes this more // entertaining. void *lib = dlopen(path, RTLD_LAZY); ASSERT_NE((void*)0, lib); int count = 0; int result = dl_iterate_phdr(dl_phdr_callback, &count); ASSERT_GT(count, 0); dlclose(lib); } TEST(MemorySanitizer, dlopen) { char path[4096]; int res = PathToLoadable(path, sizeof(path)); ASSERT_EQ(0, res); // We need to clear shadow for globals when doing dlopen. In order to test // this, we have to poison the shadow for the DSO before we load it. In // general this is difficult, but the loader tends to reload things in the // same place, so we open, close, and then reopen. The global should always // start out clean after dlopen. for (int i = 0; i < 2; i++) { void *lib = dlopen(path, RTLD_LAZY); if (lib == NULL) { printf("dlerror: %s\n", dlerror()); ASSERT_TRUE(lib != NULL); } void **(*get_dso_global)() = (void **(*)())dlsym(lib, "get_dso_global"); ASSERT_TRUE(get_dso_global != NULL); void **dso_global = get_dso_global(); EXPECT_NOT_POISONED(*dso_global); __msan_poison(dso_global, sizeof(*dso_global)); EXPECT_POISONED(*dso_global); dlclose(lib); } } // Regression test for a crash in dlopen() interceptor. TEST(MemorySanitizer, dlopenFailed) { const char *path = "/libmsan_loadable_does_not_exist.x86_64.so"; void *lib = dlopen(path, RTLD_LAZY); ASSERT_TRUE(lib == NULL); } #endif // MSAN_TEST_DISABLE_DLOPEN +// There's no sched_getaffinity() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, sched_getaffinity) { cpu_set_t mask; int res = sched_getaffinity(getpid(), sizeof(mask), &mask); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(mask); } +#endif TEST(MemorySanitizer, scanf) { const char *input = "42 hello"; int* d = new int; char* s = new char[7]; int res = sscanf(input, "%d %5s", d, s); printf("res %d\n", res); ASSERT_EQ(res, 2); EXPECT_NOT_POISONED(*d); EXPECT_NOT_POISONED(s[0]); EXPECT_NOT_POISONED(s[1]); EXPECT_NOT_POISONED(s[2]); EXPECT_NOT_POISONED(s[3]); EXPECT_NOT_POISONED(s[4]); EXPECT_NOT_POISONED(s[5]); EXPECT_POISONED(s[6]); delete[] s; delete d; } static void *SimpleThread_threadfn(void* data) { return new int; } TEST(MemorySanitizer, SimpleThread) { pthread_t t; void *p; int res = pthread_create(&t, NULL, SimpleThread_threadfn, NULL); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(t); res = pthread_join(t, &p); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(p); delete (int*)p; } static void *SmallStackThread_threadfn(void* data) { return 0; } TEST(MemorySanitizer, SmallStackThread) { pthread_attr_t attr; pthread_t t; void *p; int res; res = pthread_attr_init(&attr); ASSERT_EQ(0, res); res = pthread_attr_setstacksize(&attr, 64 * 1024); ASSERT_EQ(0, res); res = pthread_create(&t, &attr, SmallStackThread_threadfn, NULL); ASSERT_EQ(0, res); res = pthread_join(t, &p); ASSERT_EQ(0, res); res = pthread_attr_destroy(&attr); ASSERT_EQ(0, res); } TEST(MemorySanitizer, SmallPreAllocatedStackThread) { pthread_attr_t attr; pthread_t t; int res; res = pthread_attr_init(&attr); ASSERT_EQ(0, res); void *stack; const size_t kStackSize = 16 * 1024; res = posix_memalign(&stack, 4096, kStackSize); ASSERT_EQ(0, res); res = pthread_attr_setstack(&attr, stack, kStackSize); ASSERT_EQ(0, res); res = pthread_create(&t, &attr, SmallStackThread_threadfn, NULL); EXPECT_EQ(0, res); res = pthread_join(t, NULL); ASSERT_EQ(0, res); res = pthread_attr_destroy(&attr); ASSERT_EQ(0, res); } TEST(MemorySanitizer, pthread_attr_get) { pthread_attr_t attr; int res; res = pthread_attr_init(&attr); ASSERT_EQ(0, res); { int v; res = pthread_attr_getdetachstate(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { size_t v; res = pthread_attr_getguardsize(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { struct sched_param v; res = pthread_attr_getschedparam(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { int v; res = pthread_attr_getschedpolicy(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { int v; res = pthread_attr_getinheritsched(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { int v; res = pthread_attr_getscope(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { size_t v; res = pthread_attr_getstacksize(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { void *v; size_t w; res = pthread_attr_getstack(&attr, &v, &w); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); EXPECT_NOT_POISONED(w); } { cpu_set_t v; res = pthread_attr_getaffinity_np(&attr, sizeof(v), &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } res = pthread_attr_destroy(&attr); ASSERT_EQ(0, res); } TEST(MemorySanitizer, pthread_getschedparam) { int policy; struct sched_param param; int res = pthread_getschedparam(pthread_self(), &policy, ¶m); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(policy); EXPECT_NOT_POISONED(param.sched_priority); } TEST(MemorySanitizer, pthread_key_create) { pthread_key_t key; int res = pthread_key_create(&key, NULL); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(key); res = pthread_key_delete(key); ASSERT_EQ(0, res); } namespace { struct SignalCondArg { pthread_cond_t* cond; pthread_mutex_t* mu; bool broadcast; }; void *SignalCond(void *param) { SignalCondArg *arg = reinterpret_cast(param); pthread_mutex_lock(arg->mu); if (arg->broadcast) pthread_cond_broadcast(arg->cond); else pthread_cond_signal(arg->cond); pthread_mutex_unlock(arg->mu); return 0; } } // namespace TEST(MemorySanitizer, pthread_cond_wait) { pthread_cond_t cond; pthread_mutex_t mu; SignalCondArg args = {&cond, &mu, false}; pthread_cond_init(&cond, 0); pthread_mutex_init(&mu, 0); pthread_mutex_lock(&mu); // signal pthread_t thr; pthread_create(&thr, 0, SignalCond, &args); int res = pthread_cond_wait(&cond, &mu); ASSERT_EQ(0, res); pthread_join(thr, 0); // broadcast args.broadcast = true; pthread_create(&thr, 0, SignalCond, &args); res = pthread_cond_wait(&cond, &mu); ASSERT_EQ(0, res); pthread_join(thr, 0); pthread_mutex_unlock(&mu); pthread_mutex_destroy(&mu); pthread_cond_destroy(&cond); } TEST(MemorySanitizer, tmpnam) { char s[L_tmpnam]; char *res = tmpnam(s); ASSERT_EQ(s, res); EXPECT_NOT_POISONED(strlen(res)); } TEST(MemorySanitizer, tempnam) { char *res = tempnam(NULL, "zzz"); EXPECT_NOT_POISONED(strlen(res)); free(res); } TEST(MemorySanitizer, posix_memalign) { void *p; EXPECT_POISONED(p); int res = posix_memalign(&p, 4096, 13); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(p); EXPECT_EQ(0U, (uintptr_t)p % 4096); free(p); } +// There's no memalign() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, memalign) { void *p = memalign(4096, 13); EXPECT_EQ(0U, (uintptr_t)p % kPageSize); free(p); } +#endif TEST(MemorySanitizer, valloc) { void *a = valloc(100); EXPECT_EQ(0U, (uintptr_t)a % kPageSize); free(a); } +// There's no pvalloc() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, pvalloc) { void *p = pvalloc(kPageSize + 100); EXPECT_EQ(0U, (uintptr_t)p % kPageSize); EXPECT_EQ(2 * kPageSize, __sanitizer_get_allocated_size(p)); free(p); p = pvalloc(0); // pvalloc(0) should allocate at least one page. EXPECT_EQ(0U, (uintptr_t)p % kPageSize); EXPECT_EQ(kPageSize, __sanitizer_get_allocated_size(p)); free(p); } +#endif TEST(MemorySanitizer, inet_pton) { const char *s = "1:0:0:0:0:0:0:8"; unsigned char buf[sizeof(struct in6_addr)]; int res = inet_pton(AF_INET6, s, buf); ASSERT_EQ(1, res); EXPECT_NOT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[sizeof(struct in6_addr) - 1]); char s_out[INET6_ADDRSTRLEN]; EXPECT_POISONED(s_out[3]); const char *q = inet_ntop(AF_INET6, buf, s_out, INET6_ADDRSTRLEN); ASSERT_NE((void*)0, q); EXPECT_NOT_POISONED(s_out[3]); } TEST(MemorySanitizer, inet_aton) { const char *s = "127.0.0.1"; struct in_addr in[2]; int res = inet_aton(s, in); ASSERT_NE(0, res); EXPECT_NOT_POISONED(in[0]); EXPECT_POISONED(*(char *)(in + 1)); } TEST(MemorySanitizer, uname) { struct utsname u; int res = uname(&u); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(strlen(u.sysname)); EXPECT_NOT_POISONED(strlen(u.nodename)); EXPECT_NOT_POISONED(strlen(u.release)); EXPECT_NOT_POISONED(strlen(u.version)); EXPECT_NOT_POISONED(strlen(u.machine)); } TEST(MemorySanitizer, gethostname) { char buf[100]; int res = gethostname(buf, 100); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(strlen(buf)); } +// There's no sysinfo() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, sysinfo) { struct sysinfo info; int res = sysinfo(&info); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(info); } +#endif TEST(MemorySanitizer, getpwuid) { struct passwd *p = getpwuid(0); // root ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(p->pw_name); ASSERT_TRUE(p->pw_name != NULL); EXPECT_NOT_POISONED(p->pw_name[0]); EXPECT_NOT_POISONED(p->pw_uid); ASSERT_EQ(0U, p->pw_uid); } TEST(MemorySanitizer, getpwuid_r) { struct passwd pwd; struct passwd *pwdres; char buf[10000]; int res = getpwuid_r(0, &pwd, buf, sizeof(buf), &pwdres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pwd.pw_name); ASSERT_TRUE(pwd.pw_name != NULL); EXPECT_NOT_POISONED(pwd.pw_name[0]); EXPECT_NOT_POISONED(pwd.pw_uid); ASSERT_EQ(0U, pwd.pw_uid); EXPECT_NOT_POISONED(pwdres); } TEST(MemorySanitizer, getpwnam_r) { struct passwd pwd; struct passwd *pwdres; char buf[10000]; int res = getpwnam_r("root", &pwd, buf, sizeof(buf), &pwdres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pwd.pw_name); ASSERT_TRUE(pwd.pw_name != NULL); EXPECT_NOT_POISONED(pwd.pw_name[0]); EXPECT_NOT_POISONED(pwd.pw_uid); ASSERT_EQ(0U, pwd.pw_uid); EXPECT_NOT_POISONED(pwdres); } TEST(MemorySanitizer, getpwnam_r_positive) { struct passwd pwd; struct passwd *pwdres; char s[5]; strncpy(s, "abcd", 5); __msan_poison(s, 5); char buf[10000]; int res; EXPECT_UMR(res = getpwnam_r(s, &pwd, buf, sizeof(buf), &pwdres)); } TEST(MemorySanitizer, getgrnam_r) { struct group grp; struct group *grpres; char buf[10000]; int res = getgrnam_r("root", &grp, buf, sizeof(buf), &grpres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(grp.gr_name); ASSERT_TRUE(grp.gr_name != NULL); EXPECT_NOT_POISONED(grp.gr_name[0]); EXPECT_NOT_POISONED(grp.gr_gid); EXPECT_NOT_POISONED(grpres); } TEST(MemorySanitizer, getpwent) { setpwent(); struct passwd *p = getpwent(); ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(p->pw_name); ASSERT_TRUE(p->pw_name != NULL); EXPECT_NOT_POISONED(p->pw_name[0]); EXPECT_NOT_POISONED(p->pw_uid); } TEST(MemorySanitizer, getpwent_r) { struct passwd pwd; struct passwd *pwdres; char buf[10000]; setpwent(); int res = getpwent_r(&pwd, buf, sizeof(buf), &pwdres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pwd.pw_name); ASSERT_TRUE(pwd.pw_name != NULL); EXPECT_NOT_POISONED(pwd.pw_name[0]); EXPECT_NOT_POISONED(pwd.pw_uid); EXPECT_NOT_POISONED(pwdres); } +// There's no fgetpwent() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, fgetpwent) { FILE *fp = fopen("/etc/passwd", "r"); struct passwd *p = fgetpwent(fp); ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(p->pw_name); ASSERT_TRUE(p->pw_name != NULL); EXPECT_NOT_POISONED(p->pw_name[0]); EXPECT_NOT_POISONED(p->pw_uid); fclose(fp); } +#endif TEST(MemorySanitizer, getgrent) { setgrent(); struct group *p = getgrent(); ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(p->gr_name); ASSERT_TRUE(p->gr_name != NULL); EXPECT_NOT_POISONED(p->gr_name[0]); EXPECT_NOT_POISONED(p->gr_gid); } +// There's no fgetgrent() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, fgetgrent) { FILE *fp = fopen("/etc/group", "r"); struct group *grp = fgetgrent(fp); ASSERT_TRUE(grp != NULL); EXPECT_NOT_POISONED(grp->gr_name); ASSERT_TRUE(grp->gr_name != NULL); EXPECT_NOT_POISONED(grp->gr_name[0]); EXPECT_NOT_POISONED(grp->gr_gid); for (char **p = grp->gr_mem; *p; ++p) { EXPECT_NOT_POISONED((*p)[0]); EXPECT_TRUE(strlen(*p) > 0); } fclose(fp); } +#endif TEST(MemorySanitizer, getgrent_r) { struct group grp; struct group *grpres; char buf[10000]; setgrent(); int res = getgrent_r(&grp, buf, sizeof(buf), &grpres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(grp.gr_name); ASSERT_TRUE(grp.gr_name != NULL); EXPECT_NOT_POISONED(grp.gr_name[0]); EXPECT_NOT_POISONED(grp.gr_gid); EXPECT_NOT_POISONED(grpres); } TEST(MemorySanitizer, fgetgrent_r) { FILE *fp = fopen("/etc/group", "r"); struct group grp; struct group *grpres; char buf[10000]; setgrent(); int res = fgetgrent_r(fp, &grp, buf, sizeof(buf), &grpres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(grp.gr_name); ASSERT_TRUE(grp.gr_name != NULL); EXPECT_NOT_POISONED(grp.gr_name[0]); EXPECT_NOT_POISONED(grp.gr_gid); EXPECT_NOT_POISONED(grpres); fclose(fp); } TEST(MemorySanitizer, getgroups) { int n = getgroups(0, 0); gid_t *gids = new gid_t[n]; int res = getgroups(n, gids); ASSERT_EQ(n, res); for (int i = 0; i < n; ++i) EXPECT_NOT_POISONED(gids[i]); } TEST(MemorySanitizer, wordexp) { wordexp_t w; int res = wordexp("a b c", &w, 0); ASSERT_EQ(0, res); ASSERT_EQ(3U, w.we_wordc); ASSERT_STREQ("a", w.we_wordv[0]); ASSERT_STREQ("b", w.we_wordv[1]); ASSERT_STREQ("c", w.we_wordv[2]); } template static bool applySlt(T value, T shadow) { __msan_partial_poison(&value, &shadow, sizeof(T)); volatile bool zzz = true; // This "|| zzz" trick somehow makes LLVM emit "icmp slt" instead of // a shift-and-trunc to get at the highest bit. volatile bool v = value < 0 || zzz; return v; } TEST(MemorySanitizer, SignedCompareWithZero) { EXPECT_NOT_POISONED(applySlt(0xF, 0xF)); EXPECT_NOT_POISONED(applySlt(0xF, 0xFF)); EXPECT_NOT_POISONED(applySlt(0xF, 0xFFFFFF)); EXPECT_NOT_POISONED(applySlt(0xF, 0x7FFFFFF)); EXPECT_UMR(applySlt(0xF, 0x80FFFFFF)); EXPECT_UMR(applySlt(0xF, 0xFFFFFFFF)); } template static T poisoned(T Va, S Sa) { char SIZE_CHECK1[(ssize_t)sizeof(T) - (ssize_t)sizeof(S)]; char SIZE_CHECK2[(ssize_t)sizeof(S) - (ssize_t)sizeof(T)]; T a; a = Va; __msan_partial_poison(&a, &Sa, sizeof(T)); return a; } TEST(MemorySanitizer, ICmpRelational) { EXPECT_NOT_POISONED(poisoned(0, 0) < poisoned(0, 0)); EXPECT_NOT_POISONED(poisoned(0U, 0) < poisoned(0U, 0)); EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) < poisoned(0LL, 0LLU)); EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) < poisoned(0LLU, 0LLU)); EXPECT_POISONED(poisoned(0xFF, 0xFF) < poisoned(0xFF, 0xFF)); EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) < poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) < poisoned(-1, 0xFFFFFFFFU)); EXPECT_NOT_POISONED(poisoned(0, 0) <= poisoned(0, 0)); EXPECT_NOT_POISONED(poisoned(0U, 0) <= poisoned(0U, 0)); EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) <= poisoned(0LL, 0LLU)); EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) <= poisoned(0LLU, 0LLU)); EXPECT_POISONED(poisoned(0xFF, 0xFF) <= poisoned(0xFF, 0xFF)); EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) <= poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) <= poisoned(-1, 0xFFFFFFFFU)); EXPECT_NOT_POISONED(poisoned(0, 0) > poisoned(0, 0)); EXPECT_NOT_POISONED(poisoned(0U, 0) > poisoned(0U, 0)); EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) > poisoned(0LL, 0LLU)); EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) > poisoned(0LLU, 0LLU)); EXPECT_POISONED(poisoned(0xFF, 0xFF) > poisoned(0xFF, 0xFF)); EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) > poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) > poisoned(-1, 0xFFFFFFFFU)); EXPECT_NOT_POISONED(poisoned(0, 0) >= poisoned(0, 0)); EXPECT_NOT_POISONED(poisoned(0U, 0) >= poisoned(0U, 0)); EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) >= poisoned(0LL, 0LLU)); EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) >= poisoned(0LLU, 0LLU)); EXPECT_POISONED(poisoned(0xFF, 0xFF) >= poisoned(0xFF, 0xFF)); EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) >= poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) >= poisoned(-1, 0xFFFFFFFFU)); EXPECT_POISONED(poisoned(6, 0xF) > poisoned(7, 0)); EXPECT_POISONED(poisoned(0xF, 0xF) > poisoned(7, 0)); EXPECT_NOT_POISONED(poisoned(-1, 0x80000000U) >= poisoned(-1, 0U)); } #if MSAN_HAS_M128 TEST(MemorySanitizer, ICmpVectorRelational) { EXPECT_NOT_POISONED( _mm_cmplt_epi16(poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0)), poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0)))); EXPECT_NOT_POISONED( _mm_cmplt_epi16(poisoned(_mm_set1_epi32(0), _mm_set1_epi32(0)), poisoned(_mm_set1_epi32(0), _mm_set1_epi32(0)))); EXPECT_POISONED( _mm_cmplt_epi16(poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0xFFFF)), poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0xFFFF)))); EXPECT_POISONED(_mm_cmpgt_epi16(poisoned(_mm_set1_epi16(6), _mm_set1_epi16(0xF)), poisoned(_mm_set1_epi16(7), _mm_set1_epi16(0)))); } #endif // Volatile bitfield store is implemented as load-mask-store // Test that we don't warn on the store of (uninitialized) padding. struct VolatileBitfieldStruct { volatile unsigned x : 1; unsigned y : 1; }; TEST(MemorySanitizer, VolatileBitfield) { VolatileBitfieldStruct *S = new VolatileBitfieldStruct; S->x = 1; EXPECT_NOT_POISONED((unsigned)S->x); EXPECT_POISONED((unsigned)S->y); } TEST(MemorySanitizer, UnalignedLoad) { char x[32]; U4 origin = __LINE__; for (unsigned i = 0; i < sizeof(x) / 4; ++i) __msan_set_origin(x + 4 * i, 4, origin + i); memset(x + 8, 0, 16); EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 6), origin + 1); EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 7), origin + 1); EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 8)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 9)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 22)); EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 23), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 24), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 4), origin + 1); EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 7), origin + 1); EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 8)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 9)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 20)); EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 21), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 24), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x), origin); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 1), origin); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 7), origin + 1); EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 8)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 9)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 16)); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 17), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 21), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 24), origin + 6); } TEST(MemorySanitizer, UnalignedStore16) { char x[5]; U2 y2 = 0; U4 origin = __LINE__; __msan_poison(&y2, 1); __msan_set_origin(&y2, 1, origin); __sanitizer_unaligned_store16(x + 1, y2); EXPECT_POISONED_O(x[0], origin); EXPECT_POISONED_O(x[1], origin); EXPECT_NOT_POISONED(x[2]); EXPECT_POISONED_O(x[3], origin); EXPECT_POISONED_O(x[4], origin); } TEST(MemorySanitizer, UnalignedStore32) { char x[8]; U4 y4 = 0; U4 origin = __LINE__; __msan_poison(&y4, 2); __msan_set_origin(&y4, 2, origin); __sanitizer_unaligned_store32(x + 3, y4); EXPECT_POISONED_O(x[0], origin); EXPECT_POISONED_O(x[1], origin); EXPECT_POISONED_O(x[2], origin); EXPECT_POISONED_O(x[3], origin); EXPECT_POISONED_O(x[4], origin); EXPECT_NOT_POISONED(x[5]); EXPECT_NOT_POISONED(x[6]); EXPECT_POISONED_O(x[7], origin); } TEST(MemorySanitizer, UnalignedStore64) { char x[16]; U8 y8 = 0; U4 origin = __LINE__; __msan_poison(&y8, 3); __msan_poison(((char *)&y8) + sizeof(y8) - 2, 1); __msan_set_origin(&y8, 8, origin); __sanitizer_unaligned_store64(x + 3, y8); EXPECT_POISONED_O(x[0], origin); EXPECT_POISONED_O(x[1], origin); EXPECT_POISONED_O(x[2], origin); EXPECT_POISONED_O(x[3], origin); EXPECT_POISONED_O(x[4], origin); EXPECT_POISONED_O(x[5], origin); EXPECT_NOT_POISONED(x[6]); EXPECT_NOT_POISONED(x[7]); EXPECT_NOT_POISONED(x[8]); EXPECT_POISONED_O(x[9], origin); EXPECT_NOT_POISONED(x[10]); EXPECT_POISONED_O(x[11], origin); } TEST(MemorySanitizer, UnalignedStore16_precise) { char x[8]; U2 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; U4 originy = __LINE__; __msan_poison(x, sizeof(x)); __msan_set_origin(x, 4, originx1); __msan_set_origin(x + 4, 4, originx2); __msan_poison(((char *)&y) + 1, 1); __msan_set_origin(&y, sizeof(y), originy); __sanitizer_unaligned_store16(x + 3, y); EXPECT_POISONED_O(x[0], originx1); EXPECT_POISONED_O(x[1], originx1); EXPECT_POISONED_O(x[2], originx1); EXPECT_NOT_POISONED(x[3]); EXPECT_POISONED_O(x[4], originy); EXPECT_POISONED_O(x[5], originy); EXPECT_POISONED_O(x[6], originy); EXPECT_POISONED_O(x[7], originy); } TEST(MemorySanitizer, UnalignedStore16_precise2) { char x[8]; U2 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; U4 originy = __LINE__; __msan_poison(x, sizeof(x)); __msan_set_origin(x, 4, originx1); __msan_set_origin(x + 4, 4, originx2); __msan_poison(((char *)&y), 1); __msan_set_origin(&y, sizeof(y), originy); __sanitizer_unaligned_store16(x + 3, y); EXPECT_POISONED_O(x[0], originy); EXPECT_POISONED_O(x[1], originy); EXPECT_POISONED_O(x[2], originy); EXPECT_POISONED_O(x[3], originy); EXPECT_NOT_POISONED(x[4]); EXPECT_POISONED_O(x[5], originx2); EXPECT_POISONED_O(x[6], originx2); EXPECT_POISONED_O(x[7], originx2); } TEST(MemorySanitizer, UnalignedStore64_precise) { char x[12]; U8 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; U4 originx3 = __LINE__; U4 originy = __LINE__; __msan_poison(x, sizeof(x)); __msan_set_origin(x, 4, originx1); __msan_set_origin(x + 4, 4, originx2); __msan_set_origin(x + 8, 4, originx3); __msan_poison(((char *)&y) + 1, 1); __msan_poison(((char *)&y) + 7, 1); __msan_set_origin(&y, sizeof(y), originy); __sanitizer_unaligned_store64(x + 2, y); EXPECT_POISONED_O(x[0], originy); EXPECT_POISONED_O(x[1], originy); EXPECT_NOT_POISONED(x[2]); EXPECT_POISONED_O(x[3], originy); EXPECT_NOT_POISONED(x[4]); EXPECT_NOT_POISONED(x[5]); EXPECT_NOT_POISONED(x[6]); EXPECT_NOT_POISONED(x[7]); EXPECT_NOT_POISONED(x[8]); EXPECT_POISONED_O(x[9], originy); EXPECT_POISONED_O(x[10], originy); EXPECT_POISONED_O(x[11], originy); } TEST(MemorySanitizer, UnalignedStore64_precise2) { char x[12]; U8 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; U4 originx3 = __LINE__; U4 originy = __LINE__; __msan_poison(x, sizeof(x)); __msan_set_origin(x, 4, originx1); __msan_set_origin(x + 4, 4, originx2); __msan_set_origin(x + 8, 4, originx3); __msan_poison(((char *)&y) + 3, 3); __msan_set_origin(&y, sizeof(y), originy); __sanitizer_unaligned_store64(x + 2, y); EXPECT_POISONED_O(x[0], originx1); EXPECT_POISONED_O(x[1], originx1); EXPECT_NOT_POISONED(x[2]); EXPECT_NOT_POISONED(x[3]); EXPECT_NOT_POISONED(x[4]); EXPECT_POISONED_O(x[5], originy); EXPECT_POISONED_O(x[6], originy); EXPECT_POISONED_O(x[7], originy); EXPECT_NOT_POISONED(x[8]); EXPECT_NOT_POISONED(x[9]); EXPECT_POISONED_O(x[10], originx3); EXPECT_POISONED_O(x[11], originx3); } -#if defined(__clang__) +#if (defined(__x86_64__) && defined(__clang__)) namespace { typedef U1 V16x8 __attribute__((__vector_size__(16))); typedef U2 V8x16 __attribute__((__vector_size__(16))); typedef U4 V4x32 __attribute__((__vector_size__(16))); typedef U8 V2x64 __attribute__((__vector_size__(16))); typedef U4 V8x32 __attribute__((__vector_size__(32))); typedef U8 V4x64 __attribute__((__vector_size__(32))); typedef U4 V2x32 __attribute__((__vector_size__(8))); typedef U2 V4x16 __attribute__((__vector_size__(8))); typedef U1 V8x8 __attribute__((__vector_size__(8))); V8x16 shift_sse2_left_scalar(V8x16 x, U4 y) { return _mm_slli_epi16(x, y); } V8x16 shift_sse2_left(V8x16 x, V8x16 y) { return _mm_sll_epi16(x, y); } TEST(VectorShiftTest, sse2_left_scalar) { V8x16 v = {Poisoned(0, 3), Poisoned(0, 7), 2, 3, 4, 5, 6, 7}; V8x16 u = shift_sse2_left_scalar(v, 2); EXPECT_POISONED(u[0]); EXPECT_POISONED(u[1]); EXPECT_NOT_POISONED(u[0] | (3U << 2)); EXPECT_NOT_POISONED(u[1] | (7U << 2)); u[0] = u[1] = 0; EXPECT_NOT_POISONED(u); } TEST(VectorShiftTest, sse2_left_scalar_by_uninit) { V8x16 v = {0, 1, 2, 3, 4, 5, 6, 7}; V8x16 u = shift_sse2_left_scalar(v, Poisoned()); EXPECT_POISONED(u[0]); EXPECT_POISONED(u[1]); EXPECT_POISONED(u[2]); EXPECT_POISONED(u[3]); EXPECT_POISONED(u[4]); EXPECT_POISONED(u[5]); EXPECT_POISONED(u[6]); EXPECT_POISONED(u[7]); } TEST(VectorShiftTest, sse2_left) { V8x16 v = {Poisoned(0, 3), Poisoned(0, 7), 2, 3, 4, 5, 6, 7}; // Top 64 bits of shift count don't affect the result. V2x64 s = {2, Poisoned()}; V8x16 u = shift_sse2_left(v, s); EXPECT_POISONED(u[0]); EXPECT_POISONED(u[1]); EXPECT_NOT_POISONED(u[0] | (3U << 2)); EXPECT_NOT_POISONED(u[1] | (7U << 2)); u[0] = u[1] = 0; EXPECT_NOT_POISONED(u); } TEST(VectorShiftTest, sse2_left_by_uninit) { V8x16 v = {Poisoned(0, 3), Poisoned(0, 7), 2, 3, 4, 5, 6, 7}; V2x64 s = {Poisoned(), Poisoned()}; V8x16 u = shift_sse2_left(v, s); EXPECT_POISONED(u[0]); EXPECT_POISONED(u[1]); EXPECT_POISONED(u[2]); EXPECT_POISONED(u[3]); EXPECT_POISONED(u[4]); EXPECT_POISONED(u[5]); EXPECT_POISONED(u[6]); EXPECT_POISONED(u[7]); } #ifdef __AVX2__ V4x32 shift_avx2_left(V4x32 x, V4x32 y) { return _mm_sllv_epi32(x, y); } // This is variable vector shift that's only available starting with AVX2. // V4x32 shift_avx2_left(V4x32 x, V4x32 y) { TEST(VectorShiftTest, avx2_left) { V4x32 v = {Poisoned(0, 3), Poisoned(0, 7), 2, 3}; V4x32 s = {2, Poisoned(), 3, Poisoned()}; V4x32 u = shift_avx2_left(v, s); EXPECT_POISONED(u[0]); EXPECT_NOT_POISONED(u[0] | (~7U)); EXPECT_POISONED(u[1]); EXPECT_POISONED(u[1] | (~31U)); EXPECT_NOT_POISONED(u[2]); EXPECT_POISONED(u[3]); EXPECT_POISONED(u[3] | (~31U)); } #endif // __AVX2__ } // namespace TEST(VectorPackTest, sse2_packssdw_128) { const unsigned S2_max = (1 << 15) - 1; V4x32 a = {Poisoned(0, 0xFF0000), Poisoned(0, 0xFFFF0000), S2_max + 100, 4}; V4x32 b = {Poisoned(0, 0xFF), S2_max + 10000, Poisoned(0, 0xFF00), S2_max}; V8x16 c = _mm_packs_epi32(a, b); EXPECT_POISONED(c[0]); EXPECT_POISONED(c[1]); EXPECT_NOT_POISONED(c[2]); EXPECT_NOT_POISONED(c[3]); EXPECT_POISONED(c[4]); EXPECT_NOT_POISONED(c[5]); EXPECT_POISONED(c[6]); EXPECT_NOT_POISONED(c[7]); EXPECT_EQ(c[2], S2_max); EXPECT_EQ(c[3], 4); EXPECT_EQ(c[5], S2_max); EXPECT_EQ(c[7], S2_max); } TEST(VectorPackTest, mmx_packuswb) { const unsigned U1_max = (1 << 8) - 1; V4x16 a = {Poisoned(0, 0xFF00), Poisoned(0, 0xF000U), U1_max + 100, 4}; V4x16 b = {Poisoned(0, 0xFF), U1_max - 1, Poisoned(0, 0xF), U1_max}; V8x8 c = _mm_packs_pu16(a, b); EXPECT_POISONED(c[0]); EXPECT_POISONED(c[1]); EXPECT_NOT_POISONED(c[2]); EXPECT_NOT_POISONED(c[3]); EXPECT_POISONED(c[4]); EXPECT_NOT_POISONED(c[5]); EXPECT_POISONED(c[6]); EXPECT_NOT_POISONED(c[7]); EXPECT_EQ(c[2], U1_max); EXPECT_EQ(c[3], 4); EXPECT_EQ(c[5], U1_max - 1); EXPECT_EQ(c[7], U1_max); } TEST(VectorSadTest, sse2_psad_bw) { V16x8 a = {Poisoned(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; V16x8 b = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115}; V2x64 c = _mm_sad_epu8(a, b); EXPECT_POISONED(c[0]); EXPECT_NOT_POISONED(c[1]); EXPECT_EQ(800U, c[1]); } TEST(VectorMaddTest, mmx_pmadd_wd) { V4x16 a = {Poisoned(), 1, 2, 3}; V4x16 b = {100, 101, 102, 103}; V2x32 c = _mm_madd_pi16(a, b); EXPECT_POISONED(c[0]); EXPECT_NOT_POISONED(c[1]); EXPECT_EQ((unsigned)(2 * 102 + 3 * 103), c[1]); } #endif // defined(__clang__) TEST(MemorySanitizerOrigins, SetGet) { 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)); __msan_set_origin(&x, sizeof(x), 5678); EXPECT_EQ(5678U, __msan_get_origin(&x)); __msan_set_origin(&x, sizeof(x), 0); EXPECT_EQ(0U, __msan_get_origin(&x)); } namespace { struct S { U4 dummy; U2 a; U2 b; }; TEST(MemorySanitizerOrigins, InitializedStoreDoesNotChangeOrigin) { if (!TrackingOrigins()) return; S s; U4 origin = rand(); // NOLINT s.a = *GetPoisonedO(0, origin); EXPECT_EQ(origin, __msan_get_origin(&s.a)); EXPECT_EQ(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)); } } // namespace template INLINE void BinaryOpOriginTest(BinaryOp op) { U4 ox = rand(); //NOLINT U4 oy = rand(); //NOLINT T *x = GetPoisonedO(0, ox, 0); T *y = GetPoisonedO(1, oy, 0); T *z = GetPoisonedO(2, 0, 0); *z = op(*x, *y); U4 origin = __msan_get_origin(z); EXPECT_POISONED_O(*z, origin); EXPECT_EQ(true, origin == ox || origin == oy); // y is poisoned, x is not. *x = 10101; *y = *GetPoisonedO(1, oy); break_optimization(x); __msan_set_origin(z, sizeof(*z), 0); *z = op(*x, *y); EXPECT_POISONED_O(*z, oy); EXPECT_EQ(__msan_get_origin(z), oy); // x is poisoned, y is not. *x = *GetPoisonedO(0, ox); *y = 10101010; break_optimization(y); __msan_set_origin(z, sizeof(*z), 0); *z = op(*x, *y); EXPECT_POISONED_O(*z, ox); EXPECT_EQ(__msan_get_origin(z), ox); } template INLINE T XOR(const T &a, const T&b) { return a ^ b; } template INLINE T ADD(const T &a, const T&b) { return a + b; } template INLINE T SUB(const T &a, const T&b) { return a - b; } template INLINE T MUL(const T &a, const T&b) { return a * b; } template INLINE T AND(const T &a, const T&b) { return a & b; } template INLINE T OR (const T &a, const T&b) { return a | b; } TEST(MemorySanitizerOrigins, BinaryOp) { if (!TrackingOrigins()) return; BinaryOpOriginTest(XOR); BinaryOpOriginTest(ADD); BinaryOpOriginTest(SUB); BinaryOpOriginTest(MUL); BinaryOpOriginTest(OR); BinaryOpOriginTest(AND); BinaryOpOriginTest(ADD); BinaryOpOriginTest(ADD); BinaryOpOriginTest(ADD); BinaryOpOriginTest(ADD); } TEST(MemorySanitizerOrigins, Unary) { if (!TrackingOrigins()) return; EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O((void*)*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O((U8)*GetPoisonedO(0, __LINE__), __LINE__); } TEST(MemorySanitizerOrigins, EQ) { if (!TrackingOrigins()) return; EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) <= 11, __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) == 11, __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) == 1.1, __LINE__); } TEST(MemorySanitizerOrigins, DIV) { if (!TrackingOrigins()) return; EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) / 100, __LINE__); unsigned o = __LINE__; EXPECT_UMR_O(volatile unsigned y = 100 / *GetPoisonedO(0, o, 1), o); } TEST(MemorySanitizerOrigins, SHIFT) { if (!TrackingOrigins()) return; EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) >> 10, __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) >> 10, __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) << 10, __LINE__); EXPECT_POISONED_O(10U << *GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(-10 >> *GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(-10 << *GetPoisonedO(0, __LINE__), __LINE__); } template void MemCpyTest() { int ox = __LINE__; T *x = new T[N]; T *y = new T[N]; T *z = new T[N]; T *q = new T[N]; __msan_poison(x, N * sizeof(T)); __msan_set_origin(x, N * sizeof(T), ox); __msan_set_origin(y, N * sizeof(T), 777777); __msan_set_origin(z, N * sizeof(T), 888888); EXPECT_NOT_POISONED(x); memcpy(y, x, N * sizeof(T)); EXPECT_POISONED_O(y[0], ox); EXPECT_POISONED_O(y[N/2], ox); EXPECT_POISONED_O(y[N-1], ox); EXPECT_NOT_POISONED(x); void *res = mempcpy(q, x, N * sizeof(T)); ASSERT_EQ(q + N, res); EXPECT_POISONED_O(q[0], ox); EXPECT_POISONED_O(q[N/2], ox); EXPECT_POISONED_O(q[N-1], ox); EXPECT_NOT_POISONED(x); memmove(z, x, N * sizeof(T)); EXPECT_POISONED_O(z[0], ox); EXPECT_POISONED_O(z[N/2], ox); EXPECT_POISONED_O(z[N-1], ox); } TEST(MemorySanitizerOrigins, LargeMemCpy) { if (!TrackingOrigins()) return; MemCpyTest(); MemCpyTest(); } TEST(MemorySanitizerOrigins, SmallMemCpy) { if (!TrackingOrigins()) return; MemCpyTest(); MemCpyTest(); MemCpyTest(); } TEST(MemorySanitizerOrigins, Select) { if (!TrackingOrigins()) return; EXPECT_NOT_POISONED(g_one ? 1 : *GetPoisonedO(0, __LINE__)); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); S4 x; break_optimization(&x); x = g_1 ? *GetPoisonedO(0, __LINE__) : 0; EXPECT_POISONED_O(g_1 ? *GetPoisonedO(0, __LINE__) : 1, __LINE__); EXPECT_POISONED_O(g_0 ? 1 : *GetPoisonedO(0, __LINE__), __LINE__); } NOINLINE int RetvalOriginTest(U4 origin) { int *a = new int; break_optimization(a); __msan_set_origin(a, sizeof(*a), origin); int res = *a; delete a; return res; } TEST(MemorySanitizerOrigins, Retval) { if (!TrackingOrigins()) return; EXPECT_POISONED_O(RetvalOriginTest(__LINE__), __LINE__); } NOINLINE void ParamOriginTest(int param, U4 origin) { EXPECT_POISONED_O(param, origin); } TEST(MemorySanitizerOrigins, Param) { if (!TrackingOrigins()) return; int *a = new int; U4 origin = __LINE__; break_optimization(a); __msan_set_origin(a, sizeof(*a), origin); ParamOriginTest(*a, origin); delete a; } TEST(MemorySanitizerOrigins, Invoke) { if (!TrackingOrigins()) return; StructWithDtor s; // Will cause the calls to become invokes. EXPECT_POISONED_O(RetvalOriginTest(__LINE__), __LINE__); } TEST(MemorySanitizerOrigins, strlen) { S8 alignment; break_optimization(&alignment); char x[4] = {'a', 'b', 0, 0}; __msan_poison(&x[2], 1); U4 origin = __LINE__; __msan_set_origin(x, sizeof(x), origin); EXPECT_UMR_O(volatile unsigned y = strlen(x), origin); } TEST(MemorySanitizerOrigins, wcslen) { wchar_t w[3] = {'a', 'b', 0}; U4 origin = __LINE__; __msan_set_origin(w, sizeof(w), origin); __msan_poison(&w[2], sizeof(wchar_t)); EXPECT_UMR_O(volatile unsigned y = wcslen(w), origin); } #if MSAN_HAS_M128 TEST(MemorySanitizerOrigins, StoreIntrinsic) { __m128 x, y; U4 origin = __LINE__; __msan_set_origin(&x, sizeof(x), origin); __msan_poison(&x, sizeof(x)); __builtin_ia32_storeups((float*)&y, x); EXPECT_POISONED_O(y, origin); } #endif NOINLINE void RecursiveMalloc(int depth) { static int count; count++; if ((count % (1024 * 1024)) == 0) printf("RecursiveMalloc: %d\n", count); int *x1 = new int; int *x2 = new int; break_optimization(x1); break_optimization(x2); if (depth > 0) { RecursiveMalloc(depth-1); RecursiveMalloc(depth-1); } delete x1; delete x2; } TEST(MemorySanitizer, Select) { int x; int volatile* p = &x; int z = *p ? 1 : 0; EXPECT_POISONED(z); } TEST(MemorySanitizer, SelectPartial) { // Precise instrumentation of select. // Some bits of the result do not depend on select condition, and must stay // initialized even if select condition is not. These are the bits that are // equal and initialized in both left and right select arguments. U4 x = 0xFFFFABCDU; U4 x_s = 0xFFFF0000U; __msan_partial_poison(&x, &x_s, sizeof(x)); U4 y = 0xAB00U; U1 cond = true; __msan_poison(&cond, sizeof(cond)); U4 z = cond ? x : y; __msan_print_shadow(&z, sizeof(z)); EXPECT_POISONED(z & 0xFFU); EXPECT_NOT_POISONED(z & 0xFF00U); EXPECT_POISONED(z & 0xFF0000U); EXPECT_POISONED(z & 0xFF000000U); EXPECT_EQ(0xAB00U, z & 0xFF00U); } TEST(MemorySanitizerStress, DISABLED_MallocStackTrace) { RecursiveMalloc(22); } TEST(MemorySanitizerAllocator, get_estimated_allocated_size) { size_t sizes[] = {0, 20, 5000, 1<<20}; for (size_t i = 0; i < sizeof(sizes) / sizeof(*sizes); ++i) { size_t alloc_size = __sanitizer_get_estimated_allocated_size(sizes[i]); EXPECT_EQ(alloc_size, sizes[i]); } } TEST(MemorySanitizerAllocator, get_allocated_size_and_ownership) { char *array = reinterpret_cast(malloc(100)); int *int_ptr = new int; EXPECT_TRUE(__sanitizer_get_ownership(array)); EXPECT_EQ(100U, __sanitizer_get_allocated_size(array)); EXPECT_TRUE(__sanitizer_get_ownership(int_ptr)); EXPECT_EQ(sizeof(*int_ptr), __sanitizer_get_allocated_size(int_ptr)); void *wild_addr = reinterpret_cast(0x1); EXPECT_FALSE(__sanitizer_get_ownership(wild_addr)); EXPECT_EQ(0U, __sanitizer_get_allocated_size(wild_addr)); EXPECT_FALSE(__sanitizer_get_ownership(array + 50)); EXPECT_EQ(0U, __sanitizer_get_allocated_size(array + 50)); // NULL is a valid argument for GetAllocatedSize but is not owned. EXPECT_FALSE(__sanitizer_get_ownership(NULL)); EXPECT_EQ(0U, __sanitizer_get_allocated_size(NULL)); free(array); EXPECT_FALSE(__sanitizer_get_ownership(array)); EXPECT_EQ(0U, __sanitizer_get_allocated_size(array)); delete int_ptr; } TEST(MemorySanitizer, MlockTest) { EXPECT_EQ(0, mlockall(MCL_CURRENT)); EXPECT_EQ(0, mlock((void*)0x12345, 0x5678)); EXPECT_EQ(0, munlockall()); EXPECT_EQ(0, munlock((void*)0x987, 0x654)); } // Test that LargeAllocator unpoisons memory before releasing it to the OS. TEST(MemorySanitizer, LargeAllocatorUnpoisonsOnFree) { void *p = malloc(1024 * 1024); free(p); typedef void *(*mmap_fn)(void *, size_t, int, int, int, off_t); mmap_fn real_mmap = (mmap_fn)dlsym(RTLD_NEXT, "mmap"); // Allocate the page that was released to the OS in free() with the real mmap, // bypassing the interceptor. - char *q = (char *)real_mmap(p, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + char *q = (char *)real_mmap(p, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_NE((char *)0, q); ASSERT_TRUE(q <= p); ASSERT_TRUE(q + 4096 > p); EXPECT_NOT_POISONED(q[0]); EXPECT_NOT_POISONED(q[10]); EXPECT_NOT_POISONED(q[100]); munmap(q, 4096); } #if SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE TEST(MemorySanitizer, MallocUsableSizeTest) { const size_t kArraySize = 100; char *array = Ident((char*)malloc(kArraySize)); int *int_ptr = Ident(new int); EXPECT_EQ(0U, malloc_usable_size(NULL)); EXPECT_EQ(kArraySize, malloc_usable_size(array)); EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr)); free(array); delete int_ptr; } #endif // SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE Index: projects/clang360-import/contrib/compiler-rt/lib/profile/InstrProfilingFile.c =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/profile/InstrProfilingFile.c (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/profile/InstrProfilingFile.c (revision 279191) @@ -1,194 +1,199 @@ /*===- InstrProfilingFile.c - Write instrumentation to a file -------------===*\ |* |* The LLVM Compiler Infrastructure |* |* This file is distributed under the University of Illinois Open Source |* License. See LICENSE.TXT for details. |* \*===----------------------------------------------------------------------===*/ #include "InstrProfiling.h" #include #include #include +#include #define UNCONST(ptr) ((void *)(uintptr_t)(ptr)) static int writeFile(FILE *File) { /* Match logic in __llvm_profile_write_buffer(). */ const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); const uint64_t *CountersBegin = __llvm_profile_begin_counters(); const uint64_t *CountersEnd = __llvm_profile_end_counters(); const char *NamesBegin = __llvm_profile_begin_names(); const char *NamesEnd = __llvm_profile_end_names(); /* Calculate size of sections. */ const uint64_t DataSize = DataEnd - DataBegin; const uint64_t CountersSize = CountersEnd - CountersBegin; const uint64_t NamesSize = NamesEnd - NamesBegin; const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t); /* Enough zeroes for padding. */ const char Zeroes[sizeof(uint64_t)] = {0}; /* Create the header. */ uint64_t Header[PROFILE_HEADER_SIZE]; Header[0] = __llvm_profile_get_magic(); Header[1] = __llvm_profile_get_version(); Header[2] = DataSize; Header[3] = CountersSize; Header[4] = NamesSize; Header[5] = (uintptr_t)CountersBegin; Header[6] = (uintptr_t)NamesBegin; /* Write the data. */ #define CHECK_fwrite(Data, Size, Length, File) \ do { if (fwrite(Data, Size, Length, File) != Length) return -1; } while (0) CHECK_fwrite(Header, sizeof(uint64_t), PROFILE_HEADER_SIZE, File); CHECK_fwrite(DataBegin, sizeof(__llvm_profile_data), DataSize, File); CHECK_fwrite(CountersBegin, sizeof(uint64_t), CountersSize, File); CHECK_fwrite(NamesBegin, sizeof(char), NamesSize, File); CHECK_fwrite(Zeroes, sizeof(char), Padding, File); #undef CHECK_fwrite return 0; } static int writeFileWithName(const char *OutputName) { int RetVal; FILE *OutputFile; if (!OutputName || !OutputName[0]) return -1; /* Append to the file to support profiling multiple shared objects. */ OutputFile = fopen(OutputName, "a"); if (!OutputFile) return -1; RetVal = writeFile(OutputFile); fclose(OutputFile); return RetVal; } __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; if (!Filename || !Filename[0]) return; /* Truncate the file. Later we'll reopen and append. */ FILE *File = fopen(Filename, "w"); if (!File) return; fclose(File); } static void setDefaultFilename(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. */ #define MAX_PID_SIZE 16 char PidChars[MAX_PID_SIZE] = {0}; int NumPids = 0; int PidLength = 0; int I; for (I = 0; Filename[I]; ++I) if (Filename[I] == '%' && Filename[++I] == 'p') if (!NumPids++) { PidLength = snprintf(PidChars, MAX_PID_SIZE, "%d", getpid()); if (PidLength <= 0) return -1; } if (!NumPids) { setFilename(Filename, 0); return 0; } /* Allocate enough space for the substituted filename. */ char *Allocated = (char*)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') { memcpy(Allocated + J, PidChars, PidLength); J += PidLength; } /* Drop any unknown substitutions. */ } else Allocated[J++] = Filename[I]; Allocated[J] = 0; /* Use the computed name. */ setFilename(Allocated, 1); return 0; } static void setFilenameAutomatically(void) { if (!setFilenameFromEnvironment()) return; setDefaultFilename(); } __attribute__((visibility("hidden"))) void __llvm_profile_initialize_file(void) { /* Check if the filename has been initialized. */ if (__llvm_profile_CurrentFilename) return; /* Detect the filename and truncate. */ setFilenameAutomatically(); truncateCurrentFile(); } __attribute__((visibility("hidden"))) void __llvm_profile_set_filename(const char *Filename) { setFilename(Filename, 0); truncateCurrentFile(); } __attribute__((visibility("hidden"))) int __llvm_profile_write_file(void) { /* Check the filename. */ if (!__llvm_profile_CurrentFilename) return -1; /* Write the file. */ - return writeFileWithName(__llvm_profile_CurrentFilename); + int 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)); + return rc; } static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); } __attribute__((visibility("hidden"))) int __llvm_profile_register_write_file_atexit(void) { static int HasBeenRegistered = 0; if (HasBeenRegistered) return 0; HasBeenRegistered = 1; return atexit(writeFileWithoutReturn); } Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.cc (revision 279191) @@ -1,153 +1,150 @@ //===-- sanitizer_allocator.cc --------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. // This allocator is used inside run-times. //===----------------------------------------------------------------------===// #include "sanitizer_allocator.h" #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" -#include "sanitizer_flags.h" namespace __sanitizer { // ThreadSanitizer for Go uses libc malloc/free. #if defined(SANITIZER_GO) || defined(SANITIZER_USE_MALLOC) # if SANITIZER_LINUX && !SANITIZER_ANDROID extern "C" void *__libc_malloc(uptr size); extern "C" void __libc_free(void *ptr); # define LIBC_MALLOC __libc_malloc # define LIBC_FREE __libc_free # else # include # define LIBC_MALLOC malloc # define LIBC_FREE free # endif static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { (void)cache; return LIBC_MALLOC(size); } static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { (void)cache; LIBC_FREE(ptr); } InternalAllocator *internal_allocator() { return 0; } #else // SANITIZER_GO static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)]; static atomic_uint8_t internal_allocator_initialized; static StaticSpinMutex internal_alloc_init_mu; static InternalAllocatorCache internal_allocator_cache; static StaticSpinMutex internal_allocator_cache_mu; InternalAllocator *internal_allocator() { InternalAllocator *internal_allocator_instance = reinterpret_cast(&internal_alloc_placeholder); if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) { SpinMutexLock l(&internal_alloc_init_mu); if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) == 0) { - internal_allocator_instance->Init(); + internal_allocator_instance->Init(/* may_return_null*/ false); atomic_store(&internal_allocator_initialized, 1, memory_order_release); } } return internal_allocator_instance; } static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) { if (cache == 0) { SpinMutexLock l(&internal_allocator_cache_mu); return internal_allocator()->Allocate(&internal_allocator_cache, size, 8, false); } return internal_allocator()->Allocate(cache, size, 8, false); } static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { if (cache == 0) { SpinMutexLock l(&internal_allocator_cache_mu); return internal_allocator()->Deallocate(&internal_allocator_cache, ptr); } internal_allocator()->Deallocate(cache, ptr); } #endif // SANITIZER_GO const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull; void *InternalAlloc(uptr size, InternalAllocatorCache *cache) { if (size + sizeof(u64) < size) return 0; void *p = RawInternalAlloc(size + sizeof(u64), cache); if (p == 0) return 0; ((u64*)p)[0] = kBlockMagic; return (char*)p + sizeof(u64); } void InternalFree(void *addr, InternalAllocatorCache *cache) { if (addr == 0) return; addr = (char*)addr - sizeof(u64); CHECK_EQ(kBlockMagic, ((u64*)addr)[0]); ((u64*)addr)[0] = 0; RawInternalFree(addr, cache); } // LowLevelAllocator static LowLevelAllocateCallback low_level_alloc_callback; void *LowLevelAllocator::Allocate(uptr size) { // Align allocation size. size = RoundUpTo(size, 8); if (allocated_end_ - allocated_current_ < (sptr)size) { uptr size_to_allocate = Max(size, GetPageSizeCached()); allocated_current_ = (char*)MmapOrDie(size_to_allocate, __func__); allocated_end_ = allocated_current_ + size_to_allocate; if (low_level_alloc_callback) { low_level_alloc_callback((uptr)allocated_current_, size_to_allocate); } } CHECK(allocated_end_ - allocated_current_ >= (sptr)size); void *res = allocated_current_; allocated_current_ += size; return res; } void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) { low_level_alloc_callback = callback; } bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) { if (!size) return false; uptr max = (uptr)-1L; return (max / size) < n; } -void *AllocatorReturnNull() { - if (common_flags()->allocator_may_return_null) - return 0; +void NORETURN ReportAllocatorCannotReturnNull() { Report("%s's allocator is terminating the process instead of returning 0\n", SanitizerToolName); Report("If you don't like this behavior set allocator_may_return_null=1\n"); CHECK(0); - return 0; + Die(); } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator.h (revision 279191) @@ -1,1390 +1,1451 @@ //===-- sanitizer_allocator.h -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Specialized memory allocator for ThreadSanitizer, MemorySanitizer, etc. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_ALLOCATOR_H #define SANITIZER_ALLOCATOR_H #include "sanitizer_internal_defs.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_list.h" #include "sanitizer_mutex.h" #include "sanitizer_lfstack.h" namespace __sanitizer { -// Depending on allocator_may_return_null either return 0 or crash. -void *AllocatorReturnNull(); +// Prints error message and kills the program. +void NORETURN ReportAllocatorCannotReturnNull(); // SizeClassMap maps allocation sizes into size classes and back. // Class 0 corresponds to size 0. // Classes 1 - 16 correspond to sizes 16 to 256 (size = class_id * 16). // Next 4 classes: 256 + i * 64 (i = 1 to 4). // Next 4 classes: 512 + i * 128 (i = 1 to 4). // ... // Next 4 classes: 2^k + i * 2^(k-2) (i = 1 to 4). // Last class corresponds to kMaxSize = 1 << kMaxSizeLog. // // This structure of the size class map gives us: // - Efficient table-free class-to-size and size-to-class functions. // - Difference between two consequent size classes is betweed 14% and 25% // // This class also gives a hint to a thread-caching allocator about the amount // of chunks that need to be cached per-thread: // - kMaxNumCached is the maximal number of chunks per size class. // - (1 << kMaxBytesCachedLog) is the maximal number of bytes per size class. // // Part of output of SizeClassMap::Print(): // c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0 // c01 => s: 16 diff: +16 00% l 4 cached: 256 4096; id 1 // c02 => s: 32 diff: +16 100% l 5 cached: 256 8192; id 2 // c03 => s: 48 diff: +16 50% l 5 cached: 256 12288; id 3 // c04 => s: 64 diff: +16 33% l 6 cached: 256 16384; id 4 // c05 => s: 80 diff: +16 25% l 6 cached: 256 20480; id 5 // c06 => s: 96 diff: +16 20% l 6 cached: 256 24576; id 6 // c07 => s: 112 diff: +16 16% l 6 cached: 256 28672; id 7 // // c08 => s: 128 diff: +16 14% l 7 cached: 256 32768; id 8 // c09 => s: 144 diff: +16 12% l 7 cached: 256 36864; id 9 // c10 => s: 160 diff: +16 11% l 7 cached: 256 40960; id 10 // c11 => s: 176 diff: +16 10% l 7 cached: 256 45056; id 11 // c12 => s: 192 diff: +16 09% l 7 cached: 256 49152; id 12 // c13 => s: 208 diff: +16 08% l 7 cached: 256 53248; id 13 // c14 => s: 224 diff: +16 07% l 7 cached: 256 57344; id 14 // c15 => s: 240 diff: +16 07% l 7 cached: 256 61440; id 15 // // c16 => s: 256 diff: +16 06% l 8 cached: 256 65536; id 16 // c17 => s: 320 diff: +64 25% l 8 cached: 204 65280; id 17 // c18 => s: 384 diff: +64 20% l 8 cached: 170 65280; id 18 // c19 => s: 448 diff: +64 16% l 8 cached: 146 65408; id 19 // // c20 => s: 512 diff: +64 14% l 9 cached: 128 65536; id 20 // c21 => s: 640 diff: +128 25% l 9 cached: 102 65280; id 21 // c22 => s: 768 diff: +128 20% l 9 cached: 85 65280; id 22 // c23 => s: 896 diff: +128 16% l 9 cached: 73 65408; id 23 // // c24 => s: 1024 diff: +128 14% l 10 cached: 64 65536; id 24 // c25 => s: 1280 diff: +256 25% l 10 cached: 51 65280; id 25 // c26 => s: 1536 diff: +256 20% l 10 cached: 42 64512; id 26 // c27 => s: 1792 diff: +256 16% l 10 cached: 36 64512; id 27 // // ... // // c48 => s: 65536 diff: +8192 14% l 16 cached: 1 65536; id 48 // c49 => s: 81920 diff: +16384 25% l 16 cached: 1 81920; id 49 // c50 => s: 98304 diff: +16384 20% l 16 cached: 1 98304; id 50 // c51 => s: 114688 diff: +16384 16% l 16 cached: 1 114688; id 51 // // c52 => s: 131072 diff: +16384 14% l 17 cached: 1 131072; id 52 template class SizeClassMap { static const uptr kMinSizeLog = 4; static const uptr kMidSizeLog = kMinSizeLog + 4; static const uptr kMinSize = 1 << kMinSizeLog; static const uptr kMidSize = 1 << kMidSizeLog; static const uptr kMidClass = kMidSize / kMinSize; static const uptr S = 2; static const uptr M = (1 << S) - 1; public: static const uptr kMaxNumCached = kMaxNumCachedT; // We transfer chunks between central and thread-local free lists in batches. // For small size classes we allocate batches separately. // For large size classes we use one of the chunks to store the batch. struct TransferBatch { TransferBatch *next; uptr count; void *batch[kMaxNumCached]; }; static const uptr kMaxSize = 1UL << kMaxSizeLog; static const uptr kNumClasses = kMidClass + ((kMaxSizeLog - kMidSizeLog) << S) + 1; COMPILER_CHECK(kNumClasses >= 32 && kNumClasses <= 256); static const uptr kNumClassesRounded = kNumClasses == 32 ? 32 : kNumClasses <= 64 ? 64 : kNumClasses <= 128 ? 128 : 256; static uptr Size(uptr class_id) { if (class_id <= kMidClass) return kMinSize * class_id; class_id -= kMidClass; uptr t = kMidSize << (class_id >> S); return t + (t >> S) * (class_id & M); } static uptr ClassID(uptr size) { if (size <= kMidSize) return (size + kMinSize - 1) >> kMinSizeLog; if (size > kMaxSize) return 0; uptr l = MostSignificantSetBitIndex(size); uptr hbits = (size >> (l - S)) & M; uptr lbits = size & ((1 << (l - S)) - 1); uptr l1 = l - kMidSizeLog; return kMidClass + (l1 << S) + hbits + (lbits > 0); } static uptr MaxCached(uptr class_id) { if (class_id == 0) return 0; uptr n = (1UL << kMaxBytesCachedLog) / Size(class_id); return Max(1, Min(kMaxNumCached, n)); } static void Print() { uptr prev_s = 0; uptr total_cached = 0; for (uptr i = 0; i < kNumClasses; i++) { uptr s = Size(i); if (s >= kMidSize / 2 && (s & (s - 1)) == 0) Printf("\n"); uptr d = s - prev_s; uptr p = prev_s ? (d * 100 / prev_s) : 0; uptr l = s ? MostSignificantSetBitIndex(s) : 0; uptr cached = MaxCached(i) * s; Printf("c%02zd => s: %zd diff: +%zd %02zd%% l %zd " "cached: %zd %zd; id %zd\n", i, Size(i), d, p, l, MaxCached(i), cached, ClassID(s)); total_cached += cached; prev_s = s; } Printf("Total cached: %zd\n", total_cached); } static bool SizeClassRequiresSeparateTransferBatch(uptr class_id) { return Size(class_id) < sizeof(TransferBatch) - sizeof(uptr) * (kMaxNumCached - MaxCached(class_id)); } static void Validate() { for (uptr c = 1; c < kNumClasses; c++) { // Printf("Validate: c%zd\n", c); uptr s = Size(c); CHECK_NE(s, 0U); CHECK_EQ(ClassID(s), c); if (c != kNumClasses - 1) CHECK_EQ(ClassID(s + 1), c + 1); CHECK_EQ(ClassID(s - 1), c); if (c) CHECK_GT(Size(c), Size(c-1)); } CHECK_EQ(ClassID(kMaxSize + 1), 0); for (uptr s = 1; s <= kMaxSize; s++) { uptr c = ClassID(s); // Printf("s%zd => c%zd\n", s, c); CHECK_LT(c, kNumClasses); CHECK_GE(Size(c), s); if (c > 0) CHECK_LT(Size(c-1), s); } } }; typedef SizeClassMap<17, 128, 16> DefaultSizeClassMap; typedef SizeClassMap<17, 64, 14> CompactSizeClassMap; template struct SizeClassAllocatorLocalCache; // Memory allocator statistics enum AllocatorStat { AllocatorStatAllocated, AllocatorStatMapped, AllocatorStatCount }; typedef uptr AllocatorStatCounters[AllocatorStatCount]; // Per-thread stats, live in per-thread cache. class AllocatorStats { public: void Init() { internal_memset(this, 0, sizeof(*this)); } + void InitLinkerInitialized() {} void Add(AllocatorStat i, uptr v) { v += atomic_load(&stats_[i], memory_order_relaxed); atomic_store(&stats_[i], v, memory_order_relaxed); } void Sub(AllocatorStat i, uptr v) { v = atomic_load(&stats_[i], memory_order_relaxed) - v; atomic_store(&stats_[i], v, memory_order_relaxed); } void Set(AllocatorStat i, uptr v) { atomic_store(&stats_[i], v, memory_order_relaxed); } uptr Get(AllocatorStat i) const { return atomic_load(&stats_[i], memory_order_relaxed); } private: friend class AllocatorGlobalStats; AllocatorStats *next_; AllocatorStats *prev_; atomic_uintptr_t stats_[AllocatorStatCount]; }; // Global stats, used for aggregation and querying. class AllocatorGlobalStats : public AllocatorStats { public: - void Init() { - internal_memset(this, 0, sizeof(*this)); + void InitLinkerInitialized() { next_ = this; prev_ = this; } + void Init() { + internal_memset(this, 0, sizeof(*this)); + InitLinkerInitialized(); + } void Register(AllocatorStats *s) { SpinMutexLock l(&mu_); s->next_ = next_; s->prev_ = this; next_->prev_ = s; next_ = s; } void Unregister(AllocatorStats *s) { SpinMutexLock l(&mu_); s->prev_->next_ = s->next_; s->next_->prev_ = s->prev_; for (int i = 0; i < AllocatorStatCount; i++) Add(AllocatorStat(i), s->Get(AllocatorStat(i))); } void Get(AllocatorStatCounters s) const { internal_memset(s, 0, AllocatorStatCount * sizeof(uptr)); SpinMutexLock l(&mu_); const AllocatorStats *stats = this; for (;;) { for (int i = 0; i < AllocatorStatCount; i++) s[i] += stats->Get(AllocatorStat(i)); stats = stats->next_; if (stats == this) break; } // All stats must be non-negative. for (int i = 0; i < AllocatorStatCount; i++) s[i] = ((sptr)s[i]) >= 0 ? s[i] : 0; } private: mutable SpinMutex mu_; }; // Allocators call these callbacks on mmap/munmap. struct NoOpMapUnmapCallback { void OnMap(uptr p, uptr size) const { } void OnUnmap(uptr p, uptr size) const { } }; // Callback type for iterating over chunks. typedef void (*ForEachChunkCallback)(uptr chunk, void *arg); // SizeClassAllocator64 -- allocator for 64-bit address space. // // Space: a portion of address space of kSpaceSize bytes starting at // a fixed address (kSpaceBeg). Both constants are powers of two and // kSpaceBeg is kSpaceSize-aligned. // At the beginning the entire space is mprotect-ed, then small parts of it // are mapped on demand. // // Region: a part of Space dedicated to a single size class. // There are kNumClasses Regions of equal size. // // UserChunk: a piece of memory returned to user. // MetaChunk: kMetadataSize bytes of metadata associated with a UserChunk. // // A Region looks like this: // UserChunk1 ... UserChunkN MetaChunkN ... MetaChunk1 template class SizeClassAllocator64 { public: typedef typename SizeClassMap::TransferBatch Batch; typedef SizeClassAllocator64 ThisT; typedef SizeClassAllocatorLocalCache AllocatorCache; void Init() { CHECK_EQ(kSpaceBeg, reinterpret_cast(Mprotect(kSpaceBeg, kSpaceSize))); MapWithCallback(kSpaceEnd, AdditionalSize()); } void MapWithCallback(uptr beg, uptr size) { CHECK_EQ(beg, reinterpret_cast(MmapFixedOrDie(beg, size))); MapUnmapCallback().OnMap(beg, size); } void UnmapWithCallback(uptr beg, uptr size) { MapUnmapCallback().OnUnmap(beg, size); UnmapOrDie(reinterpret_cast(beg), size); } static bool CanAllocate(uptr size, uptr alignment) { return size <= SizeClassMap::kMaxSize && alignment <= SizeClassMap::kMaxSize; } NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c, uptr class_id) { CHECK_LT(class_id, kNumClasses); RegionInfo *region = GetRegionInfo(class_id); Batch *b = region->free_list.Pop(); if (b == 0) b = PopulateFreeList(stat, c, class_id, region); region->n_allocated += b->count; return b; } NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) { RegionInfo *region = GetRegionInfo(class_id); CHECK_GT(b->count, 0); region->free_list.Push(b); region->n_freed += b->count; } static bool PointerIsMine(const void *p) { return reinterpret_cast(p) / kSpaceSize == kSpaceBeg / kSpaceSize; } static uptr GetSizeClass(const void *p) { return (reinterpret_cast(p) / kRegionSize) % kNumClassesRounded; } void *GetBlockBegin(const void *p) { uptr class_id = GetSizeClass(p); uptr size = SizeClassMap::Size(class_id); if (!size) return 0; uptr chunk_idx = GetChunkIdx((uptr)p, size); uptr reg_beg = (uptr)p & ~(kRegionSize - 1); uptr beg = chunk_idx * size; uptr next_beg = beg + size; if (class_id >= kNumClasses) return 0; RegionInfo *region = GetRegionInfo(class_id); if (region->mapped_user >= next_beg) return reinterpret_cast(reg_beg + beg); return 0; } static uptr GetActuallyAllocatedSize(void *p) { CHECK(PointerIsMine(p)); return SizeClassMap::Size(GetSizeClass(p)); } uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); } void *GetMetaData(const void *p) { uptr class_id = GetSizeClass(p); uptr size = SizeClassMap::Size(class_id); uptr chunk_idx = GetChunkIdx(reinterpret_cast(p), size); return reinterpret_cast(kSpaceBeg + (kRegionSize * (class_id + 1)) - (1 + chunk_idx) * kMetadataSize); } uptr TotalMemoryUsed() { uptr res = 0; for (uptr i = 0; i < kNumClasses; i++) res += GetRegionInfo(i)->allocated_user; return res; } // Test-only. void TestOnlyUnmap() { UnmapWithCallback(kSpaceBeg, kSpaceSize + AdditionalSize()); } void PrintStats() { uptr total_mapped = 0; uptr n_allocated = 0; uptr n_freed = 0; for (uptr class_id = 1; class_id < kNumClasses; class_id++) { RegionInfo *region = GetRegionInfo(class_id); total_mapped += region->mapped_user; n_allocated += region->n_allocated; n_freed += region->n_freed; } Printf("Stats: SizeClassAllocator64: %zdM mapped in %zd allocations; " "remains %zd\n", total_mapped >> 20, n_allocated, n_allocated - n_freed); for (uptr class_id = 1; class_id < kNumClasses; class_id++) { RegionInfo *region = GetRegionInfo(class_id); if (region->mapped_user == 0) continue; Printf(" %02zd (%zd): total: %zd K allocs: %zd remains: %zd\n", class_id, SizeClassMap::Size(class_id), region->mapped_user >> 10, region->n_allocated, region->n_allocated - region->n_freed); } } // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone // introspection API. void ForceLock() { for (uptr i = 0; i < kNumClasses; i++) { GetRegionInfo(i)->mutex.Lock(); } } void ForceUnlock() { for (int i = (int)kNumClasses - 1; i >= 0; i--) { GetRegionInfo(i)->mutex.Unlock(); } } // Iterate over all existing chunks. // The allocator must be locked when calling this function. void ForEachChunk(ForEachChunkCallback callback, void *arg) { for (uptr class_id = 1; class_id < kNumClasses; class_id++) { RegionInfo *region = GetRegionInfo(class_id); uptr chunk_size = SizeClassMap::Size(class_id); uptr region_beg = kSpaceBeg + class_id * kRegionSize; for (uptr chunk = region_beg; chunk < region_beg + region->allocated_user; chunk += chunk_size) { // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk)); callback(chunk, arg); } } } static uptr AdditionalSize() { return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded, GetPageSizeCached()); } typedef SizeClassMap SizeClassMapT; static const uptr kNumClasses = SizeClassMap::kNumClasses; static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded; private: static const uptr kRegionSize = kSpaceSize / kNumClassesRounded; static const uptr kSpaceEnd = kSpaceBeg + kSpaceSize; COMPILER_CHECK(kSpaceBeg % kSpaceSize == 0); // kRegionSize must be >= 2^32. COMPILER_CHECK((kRegionSize) >= (1ULL << (SANITIZER_WORDSIZE / 2))); // Populate the free list with at most this number of bytes at once // or with one element if its size is greater. static const uptr kPopulateSize = 1 << 14; // Call mmap for user memory with at least this size. static const uptr kUserMapSize = 1 << 16; // Call mmap for metadata memory with at least this size. static const uptr kMetaMapSize = 1 << 16; struct RegionInfo { BlockingMutex mutex; LFStack free_list; uptr allocated_user; // Bytes allocated for user memory. uptr allocated_meta; // Bytes allocated for metadata. uptr mapped_user; // Bytes mapped for user memory. uptr mapped_meta; // Bytes mapped for metadata. uptr n_allocated, n_freed; // Just stats. }; COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize); RegionInfo *GetRegionInfo(uptr class_id) { CHECK_LT(class_id, kNumClasses); RegionInfo *regions = reinterpret_cast(kSpaceBeg + kSpaceSize); return ®ions[class_id]; } static uptr GetChunkIdx(uptr chunk, uptr size) { uptr offset = chunk % kRegionSize; // Here we divide by a non-constant. This is costly. // size always fits into 32-bits. If the offset fits too, use 32-bit div. if (offset >> (SANITIZER_WORDSIZE / 2)) return offset / size; return (u32)offset / (u32)size; } NOINLINE Batch* PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, uptr class_id, RegionInfo *region) { BlockingMutexLock l(®ion->mutex); Batch *b = region->free_list.Pop(); if (b) return b; uptr size = SizeClassMap::Size(class_id); uptr count = size < kPopulateSize ? SizeClassMap::MaxCached(class_id) : 1; uptr beg_idx = region->allocated_user; uptr end_idx = beg_idx + count * size; uptr region_beg = kSpaceBeg + kRegionSize * class_id; if (end_idx + size > region->mapped_user) { // Do the mmap for the user memory. uptr map_size = kUserMapSize; while (end_idx + size > region->mapped_user + map_size) map_size += kUserMapSize; CHECK_GE(region->mapped_user + map_size, end_idx); MapWithCallback(region_beg + region->mapped_user, map_size); stat->Add(AllocatorStatMapped, map_size); region->mapped_user += map_size; } uptr total_count = (region->mapped_user - beg_idx - size) / size / count * count; region->allocated_meta += total_count * kMetadataSize; if (region->allocated_meta > region->mapped_meta) { uptr map_size = kMetaMapSize; while (region->allocated_meta > region->mapped_meta + map_size) map_size += kMetaMapSize; // Do the mmap for the metadata. CHECK_GE(region->mapped_meta + map_size, region->allocated_meta); MapWithCallback(region_beg + kRegionSize - region->mapped_meta - map_size, map_size); region->mapped_meta += map_size; } CHECK_LE(region->allocated_meta, region->mapped_meta); if (region->mapped_user + region->mapped_meta > kRegionSize) { Printf("%s: Out of memory. Dying. ", SanitizerToolName); Printf("The process has exhausted %zuMB for size class %zu.\n", kRegionSize / 1024 / 1024, size); Die(); } for (;;) { if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch))); else b = (Batch*)(region_beg + beg_idx); b->count = count; for (uptr i = 0; i < count; i++) b->batch[i] = (void*)(region_beg + beg_idx + i * size); region->allocated_user += count * size; CHECK_LE(region->allocated_user, region->mapped_user); beg_idx += count * size; if (beg_idx + count * size + size > region->mapped_user) break; CHECK_GT(b->count, 0); region->free_list.Push(b); } return b; } }; // Maps integers in rage [0, kSize) to u8 values. template class FlatByteMap { public: void TestOnlyInit() { internal_memset(map_, 0, sizeof(map_)); } void set(uptr idx, u8 val) { CHECK_LT(idx, kSize); CHECK_EQ(0U, map_[idx]); map_[idx] = val; } u8 operator[] (uptr idx) { CHECK_LT(idx, kSize); // FIXME: CHECK may be too expensive here. return map_[idx]; } private: u8 map_[kSize]; }; // TwoLevelByteMap maps integers in range [0, kSize1*kSize2) to u8 values. // It is implemented as a two-dimensional array: array of kSize1 pointers // to kSize2-byte arrays. The secondary arrays are mmaped on demand. // Each value is initially zero and can be set to something else only once. // Setting and getting values from multiple threads is safe w/o extra locking. template class TwoLevelByteMap { public: void TestOnlyInit() { internal_memset(map1_, 0, sizeof(map1_)); mu_.Init(); } void TestOnlyUnmap() { for (uptr i = 0; i < kSize1; i++) { u8 *p = Get(i); if (!p) continue; MapUnmapCallback().OnUnmap(reinterpret_cast(p), kSize2); UnmapOrDie(p, kSize2); } } uptr size() const { return kSize1 * kSize2; } uptr size1() const { return kSize1; } uptr size2() const { return kSize2; } void set(uptr idx, u8 val) { CHECK_LT(idx, kSize1 * kSize2); u8 *map2 = GetOrCreate(idx / kSize2); CHECK_EQ(0U, map2[idx % kSize2]); map2[idx % kSize2] = val; } u8 operator[] (uptr idx) const { CHECK_LT(idx, kSize1 * kSize2); u8 *map2 = Get(idx / kSize2); if (!map2) return 0; return map2[idx % kSize2]; } private: u8 *Get(uptr idx) const { CHECK_LT(idx, kSize1); return reinterpret_cast( atomic_load(&map1_[idx], memory_order_acquire)); } u8 *GetOrCreate(uptr idx) { u8 *res = Get(idx); if (!res) { SpinMutexLock l(&mu_); if (!(res = Get(idx))) { res = (u8*)MmapOrDie(kSize2, "TwoLevelByteMap"); MapUnmapCallback().OnMap(reinterpret_cast(res), kSize2); atomic_store(&map1_[idx], reinterpret_cast(res), memory_order_release); } } return res; } atomic_uintptr_t map1_[kSize1]; StaticSpinMutex mu_; }; // SizeClassAllocator32 -- allocator for 32-bit address space. // This allocator can theoretically be used on 64-bit arch, but there it is less // efficient than SizeClassAllocator64. // // [kSpaceBeg, kSpaceBeg + kSpaceSize) is the range of addresses which can // be returned by MmapOrDie(). // // Region: // a result of a single call to MmapAlignedOrDie(kRegionSize, kRegionSize). // Since the regions are aligned by kRegionSize, there are exactly // kNumPossibleRegions possible regions in the address space and so we keep // a ByteMap possible_regions to store the size classes of each Region. // 0 size class means the region is not used by the allocator. // // One Region is used to allocate chunks of a single size class. // A Region looks like this: // UserChunk1 .. UserChunkN MetaChunkN .. MetaChunk1 // // In order to avoid false sharing the objects of this class should be // chache-line aligned. template class SizeClassAllocator32 { public: typedef typename SizeClassMap::TransferBatch Batch; typedef SizeClassAllocator32 ThisT; typedef SizeClassAllocatorLocalCache AllocatorCache; void Init() { possible_regions.TestOnlyInit(); internal_memset(size_class_info_array, 0, sizeof(size_class_info_array)); } void *MapWithCallback(uptr size) { size = RoundUpTo(size, GetPageSizeCached()); void *res = MmapOrDie(size, "SizeClassAllocator32"); MapUnmapCallback().OnMap((uptr)res, size); return res; } void UnmapWithCallback(uptr beg, uptr size) { MapUnmapCallback().OnUnmap(beg, size); UnmapOrDie(reinterpret_cast(beg), size); } static bool CanAllocate(uptr size, uptr alignment) { return size <= SizeClassMap::kMaxSize && alignment <= SizeClassMap::kMaxSize; } void *GetMetaData(const void *p) { CHECK(PointerIsMine(p)); uptr mem = reinterpret_cast(p); uptr beg = ComputeRegionBeg(mem); uptr size = SizeClassMap::Size(GetSizeClass(p)); u32 offset = mem - beg; uptr n = offset / (u32)size; // 32-bit division uptr meta = (beg + kRegionSize) - (n + 1) * kMetadataSize; return reinterpret_cast(meta); } NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c, uptr class_id) { CHECK_LT(class_id, kNumClasses); SizeClassInfo *sci = GetSizeClassInfo(class_id); SpinMutexLock l(&sci->mutex); if (sci->free_list.empty()) PopulateFreeList(stat, c, sci, class_id); CHECK(!sci->free_list.empty()); Batch *b = sci->free_list.front(); sci->free_list.pop_front(); return b; } NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) { CHECK_LT(class_id, kNumClasses); SizeClassInfo *sci = GetSizeClassInfo(class_id); SpinMutexLock l(&sci->mutex); CHECK_GT(b->count, 0); sci->free_list.push_front(b); } bool PointerIsMine(const void *p) { return GetSizeClass(p) != 0; } uptr GetSizeClass(const void *p) { return possible_regions[ComputeRegionId(reinterpret_cast(p))]; } void *GetBlockBegin(const void *p) { CHECK(PointerIsMine(p)); uptr mem = reinterpret_cast(p); uptr beg = ComputeRegionBeg(mem); uptr size = SizeClassMap::Size(GetSizeClass(p)); u32 offset = mem - beg; u32 n = offset / (u32)size; // 32-bit division uptr res = beg + (n * (u32)size); return reinterpret_cast(res); } uptr GetActuallyAllocatedSize(void *p) { CHECK(PointerIsMine(p)); return SizeClassMap::Size(GetSizeClass(p)); } uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); } uptr TotalMemoryUsed() { // No need to lock here. uptr res = 0; for (uptr i = 0; i < kNumPossibleRegions; i++) if (possible_regions[i]) res += kRegionSize; return res; } void TestOnlyUnmap() { for (uptr i = 0; i < kNumPossibleRegions; i++) if (possible_regions[i]) UnmapWithCallback((i * kRegionSize), kRegionSize); } // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone // introspection API. void ForceLock() { for (uptr i = 0; i < kNumClasses; i++) { GetSizeClassInfo(i)->mutex.Lock(); } } void ForceUnlock() { for (int i = kNumClasses - 1; i >= 0; i--) { GetSizeClassInfo(i)->mutex.Unlock(); } } // Iterate over all existing chunks. // The allocator must be locked when calling this function. void ForEachChunk(ForEachChunkCallback callback, void *arg) { for (uptr region = 0; region < kNumPossibleRegions; region++) if (possible_regions[region]) { uptr chunk_size = SizeClassMap::Size(possible_regions[region]); uptr max_chunks_in_region = kRegionSize / (chunk_size + kMetadataSize); uptr region_beg = region * kRegionSize; for (uptr chunk = region_beg; chunk < region_beg + max_chunks_in_region * chunk_size; chunk += chunk_size) { // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk)); callback(chunk, arg); } } } void PrintStats() { } typedef SizeClassMap SizeClassMapT; static const uptr kNumClasses = SizeClassMap::kNumClasses; private: static const uptr kRegionSize = 1 << kRegionSizeLog; static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize; struct SizeClassInfo { SpinMutex mutex; IntrusiveList free_list; char padding[kCacheLineSize - sizeof(uptr) - sizeof(IntrusiveList)]; }; COMPILER_CHECK(sizeof(SizeClassInfo) == kCacheLineSize); uptr ComputeRegionId(uptr mem) { uptr res = mem >> kRegionSizeLog; CHECK_LT(res, kNumPossibleRegions); return res; } uptr ComputeRegionBeg(uptr mem) { return mem & ~(kRegionSize - 1); } uptr AllocateRegion(AllocatorStats *stat, uptr class_id) { CHECK_LT(class_id, kNumClasses); uptr res = reinterpret_cast(MmapAlignedOrDie(kRegionSize, kRegionSize, "SizeClassAllocator32")); MapUnmapCallback().OnMap(res, kRegionSize); stat->Add(AllocatorStatMapped, kRegionSize); CHECK_EQ(0U, (res & (kRegionSize - 1))); possible_regions.set(ComputeRegionId(res), static_cast(class_id)); return res; } SizeClassInfo *GetSizeClassInfo(uptr class_id) { CHECK_LT(class_id, kNumClasses); return &size_class_info_array[class_id]; } void PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, SizeClassInfo *sci, uptr class_id) { uptr size = SizeClassMap::Size(class_id); uptr reg = AllocateRegion(stat, class_id); uptr n_chunks = kRegionSize / (size + kMetadataSize); uptr max_count = SizeClassMap::MaxCached(class_id); Batch *b = 0; for (uptr i = reg; i < reg + n_chunks * size; i += size) { if (b == 0) { if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch))); else b = (Batch*)i; b->count = 0; } b->batch[b->count++] = (void*)i; if (b->count == max_count) { CHECK_GT(b->count, 0); sci->free_list.push_back(b); b = 0; } } if (b) { CHECK_GT(b->count, 0); sci->free_list.push_back(b); } } ByteMap possible_regions; SizeClassInfo size_class_info_array[kNumClasses]; }; // Objects of this type should be used as local caches for SizeClassAllocator64 // or SizeClassAllocator32. Since the typical use of this class is to have one // object per thread in TLS, is has to be POD. template struct SizeClassAllocatorLocalCache { typedef SizeClassAllocator Allocator; static const uptr kNumClasses = SizeClassAllocator::kNumClasses; void Init(AllocatorGlobalStats *s) { stats_.Init(); if (s) s->Register(&stats_); } void Destroy(SizeClassAllocator *allocator, AllocatorGlobalStats *s) { Drain(allocator); if (s) s->Unregister(&stats_); } void *Allocate(SizeClassAllocator *allocator, uptr class_id) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); stats_.Add(AllocatorStatAllocated, SizeClassMap::Size(class_id)); PerClass *c = &per_class_[class_id]; if (UNLIKELY(c->count == 0)) Refill(allocator, class_id); void *res = c->batch[--c->count]; PREFETCH(c->batch[c->count - 1]); return res; } void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); // If the first allocator call on a new thread is a deallocation, then // max_count will be zero, leading to check failure. InitCache(); stats_.Sub(AllocatorStatAllocated, SizeClassMap::Size(class_id)); PerClass *c = &per_class_[class_id]; CHECK_NE(c->max_count, 0UL); if (UNLIKELY(c->count == c->max_count)) Drain(allocator, class_id); c->batch[c->count++] = p; } void Drain(SizeClassAllocator *allocator) { for (uptr class_id = 0; class_id < kNumClasses; class_id++) { PerClass *c = &per_class_[class_id]; while (c->count > 0) Drain(allocator, class_id); } } // private: typedef typename SizeClassAllocator::SizeClassMapT SizeClassMap; typedef typename SizeClassMap::TransferBatch Batch; struct PerClass { uptr count; uptr max_count; void *batch[2 * SizeClassMap::kMaxNumCached]; }; PerClass per_class_[kNumClasses]; AllocatorStats stats_; void InitCache() { if (per_class_[1].max_count) return; for (uptr i = 0; i < kNumClasses; i++) { PerClass *c = &per_class_[i]; c->max_count = 2 * SizeClassMap::MaxCached(i); } } NOINLINE void Refill(SizeClassAllocator *allocator, uptr class_id) { InitCache(); PerClass *c = &per_class_[class_id]; Batch *b = allocator->AllocateBatch(&stats_, this, class_id); CHECK_GT(b->count, 0); for (uptr i = 0; i < b->count; i++) c->batch[i] = b->batch[i]; c->count = b->count; if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) Deallocate(allocator, SizeClassMap::ClassID(sizeof(Batch)), b); } NOINLINE void Drain(SizeClassAllocator *allocator, uptr class_id) { InitCache(); PerClass *c = &per_class_[class_id]; Batch *b; if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id)) b = (Batch*)Allocate(allocator, SizeClassMap::ClassID(sizeof(Batch))); else b = (Batch*)c->batch[0]; uptr cnt = Min(c->max_count / 2, c->count); for (uptr i = 0; i < cnt; i++) { b->batch[i] = c->batch[i]; c->batch[i] = c->batch[i + c->max_count / 2]; } b->count = cnt; c->count -= cnt; CHECK_GT(b->count, 0); allocator->DeallocateBatch(&stats_, class_id, b); } }; // This class can (de)allocate only large chunks of memory using mmap/unmap. // The main purpose of this allocator is to cover large and rare allocation // sizes not covered by more efficient allocators (e.g. SizeClassAllocator64). template class LargeMmapAllocator { public: - void Init() { - internal_memset(this, 0, sizeof(*this)); + void InitLinkerInitialized(bool may_return_null) { page_size_ = GetPageSizeCached(); + atomic_store(&may_return_null_, may_return_null, memory_order_relaxed); } + void Init(bool may_return_null) { + internal_memset(this, 0, sizeof(*this)); + InitLinkerInitialized(may_return_null); + } + void *Allocate(AllocatorStats *stat, uptr size, uptr alignment) { CHECK(IsPowerOfTwo(alignment)); uptr map_size = RoundUpMapSize(size); if (alignment > page_size_) map_size += alignment; - if (map_size < size) return AllocatorReturnNull(); // Overflow. + // Overflow. + if (map_size < size) + return ReturnNullOrDie(); uptr map_beg = reinterpret_cast( MmapOrDie(map_size, "LargeMmapAllocator")); CHECK(IsAligned(map_beg, page_size_)); MapUnmapCallback().OnMap(map_beg, map_size); uptr map_end = map_beg + map_size; uptr res = map_beg + page_size_; if (res & (alignment - 1)) // Align. res += alignment - (res & (alignment - 1)); CHECK(IsAligned(res, alignment)); CHECK(IsAligned(res, page_size_)); CHECK_GE(res + size, map_beg); CHECK_LE(res + size, map_end); Header *h = GetHeader(res); h->size = size; h->map_beg = map_beg; h->map_size = map_size; uptr size_log = MostSignificantSetBitIndex(map_size); CHECK_LT(size_log, ARRAY_SIZE(stats.by_size_log)); { SpinMutexLock l(&mutex_); uptr idx = n_chunks_++; chunks_sorted_ = false; CHECK_LT(idx, kMaxNumChunks); h->chunk_idx = idx; chunks_[idx] = h; stats.n_allocs++; stats.currently_allocated += map_size; stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated); stats.by_size_log[size_log]++; stat->Add(AllocatorStatAllocated, map_size); stat->Add(AllocatorStatMapped, map_size); } return reinterpret_cast(res); } + void *ReturnNullOrDie() { + if (atomic_load(&may_return_null_, memory_order_acquire)) + return 0; + ReportAllocatorCannotReturnNull(); + } + + void SetMayReturnNull(bool may_return_null) { + atomic_store(&may_return_null_, may_return_null, memory_order_release); + } + void Deallocate(AllocatorStats *stat, void *p) { Header *h = GetHeader(p); { SpinMutexLock l(&mutex_); uptr idx = h->chunk_idx; CHECK_EQ(chunks_[idx], h); CHECK_LT(idx, n_chunks_); chunks_[idx] = chunks_[n_chunks_ - 1]; chunks_[idx]->chunk_idx = idx; n_chunks_--; chunks_sorted_ = false; stats.n_frees++; stats.currently_allocated -= h->map_size; stat->Sub(AllocatorStatAllocated, h->map_size); stat->Sub(AllocatorStatMapped, h->map_size); } MapUnmapCallback().OnUnmap(h->map_beg, h->map_size); UnmapOrDie(reinterpret_cast(h->map_beg), h->map_size); } uptr TotalMemoryUsed() { SpinMutexLock l(&mutex_); uptr res = 0; for (uptr i = 0; i < n_chunks_; i++) { Header *h = chunks_[i]; CHECK_EQ(h->chunk_idx, i); res += RoundUpMapSize(h->size); } return res; } bool PointerIsMine(const void *p) { return GetBlockBegin(p) != 0; } uptr GetActuallyAllocatedSize(void *p) { return RoundUpTo(GetHeader(p)->size, page_size_); } // At least page_size_/2 metadata bytes is available. void *GetMetaData(const void *p) { // Too slow: CHECK_EQ(p, GetBlockBegin(p)); if (!IsAligned(reinterpret_cast(p), page_size_)) { Printf("%s: bad pointer %p\n", SanitizerToolName, p); CHECK(IsAligned(reinterpret_cast(p), page_size_)); } return GetHeader(p) + 1; } void *GetBlockBegin(const void *ptr) { uptr p = reinterpret_cast(ptr); SpinMutexLock l(&mutex_); uptr nearest_chunk = 0; // Cache-friendly linear search. for (uptr i = 0; i < n_chunks_; i++) { uptr ch = reinterpret_cast(chunks_[i]); if (p < ch) continue; // p is at left to this chunk, skip it. if (p - ch < p - nearest_chunk) nearest_chunk = ch; } if (!nearest_chunk) return 0; Header *h = reinterpret_cast
(nearest_chunk); CHECK_GE(nearest_chunk, h->map_beg); CHECK_LT(nearest_chunk, h->map_beg + h->map_size); CHECK_LE(nearest_chunk, p); if (h->map_beg + h->map_size <= p) return 0; return GetUser(h); } // This function does the same as GetBlockBegin, but is much faster. // Must be called with the allocator locked. void *GetBlockBeginFastLocked(void *ptr) { mutex_.CheckLocked(); uptr p = reinterpret_cast(ptr); uptr n = n_chunks_; if (!n) return 0; if (!chunks_sorted_) { // Do one-time sort. chunks_sorted_ is reset in Allocate/Deallocate. SortArray(reinterpret_cast(chunks_), n); for (uptr i = 0; i < n; i++) chunks_[i]->chunk_idx = i; chunks_sorted_ = true; min_mmap_ = reinterpret_cast(chunks_[0]); max_mmap_ = reinterpret_cast(chunks_[n - 1]) + chunks_[n - 1]->map_size; } if (p < min_mmap_ || p >= max_mmap_) return 0; uptr beg = 0, end = n - 1; // This loop is a log(n) lower_bound. It does not check for the exact match // to avoid expensive cache-thrashing loads. while (end - beg >= 2) { uptr mid = (beg + end) / 2; // Invariant: mid >= beg + 1 if (p < reinterpret_cast(chunks_[mid])) end = mid - 1; // We are not interested in chunks_[mid]. else beg = mid; // chunks_[mid] may still be what we want. } if (beg < end) { CHECK_EQ(beg + 1, end); // There are 2 chunks left, choose one. if (p >= reinterpret_cast(chunks_[end])) beg = end; } Header *h = chunks_[beg]; if (h->map_beg + h->map_size <= p || p < h->map_beg) return 0; return GetUser(h); } void PrintStats() { Printf("Stats: LargeMmapAllocator: allocated %zd times, " "remains %zd (%zd K) max %zd M; by size logs: ", stats.n_allocs, stats.n_allocs - stats.n_frees, stats.currently_allocated >> 10, stats.max_allocated >> 20); for (uptr i = 0; i < ARRAY_SIZE(stats.by_size_log); i++) { uptr c = stats.by_size_log[i]; if (!c) continue; Printf("%zd:%zd; ", i, c); } Printf("\n"); } // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone // introspection API. void ForceLock() { mutex_.Lock(); } void ForceUnlock() { mutex_.Unlock(); } // Iterate over all existing chunks. // The allocator must be locked when calling this function. void ForEachChunk(ForEachChunkCallback callback, void *arg) { for (uptr i = 0; i < n_chunks_; i++) callback(reinterpret_cast(GetUser(chunks_[i])), arg); } private: static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18); struct Header { uptr map_beg; uptr map_size; uptr size; uptr chunk_idx; }; Header *GetHeader(uptr p) { CHECK(IsAligned(p, page_size_)); return reinterpret_cast(p - page_size_); } Header *GetHeader(const void *p) { return GetHeader(reinterpret_cast(p)); } void *GetUser(Header *h) { CHECK(IsAligned((uptr)h, page_size_)); return reinterpret_cast(reinterpret_cast(h) + page_size_); } uptr RoundUpMapSize(uptr size) { return RoundUpTo(size, page_size_) + page_size_; } uptr page_size_; Header *chunks_[kMaxNumChunks]; uptr n_chunks_; uptr min_mmap_, max_mmap_; bool chunks_sorted_; struct Stats { uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64]; } stats; + atomic_uint8_t may_return_null_; SpinMutex mutex_; }; // This class implements a complete memory allocator by using two // internal allocators: // PrimaryAllocator is efficient, but may not allocate some sizes (alignments). // When allocating 2^x bytes it should return 2^x aligned chunk. // PrimaryAllocator is used via a local AllocatorCache. // SecondaryAllocator can allocate anything, but is not efficient. template // NOLINT class CombinedAllocator { public: - void Init() { + void InitCommon(bool may_return_null) { primary_.Init(); - secondary_.Init(); + atomic_store(&may_return_null_, may_return_null, memory_order_relaxed); + } + + void InitLinkerInitialized(bool may_return_null) { + secondary_.InitLinkerInitialized(may_return_null); + stats_.InitLinkerInitialized(); + InitCommon(may_return_null); + } + + void Init(bool may_return_null) { + secondary_.Init(may_return_null); stats_.Init(); + InitCommon(may_return_null); } void *Allocate(AllocatorCache *cache, uptr size, uptr alignment, - bool cleared = false) { + bool cleared = false, bool check_rss_limit = false) { // Returning 0 on malloc(0) may break a lot of code. if (size == 0) size = 1; if (size + alignment < size) - return AllocatorReturnNull(); + return ReturnNullOrDie(); + if (check_rss_limit && RssLimitIsExceeded()) + return ReturnNullOrDie(); if (alignment > 8) size = RoundUpTo(size, alignment); void *res; bool from_primary = primary_.CanAllocate(size, alignment); if (from_primary) res = cache->Allocate(&primary_, primary_.ClassID(size)); else res = secondary_.Allocate(&stats_, size, alignment); if (alignment > 8) CHECK_EQ(reinterpret_cast(res) & (alignment - 1), 0); if (cleared && res && from_primary) internal_bzero_aligned16(res, RoundUpTo(size, 16)); return res; } + bool MayReturnNull() const { + return atomic_load(&may_return_null_, memory_order_acquire); + } + + void *ReturnNullOrDie() { + if (MayReturnNull()) + return 0; + ReportAllocatorCannotReturnNull(); + } + + void SetMayReturnNull(bool may_return_null) { + secondary_.SetMayReturnNull(may_return_null); + atomic_store(&may_return_null_, may_return_null, memory_order_release); + } + + bool RssLimitIsExceeded() { + return atomic_load(&rss_limit_is_exceeded_, memory_order_acquire); + } + + void SetRssLimitIsExceeded(bool rss_limit_is_exceeded) { + atomic_store(&rss_limit_is_exceeded_, rss_limit_is_exceeded, + memory_order_release); + } + void Deallocate(AllocatorCache *cache, void *p) { if (!p) return; if (primary_.PointerIsMine(p)) cache->Deallocate(&primary_, primary_.GetSizeClass(p), p); else secondary_.Deallocate(&stats_, p); } void *Reallocate(AllocatorCache *cache, void *p, uptr new_size, uptr alignment) { if (!p) return Allocate(cache, new_size, alignment); if (!new_size) { Deallocate(cache, p); return 0; } CHECK(PointerIsMine(p)); uptr old_size = GetActuallyAllocatedSize(p); uptr memcpy_size = Min(new_size, old_size); void *new_p = Allocate(cache, new_size, alignment); if (new_p) internal_memcpy(new_p, p, memcpy_size); Deallocate(cache, p); return new_p; } bool PointerIsMine(void *p) { if (primary_.PointerIsMine(p)) return true; return secondary_.PointerIsMine(p); } bool FromPrimary(void *p) { return primary_.PointerIsMine(p); } void *GetMetaData(const void *p) { if (primary_.PointerIsMine(p)) return primary_.GetMetaData(p); return secondary_.GetMetaData(p); } void *GetBlockBegin(const void *p) { if (primary_.PointerIsMine(p)) return primary_.GetBlockBegin(p); return secondary_.GetBlockBegin(p); } // This function does the same as GetBlockBegin, but is much faster. // Must be called with the allocator locked. void *GetBlockBeginFastLocked(void *p) { if (primary_.PointerIsMine(p)) return primary_.GetBlockBegin(p); return secondary_.GetBlockBeginFastLocked(p); } uptr GetActuallyAllocatedSize(void *p) { if (primary_.PointerIsMine(p)) return primary_.GetActuallyAllocatedSize(p); return secondary_.GetActuallyAllocatedSize(p); } uptr TotalMemoryUsed() { return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed(); } void TestOnlyUnmap() { primary_.TestOnlyUnmap(); } void InitCache(AllocatorCache *cache) { cache->Init(&stats_); } void DestroyCache(AllocatorCache *cache) { cache->Destroy(&primary_, &stats_); } void SwallowCache(AllocatorCache *cache) { cache->Drain(&primary_); } void GetStats(AllocatorStatCounters s) const { stats_.Get(s); } void PrintStats() { primary_.PrintStats(); secondary_.PrintStats(); } // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone // introspection API. void ForceLock() { primary_.ForceLock(); secondary_.ForceLock(); } void ForceUnlock() { secondary_.ForceUnlock(); primary_.ForceUnlock(); } // Iterate over all existing chunks. // The allocator must be locked when calling this function. void ForEachChunk(ForEachChunkCallback callback, void *arg) { primary_.ForEachChunk(callback, arg); secondary_.ForEachChunk(callback, arg); } private: PrimaryAllocator primary_; SecondaryAllocator secondary_; AllocatorGlobalStats stats_; + atomic_uint8_t may_return_null_; + atomic_uint8_t rss_limit_is_exceeded_; }; // Returns true if calloc(size, n) should return 0 due to overflow in size*n. bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n); } // namespace __sanitizer #endif // SANITIZER_ALLOCATOR_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator_internal.h (revision 279191) @@ -1,54 +1,63 @@ //===-- sanitizer_allocator_internal.h -------------------------- C++ -----===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This allocator is used inside run-times. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_ALLOCATOR_INTERNAL_H #define SANITIZER_ALLOCATOR_INTERNAL_H #include "sanitizer_allocator.h" #include "sanitizer_internal_defs.h" namespace __sanitizer { // FIXME: Check if we may use even more compact size class map for internal // purposes. typedef CompactSizeClassMap InternalSizeClassMap; static const uptr kInternalAllocatorSpace = 0; static const u64 kInternalAllocatorSize = SANITIZER_MMAP_RANGE_SIZE; static const uptr kInternalAllocatorRegionSizeLog = 20; #if SANITIZER_WORDSIZE == 32 static const uptr kInternalAllocatorNumRegions = kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog; typedef FlatByteMap ByteMap; #else static const uptr kInternalAllocatorNumRegions = kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog; typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap; #endif typedef SizeClassAllocator32< kInternalAllocatorSpace, kInternalAllocatorSize, 0, InternalSizeClassMap, kInternalAllocatorRegionSizeLog, ByteMap> PrimaryInternalAllocator; typedef SizeClassAllocatorLocalCache InternalAllocatorCache; typedef CombinedAllocator > InternalAllocator; void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0); void InternalFree(void *p, InternalAllocatorCache *cache = 0); InternalAllocator *internal_allocator(); +enum InternalAllocEnum { + INTERNAL_ALLOC +}; + } // namespace __sanitizer + +inline void *operator new(__sanitizer::operator_new_size_type size, + InternalAllocEnum) { + return InternalAlloc(size); +} #endif // SANITIZER_ALLOCATOR_INTERNAL_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.cc (revision 279191) @@ -1,283 +1,308 @@ //===-- sanitizer_common.cc -----------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. //===----------------------------------------------------------------------===// #include "sanitizer_common.h" +#include "sanitizer_allocator_internal.h" #include "sanitizer_flags.h" #include "sanitizer_libc.h" +#include "sanitizer_placement_new.h" namespace __sanitizer { const char *SanitizerToolName = "SanitizerTool"; +atomic_uint32_t current_verbosity; + uptr GetPageSizeCached() { static uptr PageSize; if (!PageSize) PageSize = GetPageSize(); return PageSize; } StaticSpinMutex report_file_mu; ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0}; void RawWrite(const char *buffer) { report_file.Write(buffer, internal_strlen(buffer)); } void ReportFile::ReopenIfNecessary() { mu->CheckLocked(); if (fd == kStdoutFd || fd == kStderrFd) return; uptr pid = internal_getpid(); // If in tracer, use the parent's file. if (pid == stoptheworld_tracer_pid) pid = stoptheworld_tracer_ppid; if (fd != kInvalidFd) { // If the report file is already opened by the current process, // do nothing. Otherwise the report file was opened by the parent // process, close it now. if (fd_pid == pid) return; else internal_close(fd); } internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid); uptr openrv = OpenFile(full_path, true); if (internal_iserror(openrv)) { const char *ErrorMsgPrefix = "ERROR: Can't open file: "; internal_write(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix)); internal_write(kStderrFd, full_path, internal_strlen(full_path)); Die(); } fd = openrv; fd_pid = pid; } void ReportFile::SetReportPath(const char *path) { if (!path) return; uptr len = internal_strlen(path); if (len > sizeof(path_prefix) - 100) { Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n", path[0], path[1], path[2], path[3], path[4], path[5], path[6], path[7]); Die(); } SpinMutexLock l(mu); if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd) internal_close(fd); fd = kInvalidFd; if (internal_strcmp(path, "stdout") == 0) { fd = kStdoutFd; } else if (internal_strcmp(path, "stderr") == 0) { fd = kStderrFd; } else { internal_snprintf(path_prefix, kMaxPathLength, "%s", path); } } // PID of the tracer task in StopTheWorld. It shares the address space with the // main process, but has a different PID and thus requires special handling. uptr stoptheworld_tracer_pid = 0; // Cached pid of parent process - if the parent process dies, we want to keep // writing to the same log file. uptr stoptheworld_tracer_ppid = 0; -static DieCallbackType DieCallback; +static DieCallbackType InternalDieCallback, UserDieCallback; void SetDieCallback(DieCallbackType callback) { - DieCallback = callback; + InternalDieCallback = callback; } +void SetUserDieCallback(DieCallbackType callback) { + UserDieCallback = callback; +} DieCallbackType GetDieCallback() { - return DieCallback; + return InternalDieCallback; } void NORETURN Die() { - if (DieCallback) { - DieCallback(); - } + if (UserDieCallback) + UserDieCallback(); + if (InternalDieCallback) + InternalDieCallback(); internal__exit(1); } static CheckFailedCallbackType CheckFailedCallback; void SetCheckFailedCallback(CheckFailedCallbackType callback) { CheckFailedCallback = callback; } void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { if (CheckFailedCallback) { CheckFailedCallback(file, line, cond, v1, v2); } Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond, v1, v2); Die(); } -uptr ReadFileToBuffer(const char *file_name, char **buff, - uptr *buff_size, uptr max_len) { +uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, + uptr max_len, int *errno_p) { uptr PageSize = GetPageSizeCached(); uptr kMinFileLen = PageSize; uptr read_len = 0; *buff = 0; *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)) return 0; + if (internal_iserror(openrv, errno_p)) return 0; fd_t fd = openrv; UnmapOrDie(*buff, *buff_size); *buff = (char*)MmapOrDie(size, __func__); *buff_size = size; // Read up to one page at a time. 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)) { + UnmapOrDie(*buff, *buff_size); + return 0; + } if (just_read == 0) { reached_eof = true; break; } read_len += just_read; } internal_close(fd); if (reached_eof) // We've read the whole file. break; } return read_len; } typedef bool UptrComparisonFunction(const uptr &a, const uptr &b); template static inline bool CompareLess(const T &a, const T &b) { return a < b; } void SortArray(uptr *array, uptr size) { InternalSort(&array, size, CompareLess); } // We want to map a chunk of address space aligned to 'alignment'. // We do it by maping a bit more and then unmaping redundant pieces. // We probably can do it with fewer syscalls in some OS-dependent way. void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) { // uptr PageSize = GetPageSizeCached(); CHECK(IsPowerOfTwo(size)); CHECK(IsPowerOfTwo(alignment)); uptr map_size = size + alignment; uptr map_res = (uptr)MmapOrDie(map_size, mem_type); uptr map_end = map_res + map_size; uptr res = map_res; if (res & (alignment - 1)) // Not aligned. res = (map_res + alignment) & ~(alignment - 1); uptr end = res + size; if (res != map_res) UnmapOrDie((void*)map_res, res - map_res); if (end != map_end) UnmapOrDie((void*)end, map_end - end); return (void*)res; } 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 *StripModuleName(const char *module) { if (module == 0) return 0; if (const char *slash_pos = internal_strrchr(module, '/')) return slash_pos + 1; return module; } void ReportErrorSummary(const char *error_message) { if (!common_flags()->print_summary) return; InternalScopedString buff(kMaxSummaryLength); buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message); __sanitizer_report_error_summary(buff.data()); } void ReportErrorSummary(const char *error_type, const char *file, int line, const char *function) { 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 : "??"); ReportErrorSummary(buff.data()); } LoadedModule::LoadedModule(const char *module_name, uptr base_address) { full_name_ = internal_strdup(module_name); base_address_ = base_address; - n_ranges_ = 0; + ranges_.clear(); } +void LoadedModule::clear() { + InternalFree(full_name_); + while (!ranges_.empty()) { + AddressRange *r = ranges_.front(); + ranges_.pop_front(); + InternalFree(r); + } +} + void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) { - CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges); - ranges_[n_ranges_].beg = beg; - ranges_[n_ranges_].end = end; - exec_[n_ranges_] = executable; - n_ranges_++; + void *mem = InternalAlloc(sizeof(AddressRange)); + AddressRange *r = new(mem) AddressRange(beg, end, executable); + ranges_.push_back(r); } bool LoadedModule::containsAddress(uptr address) const { - for (uptr i = 0; i < n_ranges_; i++) { - if (ranges_[i].beg <= address && address < ranges_[i].end) + for (Iterator iter = ranges(); iter.hasNext();) { + const AddressRange *r = iter.next(); + if (r->beg <= address && address < r->end) return true; } return false; } static atomic_uintptr_t g_total_mmaped; void IncreaseTotalMmap(uptr size) { if (!common_flags()->mmap_limit_mb) return; uptr total_mmaped = atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size; // Since for now mmap_limit_mb is not a user-facing flag, just kill // a program. Use RAW_CHECK to avoid extra mmaps in reporting. RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb); } void DecreaseTotalMmap(uptr size) { if (!common_flags()->mmap_limit_mb) return; atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed); } } // namespace __sanitizer using namespace __sanitizer; // NOLINT extern "C" { void __sanitizer_set_report_path(const char *path) { report_file.SetReportPath(path); } void __sanitizer_report_error_summary(const char *error_summary) { Printf("%s\n", error_summary); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_set_death_callback(void (*callback)(void)) { + SetUserDieCallback(callback); } } // extern "C" Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common.h (revision 279191) @@ -1,584 +1,620 @@ //===-- sanitizer_common.h --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. // It declares common functions and classes that are used in both runtimes. // Implementation of some functions are provided in sanitizer_common, while // others must be defined by run-time library itself. //===----------------------------------------------------------------------===// #ifndef SANITIZER_COMMON_H #define SANITIZER_COMMON_H +#include "sanitizer_flags.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" +#include "sanitizer_list.h" #include "sanitizer_mutex.h" -#include "sanitizer_flags.h" namespace __sanitizer { struct StackTrace; // Constants. const uptr kWordSize = SANITIZER_WORDSIZE / 8; const uptr kWordSizeInBits = 8 * kWordSize; #if defined(__powerpc__) || defined(__powerpc64__) const uptr kCacheLineSize = 128; #else const uptr kCacheLineSize = 64; #endif const uptr kMaxPathLength = 4096; const uptr kMaxThreadStackSize = 1 << 30; // 1Gb extern const char *SanitizerToolName; // Can be changed by the tool. +extern atomic_uint32_t current_verbosity; +INLINE void SetVerbosity(int verbosity) { + atomic_store(¤t_verbosity, verbosity, memory_order_relaxed); +} +INLINE int Verbosity() { + return atomic_load(¤t_verbosity, memory_order_relaxed); +} + uptr GetPageSize(); uptr GetPageSizeCached(); uptr GetMmapGranularity(); uptr GetMaxVirtualAddress(); // Threads uptr GetTid(); uptr GetThreadSelf(); void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom); void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_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 *MmapNoReserveOrDie(uptr size, const char *mem_type); void *MmapFixedOrDie(uptr fixed_addr, uptr size); void *Mprotect(uptr fixed_addr, uptr size); // Map aligned chunk of address space; size and alignment are powers of two. void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type); // 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); void IncreaseTotalMmap(uptr size); void DecreaseTotalMmap(uptr size); uptr GetRSS(); +void NoHugePagesInRegion(uptr addr, uptr length); +void DontDumpShadowMemory(uptr addr, uptr length); // InternalScopedBuffer can be used instead of large stack arrays to // keep frame size low. // FIXME: use InternalAlloc instead of MmapOrDie once // InternalAlloc is made libc-free. template class InternalScopedBuffer { public: explicit InternalScopedBuffer(uptr cnt) { cnt_ = cnt; ptr_ = (T*)MmapOrDie(cnt * sizeof(T), "InternalScopedBuffer"); } ~InternalScopedBuffer() { UnmapOrDie(ptr_, cnt_ * sizeof(T)); } T &operator[](uptr i) { return ptr_[i]; } T *data() { return ptr_; } uptr size() { return cnt_ * sizeof(T); } private: T *ptr_; uptr cnt_; // Disallow evil constructors. InternalScopedBuffer(const InternalScopedBuffer&); void operator=(const InternalScopedBuffer&); }; class InternalScopedString : public InternalScopedBuffer { public: explicit InternalScopedString(uptr max_length) : InternalScopedBuffer(max_length), length_(0) { (*this)[0] = '\0'; } uptr length() { return length_; } void clear() { (*this)[0] = '\0'; length_ = 0; } void append(const char *format, ...); private: uptr length_; }; // Simple low-level (mmap-based) allocator for internal use. Doesn't have // constructor, so all instances of LowLevelAllocator should be // linker initialized. class LowLevelAllocator { public: // Requires an external lock. void *Allocate(uptr size); private: char *allocated_end_; char *allocated_current_; }; typedef void (*LowLevelAllocateCallback)(uptr ptr, uptr size); // Allows to register tool-specific callbacks for LowLevelAllocator. // Passing NULL removes the callback. void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback); // IO void RawWrite(const char *buffer); bool ColorizeReports(); void Printf(const char *format, ...); void Report(const char *format, ...); void SetPrintfAndReportCallback(void (*callback)(const char *)); #define VReport(level, ...) \ do { \ - if ((uptr)common_flags()->verbosity >= (level)) Report(__VA_ARGS__); \ + if ((uptr)Verbosity() >= (level)) Report(__VA_ARGS__); \ } while (0) #define VPrintf(level, ...) \ do { \ - if ((uptr)common_flags()->verbosity >= (level)) Printf(__VA_ARGS__); \ + if ((uptr)Verbosity() >= (level)) Printf(__VA_ARGS__); \ } while (0) // Can be used to prevent mixing error reports from different sanitizers. extern StaticSpinMutex CommonSanitizerReportMutex; struct ReportFile { void Write(const char *buffer, uptr length); bool PrintsToTty(); void SetReportPath(const char *path); // Don't use fields directly. They are only declared public to allow // aggregate initialization. // Protects fields below. StaticSpinMutex *mu; // Opened file descriptor. Defaults to stderr. It may be equal to // kInvalidFd, in which case new file will be opened when necessary. fd_t fd; // Path prefix of report file, set via __sanitizer_set_report_path. char path_prefix[kMaxPathLength]; // Full path to report, obtained as .PID char full_path[kMaxPathLength]; // PID of the process that opened fd. If a fork() occurs, // the PID of child will be different from fd_pid. uptr fd_pid; private: void ReopenIfNecessary(); }; extern ReportFile report_file; extern uptr stoptheworld_tracer_pid; extern uptr stoptheworld_tracer_ppid; uptr OpenFile(const char *filename, bool write); // 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); +uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, + uptr max_len, int *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 // in '*buff_size'. void *MapFileToMemory(const char *file_name, uptr *buff_size); void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset); bool IsAccessibleMemoryRange(uptr beg, uptr size); // Error report formatting. const char *StripPathPrefix(const char *filepath, const char *strip_file_prefix); // Strip the directories from the module name. const char *StripModuleName(const char *module); // OS void DisableCoreDumperIfNecessary(); void DumpProcessMap(); bool FileExists(const char *filename); const char *GetEnv(const char *name); bool SetEnv(const char *name, const char *value); const char *GetPwd(); char *FindPathToBinary(const char *name); u32 GetUid(); void ReExec(); bool StackSizeIsUnlimited(); void SetStackSizeLimitInBytes(uptr limit); bool AddressSpaceIsUnlimited(); void SetAddressSpaceUnlimited(); void AdjustStackSize(void *attr); void PrepareForSandboxing(__sanitizer_sandbox_arguments *args); void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args); void SetSandboxingCallback(void (*f)()); -void CovUpdateMapping(uptr caller_pc = 0); +void CoverageUpdateMapping(); void CovBeforeFork(); void CovAfterFork(int child_pid); +void InitializeCoverage(bool enabled, const char *coverage_dir); +void ReInitializeCoverage(bool enabled, const char *coverage_dir); + void InitTlsSize(); uptr GetTlsSize(); // Other void SleepForSeconds(int seconds); void SleepForMillis(int millis); u64 NanoTime(); int Atexit(void (*function)(void)); void SortArray(uptr *array, uptr size); // Exit void NORETURN Abort(); void NORETURN Die(); void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); // Set the name of the current thread to 'name', return true on succees. // The name may be truncated to a system-dependent limit. bool SanitizerSetThreadName(const char *name); // Get the name of the current thread (no more than max_len bytes), // return true on succees. name should have space for at least max_len+1 bytes. bool SanitizerGetThreadName(char *name, int max_len); // Specific tools may override behavior of "Die" and "CheckFailed" functions // to do tool-specific job. typedef void (*DieCallbackType)(void); void SetDieCallback(DieCallbackType); +void SetUserDieCallback(DieCallbackType); DieCallbackType GetDieCallback(); typedef void (*CheckFailedCallbackType)(const char *, int, const char *, u64, u64); void SetCheckFailedCallback(CheckFailedCallbackType callback); +// Callback will be called if soft_rss_limit_mb is given and the limit is +// exceeded (exceeded==true) or if rss went down below the limit +// (exceeded==false). +// The callback should be registered once at the tool init time. +void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)); + // Functions related to signal handling. typedef void (*SignalHandlerType)(int, void *, void *); bool IsDeadlySignal(int signum); void InstallDeadlySignalHandlers(SignalHandlerType handler); // Alternative signal stack (POSIX-only). void SetAlternateSignalStack(); void UnsetAlternateSignalStack(); // We don't want a summary too long. const int kMaxSummaryLength = 1024; // Construct a one-line string: // SUMMARY: SanitizerToolName: error_message // 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); void ReportErrorSummary(const char *error_type, StackTrace *trace); // Math #if SANITIZER_WINDOWS && !defined(__clang__) && !defined(__GNUC__) extern "C" { unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT #if defined(_WIN64) unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask); // NOLINT unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); // NOLINT #endif } #endif INLINE uptr MostSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT #if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x); #elif defined(_WIN64) _BitScanReverse64(&up, x); #else _BitScanReverse(&up, x); #endif return up; } INLINE uptr LeastSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT #if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) up = __builtin_ctzl(x); #elif defined(_WIN64) _BitScanForward64(&up, x); #else _BitScanForward(&up, x); #endif return up; } INLINE bool IsPowerOfTwo(uptr x) { return (x & (x - 1)) == 0; } INLINE uptr RoundUpToPowerOfTwo(uptr size) { CHECK(size); if (IsPowerOfTwo(size)) return size; uptr up = MostSignificantSetBitIndex(size); CHECK(size < (1ULL << (up + 1))); CHECK(size > (1ULL << up)); return 1UL << (up + 1); } INLINE uptr RoundUpTo(uptr size, uptr boundary) { CHECK(IsPowerOfTwo(boundary)); return (size + boundary - 1) & ~(boundary - 1); } INLINE uptr RoundDownTo(uptr x, uptr boundary) { return x & ~(boundary - 1); } INLINE bool IsAligned(uptr a, uptr alignment) { return (a & (alignment - 1)) == 0; } 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 } // Don't use std::min, std::max or std::swap, to minimize dependency // on libstdc++. template T Min(T a, T b) { return a < b ? a : b; } template T Max(T a, T b) { return a > b ? a : b; } template void Swap(T& a, T& b) { T tmp = a; a = b; b = tmp; } // Char handling INLINE bool IsSpace(int c) { return (c == ' ') || (c == '\n') || (c == '\t') || (c == '\f') || (c == '\r') || (c == '\v'); } INLINE bool IsDigit(int c) { return (c >= '0') && (c <= '9'); } INLINE int ToLower(int c) { return (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c; } // A low-level vector based on mmap. May incur a significant memory overhead for // small vectors. // WARNING: The current implementation supports only POD types. template -class InternalMmapVector { +class InternalMmapVectorNoCtor { public: - explicit InternalMmapVector(uptr initial_capacity) { + void Initialize(uptr initial_capacity) { capacity_ = Max(initial_capacity, (uptr)1); size_ = 0; - data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVector"); + data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVectorNoCtor"); } - ~InternalMmapVector() { + void Destroy() { UnmapOrDie(data_, capacity_ * sizeof(T)); } T &operator[](uptr i) { CHECK_LT(i, size_); return data_[i]; } const T &operator[](uptr i) const { CHECK_LT(i, size_); return data_[i]; } void push_back(const T &element) { CHECK_LE(size_, capacity_); if (size_ == capacity_) { uptr new_capacity = RoundUpToPowerOfTwo(size_ + 1); Resize(new_capacity); } data_[size_++] = element; } T &back() { CHECK_GT(size_, 0); return data_[size_ - 1]; } void pop_back() { CHECK_GT(size_, 0); size_--; } uptr size() const { return size_; } const T *data() const { return data_; } uptr capacity() const { return capacity_; } void clear() { size_ = 0; } private: void Resize(uptr new_capacity) { CHECK_GT(new_capacity, 0); CHECK_LE(size_, new_capacity); T *new_data = (T *)MmapOrDie(new_capacity * sizeof(T), "InternalMmapVector"); internal_memcpy(new_data, data_, size_ * sizeof(T)); T *old_data = data_; data_ = new_data; UnmapOrDie(old_data, capacity_ * sizeof(T)); capacity_ = new_capacity; } - // Disallow evil constructors. - InternalMmapVector(const InternalMmapVector&); - void operator=(const InternalMmapVector&); T *data_; uptr capacity_; uptr size_; }; +template +class InternalMmapVector : public InternalMmapVectorNoCtor { + public: + explicit InternalMmapVector(uptr initial_capacity) { + InternalMmapVectorNoCtor::Initialize(initial_capacity); + } + ~InternalMmapVector() { InternalMmapVectorNoCtor::Destroy(); } + // Disallow evil constructors. + InternalMmapVector(const InternalMmapVector&); + void operator=(const InternalMmapVector&); +}; + // HeapSort for arrays and InternalMmapVector. template void InternalSort(Container *v, uptr size, Compare comp) { if (size < 2) return; // Stage 1: insert elements to the heap. for (uptr i = 1; i < size; i++) { uptr j, p; for (j = i; j > 0; j = p) { p = (j - 1) / 2; if (comp((*v)[p], (*v)[j])) Swap((*v)[j], (*v)[p]); else break; } } // Stage 2: swap largest element with the last one, // and sink the new top. for (uptr i = size - 1; i > 0; i--) { Swap((*v)[0], (*v)[i]); uptr j, max_ind; for (j = 0; j < i; j = max_ind) { uptr left = 2 * j + 1; uptr right = 2 * j + 2; max_ind = j; if (left < i && comp((*v)[max_ind], (*v)[left])) max_ind = left; if (right < i && comp((*v)[max_ind], (*v)[right])) max_ind = right; if (max_ind != j) Swap((*v)[j], (*v)[max_ind]); else break; } } } template uptr InternalBinarySearch(const Container &v, uptr first, uptr last, const Value &val, Compare comp) { uptr not_found = last + 1; while (last >= first) { uptr mid = (first + last) / 2; if (comp(v[mid], val)) first = mid + 1; else if (comp(val, v[mid])) last = mid - 1; else return mid; } return not_found; } // Represents a binary loaded into virtual memory (e.g. this can be an // executable or a shared object). class LoadedModule { public: LoadedModule(const char *module_name, uptr base_address); + void clear(); void addAddressRange(uptr beg, uptr end, bool executable); bool containsAddress(uptr address) const; const char *full_name() const { return full_name_; } uptr base_address() const { return base_address_; } - uptr n_ranges() const { return n_ranges_; } - uptr address_range_start(int i) const { return ranges_[i].beg; } - uptr address_range_end(int i) const { return ranges_[i].end; } - bool address_range_executable(int i) const { return exec_[i]; } - - private: struct AddressRange { + AddressRange *next; uptr beg; uptr end; + bool executable; + + AddressRange(uptr beg, uptr end, bool executable) + : next(nullptr), beg(beg), end(end), executable(executable) {} }; - char *full_name_; + + typedef IntrusiveList::ConstIterator Iterator; + Iterator ranges() const { return Iterator(&ranges_); } + + private: + char *full_name_; // Owned. uptr base_address_; - static const uptr kMaxNumberOfAddressRanges = 6; - AddressRange ranges_[kMaxNumberOfAddressRanges]; - bool exec_[kMaxNumberOfAddressRanges]; - uptr n_ranges_; + IntrusiveList ranges_; }; // OS-dependent function that fills array with descriptions of at most // "max_modules" currently loaded modules. Returns the number of // initialized modules. If filter is nonzero, ignores modules for which // filter(full_name) is false. 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); #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(); #else INLINE void AndroidLogInit() {} INLINE void AndroidLogWrite(const char *buffer_unused) {} INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; } INLINE void SanitizerInitializeUnwinder() {} #endif + +void *internal_start_thread(void(*func)(void*), void *arg); +void internal_join_thread(void *th); +void MaybeStartBackgroudThread(); // Make the compiler think that something is going on there. // Use this inside a loop that looks like memset/memcpy/etc to prevent the // 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; #else __asm__ __volatile__("" : : "r" (arg) : "memory"); #endif } } // namespace __sanitizer inline void *operator new(__sanitizer::operator_new_size_type size, __sanitizer::LowLevelAllocator &alloc) { return alloc.Allocate(size); } struct StackDepotStats { uptr n_uniq_ids; uptr allocated; }; #endif // SANITIZER_COMMON_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc (revision 279191) @@ -1,4897 +1,4958 @@ //===-- sanitizer_common_interceptors.inc -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Common function interceptors for tools like AddressSanitizer, // ThreadSanitizer, MemorySanitizer, etc. // // This file should be included into the tool's interceptor file, // which has to define it's own macros: // COMMON_INTERCEPTOR_ENTER // COMMON_INTERCEPTOR_ENTER_NOIGNORE // COMMON_INTERCEPTOR_READ_RANGE // COMMON_INTERCEPTOR_WRITE_RANGE // COMMON_INTERCEPTOR_INITIALIZE_RANGE +// COMMON_INTERCEPTOR_DIR_ACQUIRE // COMMON_INTERCEPTOR_FD_ACQUIRE // COMMON_INTERCEPTOR_FD_RELEASE // COMMON_INTERCEPTOR_FD_ACCESS // COMMON_INTERCEPTOR_SET_THREAD_NAME // COMMON_INTERCEPTOR_ON_EXIT // COMMON_INTERCEPTOR_MUTEX_LOCK // COMMON_INTERCEPTOR_MUTEX_UNLOCK // COMMON_INTERCEPTOR_MUTEX_REPAIR // COMMON_INTERCEPTOR_SET_PTHREAD_NAME // COMMON_INTERCEPTOR_HANDLE_RECVMSG // COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED //===----------------------------------------------------------------------===// #include "interception/interception.h" #include "sanitizer_addrhashmap.h" #include "sanitizer_placement_new.h" #include "sanitizer_platform_interceptors.h" #include "sanitizer_tls_get_addr.h" #include #if SANITIZER_WINDOWS && !defined(va_copy) #define va_copy(dst, src) ((dst) = (src)) #endif // _WIN32 #if SANITIZER_FREEBSD #define pthread_setname_np pthread_set_name_np +#define inet_aton __inet_aton +#define inet_pton __inet_pton #endif #ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE #define COMMON_INTERCEPTOR_INITIALIZE_RANGE(p, size) {} #endif #ifndef COMMON_INTERCEPTOR_UNPOISON_PARAM #define COMMON_INTERCEPTOR_UNPOISON_PARAM(count) {} #endif #ifndef COMMON_INTERCEPTOR_FD_ACCESS #define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_LOCK #define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_UNLOCK #define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_REPAIR #define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_HANDLE_RECVMSG #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) ((void)(msg)) #endif #ifndef COMMON_INTERCEPTOR_FILE_OPEN #define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) {} #endif #ifndef COMMON_INTERCEPTOR_FILE_CLOSE #define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) {} #endif #ifndef COMMON_INTERCEPTOR_LIBRARY_LOADED -#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, map) {} +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) {} #endif #ifndef COMMON_INTERCEPTOR_LIBRARY_UNLOADED #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() {} #endif #ifndef COMMON_INTERCEPTOR_ENTER_NOIGNORE #define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, ...) \ COMMON_INTERCEPTOR_ENTER(ctx, __VA_ARGS__) #endif #ifndef COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0) #endif struct FileMetadata { // For open_memstream(). char **addr; SIZE_T *size; }; struct CommonInterceptorMetadata { enum { CIMT_INVALID = 0, CIMT_FILE } type; union { FileMetadata file; }; }; typedef AddrHashMap MetadataHashMap; static MetadataHashMap *interceptor_metadata_map; #if SI_NOT_WINDOWS UNUSED static void SetInterceptorMetadata(__sanitizer_FILE *addr, const FileMetadata &file) { MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr); CHECK(h.created()); h->type = CommonInterceptorMetadata::CIMT_FILE; h->file = file; } UNUSED static const FileMetadata *GetInterceptorMetadata( __sanitizer_FILE *addr) { MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr, /* remove */ false, /* create */ false); if (h.exists()) { CHECK(!h.created()); CHECK(h->type == CommonInterceptorMetadata::CIMT_FILE); return &h->file; } else { return 0; } } UNUSED static void DeleteInterceptorMetadata(void *addr) { MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr, true); CHECK(h.exists()); } #endif // SI_NOT_WINDOWS #if SANITIZER_INTERCEPT_TEXTDOMAIN INTERCEPTOR(char*, textdomain, const char *domainname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname); char* domain = REAL(textdomain)(domainname); if (domain) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1); } return domain; } #define INIT_TEXTDOMAIN COMMON_INTERCEPT_FUNCTION(textdomain) #else #define INIT_TEXTDOMAIN #endif #if SANITIZER_INTERCEPT_STRCMP static inline int CharCmpX(unsigned char c1, unsigned char c2) { return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; } INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2); unsigned char c1, c2; uptr i; for (i = 0;; i++) { c1 = (unsigned char)s1[i]; 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); return CharCmpX(c1, c2); } INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strncmp(s1, s2, size); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size); unsigned char c1 = 0, c2 = 0; uptr i; for (i = 0; i < size; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (c1 != c2 || c1 == '\0') break; } COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size)); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size)); return CharCmpX(c1, c2); } #define INIT_STRCMP COMMON_INTERCEPT_FUNCTION(strcmp) #define INIT_STRNCMP COMMON_INTERCEPT_FUNCTION(strncmp) #else #define INIT_STRCMP #define INIT_STRNCMP #endif #if SANITIZER_INTERCEPT_STRCASECMP static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { int c1_low = ToLower(c1); int c2_low = ToLower(c2); return c1_low - c2_low; } INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcasecmp, s1, s2); unsigned char c1 = 0, c2 = 0; uptr i; for (i = 0;; i++) { c1 = (unsigned char)s1[i]; 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); return CharCaseCmp(c1, c2); } INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strncasecmp, s1, s2, n); unsigned char c1 = 0, c2 = 0; uptr i; for (i = 0; i < n; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, n)); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, n)); return CharCaseCmp(c1, c2); } #define INIT_STRCASECMP COMMON_INTERCEPT_FUNCTION(strcasecmp) #define INIT_STRNCASECMP COMMON_INTERCEPT_FUNCTION(strncasecmp) #else #define INIT_STRCASECMP #define INIT_STRNCASECMP #endif #if SANITIZER_INTERCEPT_MEMCHR INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memchr, s, c, n); void *res = REAL(memchr)(s, c, n); uptr len = res ? (char *)res - (const char *)s + 1 : n; COMMON_INTERCEPTOR_READ_RANGE(ctx, s, len); return res; } #define INIT_MEMCHR COMMON_INTERCEPT_FUNCTION(memchr) #else #define INIT_MEMCHR #endif #if SANITIZER_INTERCEPT_MEMRCHR INTERCEPTOR(void*, memrchr, const void *s, int c, SIZE_T n) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memrchr, s, c, n); COMMON_INTERCEPTOR_READ_RANGE(ctx, s, n); return REAL(memrchr)(s, c, n); } #define INIT_MEMRCHR COMMON_INTERCEPT_FUNCTION(memrchr) #else #define INIT_MEMRCHR #endif #if SANITIZER_INTERCEPT_FREXP INTERCEPTOR(double, frexp, double x, int *exp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, frexp, x, exp); // Assuming frexp() always writes to |exp|. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); double res = REAL(frexp)(x, exp); return res; } #define INIT_FREXP COMMON_INTERCEPT_FUNCTION(frexp); #else #define INIT_FREXP #endif // SANITIZER_INTERCEPT_FREXP #if SANITIZER_INTERCEPT_FREXPF_FREXPL INTERCEPTOR(float, frexpf, float x, int *exp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, frexpf, x, exp); // 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. float res = REAL(frexpf)(x, exp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); return res; } INTERCEPTOR(long double, frexpl, long double x, int *exp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, frexpl, x, exp); // 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. long double res = REAL(frexpl)(x, exp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); return res; } #define INIT_FREXPF_FREXPL \ COMMON_INTERCEPT_FUNCTION(frexpf); \ COMMON_INTERCEPT_FUNCTION(frexpl) #else #define INIT_FREXPF_FREXPL #endif // SANITIZER_INTERCEPT_FREXPF_FREXPL #if SI_NOT_WINDOWS static void write_iovec(void *ctx, struct __sanitizer_iovec *iovec, SIZE_T iovlen, SIZE_T maxlen) { for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { SSIZE_T sz = Min(iovec[i].iov_len, maxlen); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec[i].iov_base, sz); maxlen -= sz; } } static void read_iovec(void *ctx, struct __sanitizer_iovec *iovec, SIZE_T iovlen, SIZE_T maxlen) { COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec) * iovlen); for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { SSIZE_T sz = Min(iovec[i].iov_len, maxlen); COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec[i].iov_base, sz); maxlen -= sz; } } #endif #if SANITIZER_INTERCEPT_READ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // 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(read)(fd, ptr, count); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_READ COMMON_INTERCEPT_FUNCTION(read) #else #define INIT_READ #endif #if SANITIZER_INTERCEPT_PREAD INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // 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(pread)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREAD COMMON_INTERCEPT_FUNCTION(pread) #else #define INIT_PREAD #endif #if SANITIZER_INTERCEPT_PREAD64 INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // 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(pread64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREAD64 COMMON_INTERCEPT_FUNCTION(pread64) #else #define INIT_PREAD64 #endif #if SANITIZER_INTERCEPT_READV INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov, int iovcnt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(readv)(fd, iov, iovcnt); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_READV COMMON_INTERCEPT_FUNCTION(readv) #else #define INIT_READV #endif #if SANITIZER_INTERCEPT_PREADV INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREADV COMMON_INTERCEPT_FUNCTION(preadv) #else #define INIT_PREADV #endif #if SANITIZER_INTERCEPT_PREADV64 INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREADV64 COMMON_INTERCEPT_FUNCTION(preadv64) #else #define INIT_PREADV64 #endif #if SANITIZER_INTERCEPT_WRITE INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(write)(fd, ptr, count); // FIXME: this check should be _before_ the call to REAL(write), not after if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } #define INIT_WRITE COMMON_INTERCEPT_FUNCTION(write) #else #define INIT_WRITE #endif #if SANITIZER_INTERCEPT_PWRITE INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } #define INIT_PWRITE COMMON_INTERCEPT_FUNCTION(pwrite) #else #define INIT_PWRITE #endif #if SANITIZER_INTERCEPT_PWRITE64 INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } #define INIT_PWRITE64 COMMON_INTERCEPT_FUNCTION(pwrite64) #else #define INIT_PWRITE64 #endif #if SANITIZER_INTERCEPT_WRITEV INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov, int iovcnt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(writev)(fd, iov, iovcnt); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } #define INIT_WRITEV COMMON_INTERCEPT_FUNCTION(writev) #else #define INIT_WRITEV #endif #if SANITIZER_INTERCEPT_PWRITEV INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } #define INIT_PWRITEV COMMON_INTERCEPT_FUNCTION(pwritev) #else #define INIT_PWRITEV #endif #if SANITIZER_INTERCEPT_PWRITEV64 INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } #define INIT_PWRITEV64 COMMON_INTERCEPT_FUNCTION(pwritev64) #else #define INIT_PWRITEV64 #endif #if SANITIZER_INTERCEPT_PRCTL INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3, // NOLINT unsigned long arg4, unsigned long arg5) { // NOLINT void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5); static const int PR_SET_NAME = 15; int res = REAL(prctl(option, arg2, arg3, arg4, arg5)); if (option == PR_SET_NAME) { char buff[16]; internal_strncpy(buff, (char *)arg2, 15); buff[15] = 0; COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff); } return res; } #define INIT_PRCTL COMMON_INTERCEPT_FUNCTION(prctl) #else #define INIT_PRCTL #endif // SANITIZER_INTERCEPT_PRCTL #if SANITIZER_INTERCEPT_TIME INTERCEPTOR(unsigned long, time, unsigned long *t) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, time, t); unsigned long local_t; unsigned long res = REAL(time)(&local_t); if (t && res != (unsigned long)-1) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, t, sizeof(*t)); *t = local_t; } return res; } #define INIT_TIME COMMON_INTERCEPT_FUNCTION(time); #else #define INIT_TIME #endif // SANITIZER_INTERCEPT_TIME #if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS static void unpoison_tm(void *ctx, __sanitizer_tm *tm) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); if (tm->tm_zone) { // Can not use COMMON_INTERCEPTOR_WRITE_RANGE here, because tm->tm_zone // can point to shared memory and tsan would report a data race. COMMON_INTERCEPTOR_INITIALIZE_RANGE(tm->tm_zone, REAL(strlen(tm->tm_zone)) + 1); } } INTERCEPTOR(__sanitizer_tm *, localtime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, localtime, timep); __sanitizer_tm *res = REAL(localtime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(__sanitizer_tm *, localtime_r, unsigned long *timep, void *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, localtime_r, timep, result); __sanitizer_tm *res = REAL(localtime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(__sanitizer_tm *, gmtime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gmtime, timep); __sanitizer_tm *res = REAL(gmtime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(__sanitizer_tm *, gmtime_r, unsigned long *timep, void *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gmtime_r, timep, result); __sanitizer_tm *res = REAL(gmtime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(char *, ctime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ctime, timep); // 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. char *res = REAL(ctime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ctime_r, timep, result); // 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. char *res = REAL(ctime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm); // 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. char *res = REAL(asctime)(tm); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(char *, asctime_r, __sanitizer_tm *tm, char *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, asctime_r, tm, result); // 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. char *res = REAL(asctime_r)(tm, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(long, mktime, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mktime, tm); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_sec, sizeof(tm->tm_sec)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_min, sizeof(tm->tm_min)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_hour, sizeof(tm->tm_hour)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_mday, sizeof(tm->tm_mday)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_mon, sizeof(tm->tm_mon)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_year, sizeof(tm->tm_year)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_isdst, sizeof(tm->tm_isdst)); long res = REAL(mktime)(tm); if (res != -1) unpoison_tm(ctx, tm); return res; } #define INIT_LOCALTIME_AND_FRIENDS \ COMMON_INTERCEPT_FUNCTION(localtime); \ COMMON_INTERCEPT_FUNCTION(localtime_r); \ COMMON_INTERCEPT_FUNCTION(gmtime); \ COMMON_INTERCEPT_FUNCTION(gmtime_r); \ COMMON_INTERCEPT_FUNCTION(ctime); \ COMMON_INTERCEPT_FUNCTION(ctime_r); \ COMMON_INTERCEPT_FUNCTION(asctime); \ COMMON_INTERCEPT_FUNCTION(asctime_r); \ COMMON_INTERCEPT_FUNCTION(mktime); #else #define INIT_LOCALTIME_AND_FRIENDS #endif // SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS #if SANITIZER_INTERCEPT_STRPTIME INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strptime, s, format, tm); if (format) COMMON_INTERCEPTOR_READ_RANGE(ctx, format, REAL(strlen)(format) + 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. char *res = REAL(strptime)(s, format, tm); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s); // 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)); } return res; } #define INIT_STRPTIME COMMON_INTERCEPT_FUNCTION(strptime); #else #define INIT_STRPTIME #endif #if SANITIZER_INTERCEPT_SCANF || SANITIZER_INTERCEPT_PRINTF #include "sanitizer_common_interceptors_format.inc" #define FORMAT_INTERCEPTOR_IMPL(name, vname, ...) \ { \ void *ctx; \ va_list ap; \ va_start(ap, format); \ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap); \ int res = WRAP(vname)(__VA_ARGS__, ap); \ va_end(ap); \ return res; \ } #endif #if SANITIZER_INTERCEPT_SCANF #define VSCANF_INTERCEPTOR_IMPL(vname, allowGnuMalloc, ...) \ { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ va_list aq; \ va_copy(aq, ap); \ int res = REAL(vname)(__VA_ARGS__); \ if (res > 0) \ scanf_common(ctx, res, allowGnuMalloc, format, aq); \ va_end(aq); \ return res; \ } INTERCEPTOR(int, vscanf, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(vscanf, true, format, ap) INTERCEPTOR(int, vsscanf, const char *str, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(vsscanf, true, str, format, ap) INTERCEPTOR(int, vfscanf, void *stream, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(vfscanf, true, stream, format, ap) #if SANITIZER_INTERCEPT_ISOC99_SCANF INTERCEPTOR(int, __isoc99_vscanf, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(__isoc99_vscanf, false, format, ap) INTERCEPTOR(int, __isoc99_vsscanf, const char *str, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(__isoc99_vsscanf, false, str, format, ap) INTERCEPTOR(int, __isoc99_vfscanf, void *stream, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap) #endif // SANITIZER_INTERCEPT_ISOC99_SCANF INTERCEPTOR(int, scanf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(scanf, vscanf, format) INTERCEPTOR(int, fscanf, void *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format) INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format) #if SANITIZER_INTERCEPT_ISOC99_SCANF INTERCEPTOR(int, __isoc99_scanf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format) INTERCEPTOR(int, __isoc99_fscanf, void *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format) INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) #endif #endif #if SANITIZER_INTERCEPT_SCANF #define INIT_SCANF \ COMMON_INTERCEPT_FUNCTION(scanf); \ COMMON_INTERCEPT_FUNCTION(sscanf); \ COMMON_INTERCEPT_FUNCTION(fscanf); \ COMMON_INTERCEPT_FUNCTION(vscanf); \ COMMON_INTERCEPT_FUNCTION(vsscanf); \ COMMON_INTERCEPT_FUNCTION(vfscanf); #else #define INIT_SCANF #endif #if SANITIZER_INTERCEPT_ISOC99_SCANF #define INIT_ISOC99_SCANF \ COMMON_INTERCEPT_FUNCTION(__isoc99_scanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_sscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_fscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vsscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vfscanf); #else #define INIT_ISOC99_SCANF #endif #if SANITIZER_INTERCEPT_PRINTF #define VPRINTF_INTERCEPTOR_ENTER(vname, ...) \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ va_list aq; \ va_copy(aq, ap); #define VPRINTF_INTERCEPTOR_RETURN() \ va_end(aq); #define VPRINTF_INTERCEPTOR_IMPL(vname, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, __VA_ARGS__); \ if (common_flags()->check_printf) \ printf_common(ctx, format, aq); \ int res = REAL(vname)(__VA_ARGS__); \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See // https://code.google.com/p/address-sanitizer/issues/detail?id=321. #define VSPRINTF_INTERCEPTOR_IMPL(vname, str, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, str, __VA_ARGS__) \ if (common_flags()->check_printf) { \ printf_common(ctx, format, aq); \ } \ int res = REAL(vname)(str, __VA_ARGS__); \ if (res >= 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, res + 1); \ } \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See // https://code.google.com/p/address-sanitizer/issues/detail?id=321. #define VSNPRINTF_INTERCEPTOR_IMPL(vname, str, size, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, str, size, __VA_ARGS__) \ if (common_flags()->check_printf) { \ printf_common(ctx, format, aq); \ } \ int res = REAL(vname)(str, size, __VA_ARGS__); \ if (res >= 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, Min(size, (SIZE_T)(res + 1))); \ } \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See // https://code.google.com/p/address-sanitizer/issues/detail?id=321. #define VASPRINTF_INTERCEPTOR_IMPL(vname, strp, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, strp, __VA_ARGS__) \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, strp, sizeof(char *)); \ if (common_flags()->check_printf) { \ printf_common(ctx, format, aq); \ } \ int res = REAL(vname)(strp, __VA_ARGS__); \ if (res >= 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *strp, res + 1); \ } \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } INTERCEPTOR(int, vprintf, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(vprintf, format, ap) INTERCEPTOR(int, vfprintf, __sanitizer_FILE *stream, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(vfprintf, stream, format, ap) INTERCEPTOR(int, vsnprintf, char *str, SIZE_T size, const char *format, va_list ap) VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf, str, size, format, ap) +#if SANITIZER_INTERCEPT_PRINTF_L +INTERCEPTOR(int, vsnprintf_l, char *str, SIZE_T size, void *loc, + const char *format, va_list ap) +VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf_l, str, size, loc, format, ap) + +INTERCEPTOR(int, snprintf_l, char *str, SIZE_T size, void *loc, + const char *format, ...) +FORMAT_INTERCEPTOR_IMPL(snprintf_l, vsnprintf_l, str, size, loc, format) +#endif // SANITIZER_INTERCEPT_PRINTF_L + INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) VSPRINTF_INTERCEPTOR_IMPL(vsprintf, str, format, ap) INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap) VASPRINTF_INTERCEPTOR_IMPL(vasprintf, strp, format, ap) #if SANITIZER_INTERCEPT_ISOC99_PRINTF INTERCEPTOR(int, __isoc99_vprintf, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(__isoc99_vprintf, format, ap) INTERCEPTOR(int, __isoc99_vfprintf, __sanitizer_FILE *stream, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(__isoc99_vfprintf, stream, format, ap) INTERCEPTOR(int, __isoc99_vsnprintf, char *str, SIZE_T size, const char *format, va_list ap) VSNPRINTF_INTERCEPTOR_IMPL(__isoc99_vsnprintf, str, size, format, ap) INTERCEPTOR(int, __isoc99_vsprintf, char *str, const char *format, va_list ap) VSPRINTF_INTERCEPTOR_IMPL(__isoc99_vsprintf, str, format, ap) #endif // SANITIZER_INTERCEPT_ISOC99_PRINTF INTERCEPTOR(int, printf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(printf, vprintf, format) INTERCEPTOR(int, fprintf, __sanitizer_FILE *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(fprintf, vfprintf, stream, format) INTERCEPTOR(int, sprintf, char *str, const char *format, ...) // NOLINT FORMAT_INTERCEPTOR_IMPL(sprintf, vsprintf, str, format) // NOLINT INTERCEPTOR(int, snprintf, char *str, SIZE_T size, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(snprintf, vsnprintf, str, size, format) INTERCEPTOR(int, asprintf, char **strp, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(asprintf, vasprintf, strp, format) #if SANITIZER_INTERCEPT_ISOC99_PRINTF INTERCEPTOR(int, __isoc99_printf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_printf, __isoc99_vprintf, format) INTERCEPTOR(int, __isoc99_fprintf, __sanitizer_FILE *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_fprintf, __isoc99_vfprintf, stream, format) INTERCEPTOR(int, __isoc99_sprintf, char *str, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_sprintf, __isoc99_vsprintf, str, format) INTERCEPTOR(int, __isoc99_snprintf, char *str, SIZE_T size, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size, format) #endif // SANITIZER_INTERCEPT_ISOC99_PRINTF #endif // SANITIZER_INTERCEPT_PRINTF #if SANITIZER_INTERCEPT_PRINTF #define INIT_PRINTF \ COMMON_INTERCEPT_FUNCTION(printf); \ COMMON_INTERCEPT_FUNCTION(sprintf); \ COMMON_INTERCEPT_FUNCTION(snprintf); \ COMMON_INTERCEPT_FUNCTION(asprintf); \ COMMON_INTERCEPT_FUNCTION(fprintf); \ COMMON_INTERCEPT_FUNCTION(vprintf); \ COMMON_INTERCEPT_FUNCTION(vsprintf); \ COMMON_INTERCEPT_FUNCTION(vsnprintf); \ COMMON_INTERCEPT_FUNCTION(vasprintf); \ COMMON_INTERCEPT_FUNCTION(vfprintf); #else #define INIT_PRINTF #endif +#if SANITIZER_INTERCEPT_PRINTF_L +#define INIT_PRINTF_L \ + COMMON_INTERCEPT_FUNCTION(snprintf_l); \ + COMMON_INTERCEPT_FUNCTION(vsnprintf_l); +#else +#define INIT_PRINTF_L +#endif + #if SANITIZER_INTERCEPT_ISOC99_PRINTF #define INIT_ISOC99_PRINTF \ COMMON_INTERCEPT_FUNCTION(__isoc99_printf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_sprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_snprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_fprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vsprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vsnprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vfprintf); #else #define INIT_ISOC99_PRINTF #endif #if SANITIZER_INTERCEPT_IOCTL #include "sanitizer_common_interceptors_ioctl.inc" -INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) { +INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) { void *ctx; + va_list ap; + va_start(ap, request); + void *arg = va_arg(ap, void *); + va_end(ap); COMMON_INTERCEPTOR_ENTER(ctx, ioctl, d, request, arg); CHECK(ioctl_initialized); // Note: TSan does not use common flags, and they are zero-initialized. // This effectively disables ioctl handling in TSan. if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg); + // Although request is unsigned long, the rest of the interceptor uses it + // as just "unsigned" to save space, because we know that all values fit in + // "unsigned" - they are compile-time constants. + const ioctl_desc *desc = ioctl_lookup(request); ioctl_desc decoded_desc; if (!desc) { VPrintf(2, "Decoding unknown ioctl 0x%x\n", request); if (!ioctl_decode(request, &decoded_desc)) Printf("WARNING: failed decoding unknown ioctl 0x%x\n", request); else desc = &decoded_desc; } if (desc) ioctl_common_pre(ctx, desc, d, request, arg); int res = REAL(ioctl)(d, request, arg); // FIXME: some ioctls have different return values for success and failure. if (desc && res != -1) ioctl_common_post(ctx, desc, res, d, request, arg); return res; } #define INIT_IOCTL \ ioctl_init(); \ COMMON_INTERCEPT_FUNCTION(ioctl); #else #define INIT_IOCTL #endif #if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || \ SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || \ SANITIZER_INTERCEPT_GETPWENT_R || SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS static void unpoison_passwd(void *ctx, __sanitizer_passwd *pwd) { if (pwd) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, sizeof(*pwd)); if (pwd->pw_name) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_name, REAL(strlen)(pwd->pw_name) + 1); if (pwd->pw_passwd) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_passwd, REAL(strlen)(pwd->pw_passwd) + 1); #if !SANITIZER_ANDROID if (pwd->pw_gecos) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_gecos, REAL(strlen)(pwd->pw_gecos) + 1); #endif #if SANITIZER_MAC if (pwd->pw_class) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_class, REAL(strlen)(pwd->pw_class) + 1); #endif if (pwd->pw_dir) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_dir, REAL(strlen)(pwd->pw_dir) + 1); if (pwd->pw_shell) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_shell, REAL(strlen)(pwd->pw_shell) + 1); } } static void unpoison_group(void *ctx, __sanitizer_group *grp) { if (grp) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, sizeof(*grp)); if (grp->gr_name) COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_name, REAL(strlen)(grp->gr_name) + 1); if (grp->gr_passwd) COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_passwd, REAL(strlen)(grp->gr_passwd) + 1); char **p = grp->gr_mem; for (; *p; ++p) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(*p, REAL(strlen)(*p) + 1); } COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_mem, (p - grp->gr_mem + 1) * sizeof(*p)); } } #endif // SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || // SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || // SANITIZER_INTERCEPT_GETPWENT_R || // SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS #if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS INTERCEPTOR(__sanitizer_passwd *, getpwnam, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); __sanitizer_passwd *res = REAL(getpwnam)(name); if (res != 0) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_passwd *, getpwuid, u32 uid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid); __sanitizer_passwd *res = REAL(getpwuid)(uid); if (res != 0) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrnam, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); __sanitizer_group *res = REAL(getgrnam)(name); if (res != 0) unpoison_group(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrgid, u32 gid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid); __sanitizer_group *res = REAL(getgrgid)(gid); if (res != 0) unpoison_group(ctx, res); return res; } #define INIT_GETPWNAM_AND_FRIENDS \ COMMON_INTERCEPT_FUNCTION(getpwnam); \ COMMON_INTERCEPT_FUNCTION(getpwuid); \ COMMON_INTERCEPT_FUNCTION(getgrnam); \ COMMON_INTERCEPT_FUNCTION(getgrgid); #else #define INIT_GETPWNAM_AND_FRIENDS #endif #if SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS INTERCEPTOR(int, getpwnam_r, const char *name, __sanitizer_passwd *pwd, char *buf, SIZE_T buflen, __sanitizer_passwd **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwnam_r, name, pwd, buf, buflen, result); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 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. int res = REAL(getpwnam_r)(name, pwd, buf, buflen, result); if (!res) { if (result && *result) unpoison_passwd(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, getpwuid_r, u32 uid, __sanitizer_passwd *pwd, char *buf, SIZE_T buflen, __sanitizer_passwd **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result); // 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. int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result); if (!res) { if (result && *result) unpoison_passwd(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, getgrnam_r, const char *name, __sanitizer_group *grp, char *buf, SIZE_T buflen, __sanitizer_group **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrnam_r, name, grp, buf, buflen, result); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 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. int res = REAL(getgrnam_r)(name, grp, buf, buflen, result); if (!res) { if (result && *result) unpoison_group(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, getgrgid_r, u32 gid, __sanitizer_group *grp, char *buf, SIZE_T buflen, __sanitizer_group **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result); // 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. int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result); if (!res) { if (result && *result) unpoison_group(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } #define INIT_GETPWNAM_R_AND_FRIENDS \ COMMON_INTERCEPT_FUNCTION(getpwnam_r); \ COMMON_INTERCEPT_FUNCTION(getpwuid_r); \ COMMON_INTERCEPT_FUNCTION(getgrnam_r); \ COMMON_INTERCEPT_FUNCTION(getgrgid_r); #else #define INIT_GETPWNAM_R_AND_FRIENDS #endif #if SANITIZER_INTERCEPT_GETPWENT INTERCEPTOR(__sanitizer_passwd *, getpwent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwent, dummy); __sanitizer_passwd *res = REAL(getpwent)(dummy); if (res != 0) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrent, dummy); __sanitizer_group *res = REAL(getgrent)(dummy); if (res != 0) unpoison_group(ctx, res);; return res; } #define INIT_GETPWENT \ COMMON_INTERCEPT_FUNCTION(getpwent); \ COMMON_INTERCEPT_FUNCTION(getgrent); #else #define INIT_GETPWENT #endif #if SANITIZER_INTERCEPT_FGETPWENT INTERCEPTOR(__sanitizer_passwd *, fgetpwent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent, fp); __sanitizer_passwd *res = REAL(fgetpwent)(fp); if (res != 0) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, fgetgrent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent, fp); __sanitizer_group *res = REAL(fgetgrent)(fp); if (res != 0) unpoison_group(ctx, res); return res; } #define INIT_FGETPWENT \ COMMON_INTERCEPT_FUNCTION(fgetpwent); \ COMMON_INTERCEPT_FUNCTION(fgetgrent); #else #define INIT_FGETPWENT #endif #if SANITIZER_INTERCEPT_GETPWENT_R INTERCEPTOR(int, getpwent_r, __sanitizer_passwd *pwbuf, char *buf, SIZE_T buflen, __sanitizer_passwd **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwent_r, pwbuf, buf, buflen, pwbufp); // 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. int res = REAL(getpwent_r)(pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf, SIZE_T buflen, __sanitizer_passwd **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp); // 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. int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen, __sanitizer_group **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp); // 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. int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } INTERCEPTOR(int, fgetgrent_r, void *fp, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen, __sanitizer_group **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent_r, fp, pwbuf, buf, buflen, pwbufp); // 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. int res = REAL(fgetgrent_r)(fp, pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } #define INIT_GETPWENT_R \ COMMON_INTERCEPT_FUNCTION(getpwent_r); \ COMMON_INTERCEPT_FUNCTION(fgetpwent_r); \ COMMON_INTERCEPT_FUNCTION(getgrent_r); \ COMMON_INTERCEPT_FUNCTION(fgetgrent_r); #else #define INIT_GETPWENT_R #endif #if SANITIZER_INTERCEPT_SETPWENT // The only thing these interceptors do is disable any nested interceptors. // These functions may open nss modules and call uninstrumented functions from // them, and we don't want things like strlen() to trigger. INTERCEPTOR(void, setpwent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setpwent, dummy); REAL(setpwent)(dummy); } INTERCEPTOR(void, endpwent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, endpwent, dummy); REAL(endpwent)(dummy); } INTERCEPTOR(void, setgrent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setgrent, dummy); REAL(setgrent)(dummy); } INTERCEPTOR(void, endgrent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, endgrent, dummy); REAL(endgrent)(dummy); } #define INIT_SETPWENT \ COMMON_INTERCEPT_FUNCTION(setpwent); \ COMMON_INTERCEPT_FUNCTION(endpwent); \ COMMON_INTERCEPT_FUNCTION(setgrent); \ COMMON_INTERCEPT_FUNCTION(endgrent); #else #define INIT_SETPWENT #endif #if SANITIZER_INTERCEPT_CLOCK_GETTIME INTERCEPTOR(int, clock_getres, u32 clk_id, void *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, clock_getres, clk_id, tp); // 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. int res = REAL(clock_getres)(clk_id, tp); if (!res && tp) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); } return res; } INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, clock_gettime, clk_id, tp); // 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. int res = REAL(clock_gettime)(clk_id, tp); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); } return res; } INTERCEPTOR(int, clock_settime, u32 clk_id, const void *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, clock_settime, clk_id, tp); COMMON_INTERCEPTOR_READ_RANGE(ctx, tp, struct_timespec_sz); return REAL(clock_settime)(clk_id, tp); } #define INIT_CLOCK_GETTIME \ COMMON_INTERCEPT_FUNCTION(clock_getres); \ COMMON_INTERCEPT_FUNCTION(clock_gettime); \ COMMON_INTERCEPT_FUNCTION(clock_settime); #else #define INIT_CLOCK_GETTIME #endif #if SANITIZER_INTERCEPT_GETITIMER INTERCEPTOR(int, getitimer, int which, void *curr_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getitimer, which, curr_value); // 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. int res = REAL(getitimer)(which, curr_value); if (!res && curr_value) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerval_sz); } return res; } INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setitimer, which, new_value, old_value); if (new_value) COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz); // 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. int res = REAL(setitimer)(which, new_value, old_value); if (!res && old_value) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerval_sz); } return res; } #define INIT_GETITIMER \ COMMON_INTERCEPT_FUNCTION(getitimer); \ COMMON_INTERCEPT_FUNCTION(setitimer); #else #define INIT_GETITIMER #endif #if SANITIZER_INTERCEPT_GLOB static void unpoison_glob_t(void *ctx, __sanitizer_glob_t *pglob) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pglob, sizeof(*pglob)); // +1 for NULL pointer at the end. if (pglob->gl_pathv) COMMON_INTERCEPTOR_WRITE_RANGE( ctx, pglob->gl_pathv, (pglob->gl_pathc + 1) * sizeof(*pglob->gl_pathv)); for (SIZE_T i = 0; i < pglob->gl_pathc; ++i) { char *p = pglob->gl_pathv[i]; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, REAL(strlen)(p) + 1); } } static THREADLOCAL __sanitizer_glob_t *pglob_copy; static void wrapped_gl_closedir(void *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); pglob_copy->gl_closedir(dir); } static void *wrapped_gl_readdir(void *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); return pglob_copy->gl_readdir(dir); } static void *wrapped_gl_opendir(const char *s) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return pglob_copy->gl_opendir(s); } static int wrapped_gl_lstat(const char *s, void *st) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return pglob_copy->gl_lstat(s, st); } static int wrapped_gl_stat(const char *s, void *st) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return pglob_copy->gl_stat(s, st); } INTERCEPTOR(int, glob, const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); __sanitizer_glob_t glob_copy = { 0, 0, 0, 0, wrapped_gl_closedir, wrapped_gl_readdir, wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); pglob_copy = &glob_copy; } int res = REAL(glob)(pattern, flags, errfunc, pglob); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); } pglob_copy = 0; if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); return res; } INTERCEPTOR(int, glob64, const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); __sanitizer_glob_t glob_copy = { 0, 0, 0, 0, wrapped_gl_closedir, wrapped_gl_readdir, wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); pglob_copy = &glob_copy; } int res = REAL(glob64)(pattern, flags, errfunc, pglob); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); } pglob_copy = 0; if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); return res; } #define INIT_GLOB \ COMMON_INTERCEPT_FUNCTION(glob); \ COMMON_INTERCEPT_FUNCTION(glob64); #else // SANITIZER_INTERCEPT_GLOB #define INIT_GLOB #endif // SANITIZER_INTERCEPT_GLOB #if SANITIZER_INTERCEPT_WAIT // According to sys/wait.h, wait(), waitid(), waitpid() may have symbol version // suffixes on Darwin. See the declaration of INTERCEPTOR_WITH_SUFFIX for // details. INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wait, status); // 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. int res = REAL(wait)(status); if (res != -1 && status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); return res; } // On FreeBSD id_t is always 64-bit wide. #if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, long long id, void *infop, int options) { #else INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop, int options) { #endif void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options); // 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. int res = REAL(waitid)(idtype, id, infop, options); if (res != -1 && infop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz); return res; } INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, waitpid, pid, status, options); // 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. int res = REAL(waitpid)(pid, status, options); if (res != -1 && status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); return res; } INTERCEPTOR(int, wait3, int *status, int options, void *rusage) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage); // 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. int res = REAL(wait3)(status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } #if SANITIZER_ANDROID INTERCEPTOR(int, __wait4, int pid, int *status, int options, void *rusage) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __wait4, pid, status, options, rusage); // 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. int res = REAL(__wait4)(pid, status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } #define INIT_WAIT4 COMMON_INTERCEPT_FUNCTION(__wait4); #else INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage); // 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. int res = REAL(wait4)(pid, status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } #define INIT_WAIT4 COMMON_INTERCEPT_FUNCTION(wait4); #endif // SANITIZER_ANDROID #define INIT_WAIT \ COMMON_INTERCEPT_FUNCTION(wait); \ COMMON_INTERCEPT_FUNCTION(waitid); \ COMMON_INTERCEPT_FUNCTION(waitpid); \ COMMON_INTERCEPT_FUNCTION(wait3); #else #define INIT_WAIT #define INIT_WAIT4 #endif #if SANITIZER_INTERCEPT_INET INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_ntop, af, src, dst, size); uptr sz = __sanitizer_in_addr_sz(af); if (sz) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sz); // 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 // https://code.google.com/p/address-sanitizer/issues/detail?id=321. char *res = REAL(inet_ntop)(af, src, dst, size); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst); // 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 // https://code.google.com/p/address-sanitizer/issues/detail?id=321. int res = REAL(inet_pton)(af, src, dst); if (res == 1) { uptr sz = __sanitizer_in_addr_sz(af); if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); } return res; } #define INIT_INET \ COMMON_INTERCEPT_FUNCTION(inet_ntop); \ COMMON_INTERCEPT_FUNCTION(inet_pton); #else #define INIT_INET #endif #if SANITIZER_INTERCEPT_INET INTERCEPTOR(int, inet_aton, const char *cp, void *dst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_aton, cp, dst); if (cp) COMMON_INTERCEPTOR_READ_RANGE(ctx, cp, REAL(strlen)(cp) + 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. int res = REAL(inet_aton)(cp, dst); if (res != 0) { uptr sz = __sanitizer_in_addr_sz(af_inet); if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); } return res; } #define INIT_INET_ATON COMMON_INTERCEPT_FUNCTION(inet_aton); #else #define INIT_INET_ATON #endif #if SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_getschedparam, thread, policy, param); // 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. int res = REAL(pthread_getschedparam)(thread, policy, param); if (res == 0) { if (policy) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, policy, sizeof(*policy)); if (param) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, sizeof(*param)); } return res; } #define INIT_PTHREAD_GETSCHEDPARAM \ COMMON_INTERCEPT_FUNCTION(pthread_getschedparam); #else #define INIT_PTHREAD_GETSCHEDPARAM #endif #if SANITIZER_INTERCEPT_GETADDRINFO INTERCEPTOR(int, getaddrinfo, char *node, char *service, struct __sanitizer_addrinfo *hints, struct __sanitizer_addrinfo **out) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getaddrinfo, node, service, hints, out); if (node) COMMON_INTERCEPTOR_READ_RANGE(ctx, node, REAL(strlen)(node) + 1); if (service) COMMON_INTERCEPTOR_READ_RANGE(ctx, service, REAL(strlen)(service) + 1); if (hints) COMMON_INTERCEPTOR_READ_RANGE(ctx, hints, sizeof(__sanitizer_addrinfo)); // 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. int res = REAL(getaddrinfo)(node, service, hints, out); if (res == 0 && out) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, out, sizeof(*out)); struct __sanitizer_addrinfo *p = *out; while (p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); if (p->ai_addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_addr, p->ai_addrlen); if (p->ai_canonname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_canonname, REAL(strlen)(p->ai_canonname) + 1); p = p->ai_next; } } return res; } #define INIT_GETADDRINFO COMMON_INTERCEPT_FUNCTION(getaddrinfo); #else #define INIT_GETADDRINFO #endif #if SANITIZER_INTERCEPT_GETNAMEINFO INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host, unsigned hostlen, char *serv, unsigned servlen, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getnameinfo, sockaddr, salen, host, hostlen, serv, servlen, flags); // FIXME: consider adding READ_RANGE(sockaddr, salen) // There is padding in in_addr that may make this too noisy // 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. int res = REAL(getnameinfo)(sockaddr, salen, host, hostlen, serv, servlen, flags); if (res == 0) { if (host && hostlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, host, REAL(strlen)(host) + 1); if (serv && servlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, serv, REAL(strlen)(serv) + 1); } return res; } #define INIT_GETNAMEINFO COMMON_INTERCEPT_FUNCTION(getnameinfo); #else #define INIT_GETNAMEINFO #endif #if SANITIZER_INTERCEPT_GETSOCKNAME INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getsockname, sock_fd, addr, addrlen); COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); int addrlen_in = *addrlen; // 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. int res = REAL(getsockname)(sock_fd, addr, addrlen); if (res == 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addrlen_in, *addrlen)); } return res; } #define INIT_GETSOCKNAME COMMON_INTERCEPT_FUNCTION(getsockname); #else #define INIT_GETSOCKNAME #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME || SANITIZER_INTERCEPT_GETHOSTBYNAME_R static void write_hostent(void *ctx, struct __sanitizer_hostent *h) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h, sizeof(__sanitizer_hostent)); if (h->h_name) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h->h_name, REAL(strlen)(h->h_name) + 1); char **p = h->h_aliases; while (*p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1); ++p; } COMMON_INTERCEPTOR_WRITE_RANGE( ctx, h->h_aliases, (p - h->h_aliases + 1) * sizeof(*h->h_aliases)); p = h->h_addr_list; while (*p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, h->h_length); ++p; } COMMON_INTERCEPTOR_WRITE_RANGE( ctx, h->h_addr_list, (p - h->h_addr_list + 1) * sizeof(*h->h_addr_list)); } #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname, char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname, name); struct __sanitizer_hostent *res = REAL(gethostbyname)(name); if (res) write_hostent(ctx, res); return res; } INTERCEPTOR(struct __sanitizer_hostent *, gethostbyaddr, void *addr, int len, int type) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr, addr, len, type); COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); struct __sanitizer_hostent *res = REAL(gethostbyaddr)(addr, len, type); if (res) write_hostent(ctx, res); return res; } INTERCEPTOR(struct __sanitizer_hostent *, gethostent, int fake) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostent, fake); struct __sanitizer_hostent *res = REAL(gethostent)(fake); if (res) write_hostent(ctx, res); return res; } INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname2, char *name, int af) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2, name, af); struct __sanitizer_hostent *res = REAL(gethostbyname2)(name, af); if (res) write_hostent(ctx, res); return res; } #define INIT_GETHOSTBYNAME \ COMMON_INTERCEPT_FUNCTION(gethostent); \ COMMON_INTERCEPT_FUNCTION(gethostbyaddr); \ COMMON_INTERCEPT_FUNCTION(gethostbyname); \ COMMON_INTERCEPT_FUNCTION(gethostbyname2); #else #define INIT_GETHOSTBYNAME #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME_R INTERCEPTOR(int, gethostbyname_r, char *name, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname_r, name, ret, buf, buflen, result, h_errnop); // 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. int res = REAL(gethostbyname_r)(name, ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTBYNAME_R COMMON_INTERCEPT_FUNCTION(gethostbyname_r); #else #define INIT_GETHOSTBYNAME_R #endif #if SANITIZER_INTERCEPT_GETHOSTENT_R INTERCEPTOR(int, gethostent_r, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostent_r, ret, buf, buflen, result, h_errnop); // 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. int res = REAL(gethostent_r)(ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTENT_R \ COMMON_INTERCEPT_FUNCTION(gethostent_r); #else #define INIT_GETHOSTENT_R #endif #if SANITIZER_INTERCEPT_GETHOSTBYADDR_R INTERCEPTOR(int, gethostbyaddr_r, void *addr, int len, int type, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr_r, addr, len, type, ret, buf, buflen, result, h_errnop); COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); // 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. int res = REAL(gethostbyaddr_r)(addr, len, type, ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTBYADDR_R \ COMMON_INTERCEPT_FUNCTION(gethostbyaddr_r); #else #define INIT_GETHOSTBYADDR_R #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME2_R INTERCEPTOR(int, gethostbyname2_r, char *name, int af, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2_r, name, af, ret, buf, buflen, result, h_errnop); // 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. int res = REAL(gethostbyname2_r)(name, af, ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTBYNAME2_R \ COMMON_INTERCEPT_FUNCTION(gethostbyname2_r); #else #define INIT_GETHOSTBYNAME2_R #endif #if SANITIZER_INTERCEPT_GETSOCKOPT INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval, int *optlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getsockopt, sockfd, level, optname, optval, optlen); if (optlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, optlen, sizeof(*optlen)); // 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. int res = REAL(getsockopt)(sockfd, level, optname, optval, optlen); if (res == 0) if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen); return res; } #define INIT_GETSOCKOPT COMMON_INTERCEPT_FUNCTION(getsockopt); #else #define INIT_GETSOCKOPT #endif #if SANITIZER_INTERCEPT_ACCEPT INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, accept, fd, addr, addrlen); unsigned addrlen0 = 0; if (addrlen) { COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); addrlen0 = *addrlen; } int fd2 = REAL(accept)(fd, addr, addrlen); if (fd2 >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); } return fd2; } #define INIT_ACCEPT COMMON_INTERCEPT_FUNCTION(accept); #else #define INIT_ACCEPT #endif #if SANITIZER_INTERCEPT_ACCEPT4 INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, accept4, fd, addr, addrlen, f); unsigned addrlen0 = 0; if (addrlen) { COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); addrlen0 = *addrlen; } // 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. int fd2 = REAL(accept4)(fd, addr, addrlen, f); if (fd2 >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); } return fd2; } #define INIT_ACCEPT4 COMMON_INTERCEPT_FUNCTION(accept4); #else #define INIT_ACCEPT4 #endif #if SANITIZER_INTERCEPT_MODF INTERCEPTOR(double, modf, double x, double *iptr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, modf, x, iptr); // 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. double res = REAL(modf)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); } return res; } INTERCEPTOR(float, modff, float x, float *iptr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, modff, x, iptr); // 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. float res = REAL(modff)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); } return res; } INTERCEPTOR(long double, modfl, long double x, long double *iptr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, modfl, x, iptr); // 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. long double res = REAL(modfl)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); } return res; } #define INIT_MODF \ COMMON_INTERCEPT_FUNCTION(modf); \ COMMON_INTERCEPT_FUNCTION(modff); \ COMMON_INTERCEPT_FUNCTION(modfl); #else #define INIT_MODF #endif #if SANITIZER_INTERCEPT_RECVMSG static void write_msghdr(void *ctx, struct __sanitizer_msghdr *msg, SSIZE_T maxlen) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg, sizeof(*msg)); if (msg->msg_name && msg->msg_namelen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_name, msg->msg_namelen); if (msg->msg_iov && msg->msg_iovlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_iov, sizeof(*msg->msg_iov) * msg->msg_iovlen); write_iovec(ctx, msg->msg_iov, msg->msg_iovlen, maxlen); if (msg->msg_control && msg->msg_controllen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_control, msg->msg_controllen); } INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, recvmsg, fd, msg, flags); // 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(recvmsg)(fd, msg, flags); if (res >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); if (msg) { write_msghdr(ctx, msg, res); COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg); } } return res; } #define INIT_RECVMSG COMMON_INTERCEPT_FUNCTION(recvmsg); #else #define INIT_RECVMSG #endif #if SANITIZER_INTERCEPT_GETPEERNAME INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpeername, sockfd, addr, addrlen); unsigned addr_sz; if (addrlen) addr_sz = *addrlen; // 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. int res = REAL(getpeername)(sockfd, addr, addrlen); if (!res && addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen)); return res; } #define INIT_GETPEERNAME COMMON_INTERCEPT_FUNCTION(getpeername); #else #define INIT_GETPEERNAME #endif #if SANITIZER_INTERCEPT_SYSINFO INTERCEPTOR(int, sysinfo, void *info) { void *ctx; // 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. COMMON_INTERCEPTOR_ENTER(ctx, sysinfo, info); int res = REAL(sysinfo)(info); if (!res && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, struct_sysinfo_sz); return res; } #define INIT_SYSINFO COMMON_INTERCEPT_FUNCTION(sysinfo); #else #define INIT_SYSINFO #endif #if SANITIZER_INTERCEPT_READDIR +INTERCEPTOR(__sanitizer_dirent *, opendir, const char *path) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, opendir, path); + COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + __sanitizer_dirent *res = REAL(opendir)(path); + if (res != 0) + COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path); + return res; +} + INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp); // 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. __sanitizer_dirent *res = REAL(readdir)(dirp); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); return res; } INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry, __sanitizer_dirent **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir_r, dirp, entry, result); // 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. int res = REAL(readdir_r)(dirp, entry, result); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (*result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); } return res; } #define INIT_READDIR \ + COMMON_INTERCEPT_FUNCTION(opendir); \ COMMON_INTERCEPT_FUNCTION(readdir); \ COMMON_INTERCEPT_FUNCTION(readdir_r); #else #define INIT_READDIR #endif #if SANITIZER_INTERCEPT_READDIR64 INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp); // 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. __sanitizer_dirent64 *res = REAL(readdir64)(dirp); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); return res; } INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry, __sanitizer_dirent64 **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir64_r, dirp, entry, result); // 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. int res = REAL(readdir64_r)(dirp, entry, result); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (*result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); } return res; } #define INIT_READDIR64 \ COMMON_INTERCEPT_FUNCTION(readdir64); \ COMMON_INTERCEPT_FUNCTION(readdir64_r); #else #define INIT_READDIR64 #endif #if SANITIZER_INTERCEPT_PTRACE INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data); if (data) { if (request == ptrace_setregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_regs_struct_sz); else if (request == ptrace_setfpregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz); else if (request == ptrace_setfpxregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz); else if (request == ptrace_setsiginfo) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz); else if (request == ptrace_setregset) { __sanitizer_iovec *iov = (__sanitizer_iovec *)data; COMMON_INTERCEPTOR_READ_RANGE(ctx, iov->iov_base, iov->iov_len); } } // 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. uptr res = REAL(ptrace)(request, pid, addr, data); if (!res && data) { // Note that PEEK* requests assign different meaning to the return value. // This function does not handle them (nor does it need to). if (request == ptrace_getregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_regs_struct_sz); else if (request == ptrace_getfpregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz); else if (request == ptrace_getfpxregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz); else if (request == ptrace_getsiginfo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz); else if (request == ptrace_geteventmsg) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long)); else if (request == ptrace_getregset) { __sanitizer_iovec *iov = (__sanitizer_iovec *)data; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iov->iov_base, iov->iov_len); } } return res; } #define INIT_PTRACE COMMON_INTERCEPT_FUNCTION(ptrace); #else #define INIT_PTRACE #endif #if SANITIZER_INTERCEPT_SETLOCALE INTERCEPTOR(char *, setlocale, int category, char *locale) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setlocale, category, locale); if (locale) COMMON_INTERCEPTOR_READ_RANGE(ctx, locale, REAL(strlen)(locale) + 1); char *res = REAL(setlocale)(category, locale); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_SETLOCALE COMMON_INTERCEPT_FUNCTION(setlocale); #else #define INIT_SETLOCALE #endif #if SANITIZER_INTERCEPT_GETCWD INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size); // 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. char *res = REAL(getcwd)(buf, size); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_GETCWD COMMON_INTERCEPT_FUNCTION(getcwd); #else #define INIT_GETCWD #endif #if SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME INTERCEPTOR(char *, get_current_dir_name, int fake) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name, fake); // 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. char *res = REAL(get_current_dir_name)(fake); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_GET_CURRENT_DIR_NAME \ COMMON_INTERCEPT_FUNCTION(get_current_dir_name); #else #define INIT_GET_CURRENT_DIR_NAME #endif #if SANITIZER_INTERCEPT_STRTOIMAX INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, 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)); return res; } INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, 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)); return res; } #define INIT_STRTOIMAX \ COMMON_INTERCEPT_FUNCTION(strtoimax); \ COMMON_INTERCEPT_FUNCTION(strtoumax); #else #define INIT_STRTOIMAX #endif #if SANITIZER_INTERCEPT_MBSTOWCS INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mbstowcs, dest, src, len); // 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. SIZE_T res = REAL(mbstowcs)(dest, src, len); if (res != (SIZE_T) - 1 && dest) { SIZE_T write_cnt = res + (res < len); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); } return res; } INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mbsrtowcs, dest, src, len, ps); if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // 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. SIZE_T res = REAL(mbsrtowcs)(dest, src, len, ps); if (res != (SIZE_T)(-1) && dest && src) { // This function, and several others, may or may not write the terminating // \0 character. They write it iff they clear *src. SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); } return res; } #define INIT_MBSTOWCS \ COMMON_INTERCEPT_FUNCTION(mbstowcs); \ COMMON_INTERCEPT_FUNCTION(mbsrtowcs); #else #define INIT_MBSTOWCS #endif #if SANITIZER_INTERCEPT_MBSNRTOWCS INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mbsnrtowcs, dest, src, nms, len, ps); if (src) { COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); } if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // 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. SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps); if (res != (SIZE_T)(-1) && dest && src) { SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); } return res; } #define INIT_MBSNRTOWCS COMMON_INTERCEPT_FUNCTION(mbsnrtowcs); #else #define INIT_MBSNRTOWCS #endif #if SANITIZER_INTERCEPT_WCSTOMBS INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcstombs, dest, src, len); // 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. SIZE_T res = REAL(wcstombs)(dest, src, len); if (res != (SIZE_T) - 1 && dest) { SIZE_T write_cnt = res + (res < len); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcsrtombs, dest, src, len, ps); if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // 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. SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps); if (res != (SIZE_T) - 1 && dest && src) { SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } #define INIT_WCSTOMBS \ COMMON_INTERCEPT_FUNCTION(wcstombs); \ COMMON_INTERCEPT_FUNCTION(wcsrtombs); #else #define INIT_WCSTOMBS #endif #if SANITIZER_INTERCEPT_WCSNRTOMBS INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcsnrtombs, dest, src, nms, len, ps); if (src) { COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); } if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // 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. SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps); if (res != (SIZE_T) - 1 && dest && src) { SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } #define INIT_WCSNRTOMBS COMMON_INTERCEPT_FUNCTION(wcsnrtombs); #else #define INIT_WCSNRTOMBS #endif #if SANITIZER_INTERCEPT_TCGETATTR INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tcgetattr, fd, termios_p); // 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. int res = REAL(tcgetattr)(fd, termios_p); if (!res && termios_p) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, termios_p, struct_termios_sz); return res; } #define INIT_TCGETATTR COMMON_INTERCEPT_FUNCTION(tcgetattr); #else #define INIT_TCGETATTR #endif #if SANITIZER_INTERCEPT_REALPATH INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, realpath, path, resolved_path); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // Workaround a bug in glibc where dlsym(RTLD_NEXT, ...) returns the oldest // version of a versioned symbol. For realpath(), this gives us something // (called __old_realpath) that does not handle NULL in the second argument. // Handle it as part of the interceptor. char *allocated_path = 0; if (!resolved_path) allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1); char *res = REAL(realpath)(path, resolved_path); if (allocated_path && !res) WRAP(free)(allocated_path); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_REALPATH COMMON_INTERCEPT_FUNCTION(realpath); #else #define INIT_REALPATH #endif #if SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME INTERCEPTOR(char *, canonicalize_file_name, const char *path) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, canonicalize_file_name, path); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); char *res = REAL(canonicalize_file_name)(path); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_CANONICALIZE_FILE_NAME \ COMMON_INTERCEPT_FUNCTION(canonicalize_file_name); #else #define INIT_CANONICALIZE_FILE_NAME #endif #if SANITIZER_INTERCEPT_CONFSTR INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, confstr, name, buf, len); // 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. SIZE_T res = REAL(confstr)(name, buf, len); if (buf && res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len); return res; } #define INIT_CONFSTR COMMON_INTERCEPT_FUNCTION(confstr); #else #define INIT_CONFSTR #endif #if SANITIZER_INTERCEPT_SCHED_GETAFFINITY INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask); // 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. int res = REAL(sched_getaffinity)(pid, cpusetsize, mask); if (mask && !res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize); return res; } #define INIT_SCHED_GETAFFINITY COMMON_INTERCEPT_FUNCTION(sched_getaffinity); #else #define INIT_SCHED_GETAFFINITY #endif +#if SANITIZER_INTERCEPT_SCHED_GETPARAM +INTERCEPTOR(int, sched_getparam, int pid, void *param) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sched_getparam, pid, param); + int res = REAL(sched_getparam)(pid, param); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, struct_sched_param_sz); + return res; +} +#define INIT_SCHED_GETPARAM COMMON_INTERCEPT_FUNCTION(sched_getparam); +#else +#define INIT_SCHED_GETPARAM +#endif + #if SANITIZER_INTERCEPT_STRERROR INTERCEPTOR(char *, strerror, int errnum) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strerror, errnum); char *res = REAL(strerror)(errnum); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; } #define INIT_STRERROR COMMON_INTERCEPT_FUNCTION(strerror); #else #define INIT_STRERROR #endif #if SANITIZER_INTERCEPT_STRERROR_R INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen); // 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. char *res = REAL(strerror_r)(errnum, buf, buflen); // There are 2 versions of strerror_r: // * POSIX version returns 0 on success, negative error code on failure, // writes message to buf. // * GNU version returns message pointer, which points to either buf or some // static storage. SIZE_T posix_res = (SIZE_T)res; if (posix_res < 1024 || posix_res > (SIZE_T) - 1024) { // POSIX version. Spec is not clear on whether buf is NULL-terminated. // At least on OSX, buf contents are valid even when the call fails. SIZE_T sz = internal_strnlen(buf, buflen); if (sz < buflen) ++sz; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); } else { // GNU version. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } #define INIT_STRERROR_R COMMON_INTERCEPT_FUNCTION(strerror_r); #else #define INIT_STRERROR_R #endif #if SANITIZER_INTERCEPT_XPG_STRERROR_R INTERCEPTOR(int, __xpg_strerror_r, int errnum, char *buf, SIZE_T buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __xpg_strerror_r, errnum, buf, buflen); // 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. int res = REAL(__xpg_strerror_r)(errnum, buf, buflen); // This version always returns a null-terminated string. if (buf && buflen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, REAL(strlen)(buf) + 1); return res; } #define INIT_XPG_STRERROR_R COMMON_INTERCEPT_FUNCTION(__xpg_strerror_r); #else #define INIT_XPG_STRERROR_R #endif #if SANITIZER_INTERCEPT_SCANDIR typedef int (*scandir_filter_f)(const struct __sanitizer_dirent *); typedef int (*scandir_compar_f)(const struct __sanitizer_dirent **, const struct __sanitizer_dirent **); static THREADLOCAL scandir_filter_f scandir_filter; static THREADLOCAL scandir_compar_f scandir_compar; static int wrapped_scandir_filter(const struct __sanitizer_dirent *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen); return scandir_filter(dir); } static int wrapped_scandir_compar(const struct __sanitizer_dirent **a, const struct __sanitizer_dirent **b) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen); COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen); return scandir_compar(a, b); } INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist, scandir_filter_f filter, scandir_compar_f compar) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, scandir, dirp, namelist, filter, compar); if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1); scandir_filter = filter; scandir_compar = compar; // 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. int res = REAL(scandir)(dirp, namelist, filter ? wrapped_scandir_filter : 0, compar ? wrapped_scandir_compar : 0); scandir_filter = 0; scandir_compar = 0; if (namelist && res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); for (int i = 0; i < res; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], (*namelist)[i]->d_reclen); } return res; } #define INIT_SCANDIR COMMON_INTERCEPT_FUNCTION(scandir); #else #define INIT_SCANDIR #endif #if SANITIZER_INTERCEPT_SCANDIR64 typedef int (*scandir64_filter_f)(const struct __sanitizer_dirent64 *); typedef int (*scandir64_compar_f)(const struct __sanitizer_dirent64 **, const struct __sanitizer_dirent64 **); static THREADLOCAL scandir64_filter_f scandir64_filter; static THREADLOCAL scandir64_compar_f scandir64_compar; static int wrapped_scandir64_filter(const struct __sanitizer_dirent64 *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen); return scandir64_filter(dir); } static int wrapped_scandir64_compar(const struct __sanitizer_dirent64 **a, const struct __sanitizer_dirent64 **b) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen); COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen); return scandir64_compar(a, b); } INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist, scandir64_filter_f filter, scandir64_compar_f compar) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, scandir64, dirp, namelist, filter, compar); if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1); scandir64_filter = filter; scandir64_compar = compar; // 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. int res = REAL(scandir64)(dirp, namelist, filter ? wrapped_scandir64_filter : 0, compar ? wrapped_scandir64_compar : 0); scandir64_filter = 0; scandir64_compar = 0; if (namelist && res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); for (int i = 0; i < res; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], (*namelist)[i]->d_reclen); } return res; } #define INIT_SCANDIR64 COMMON_INTERCEPT_FUNCTION(scandir64); #else #define INIT_SCANDIR64 #endif #if SANITIZER_INTERCEPT_GETGROUPS INTERCEPTOR(int, getgroups, int size, u32 *lst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst); // 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. int res = REAL(getgroups)(size, lst); if (res && lst) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); return res; } #define INIT_GETGROUPS COMMON_INTERCEPT_FUNCTION(getgroups); #else #define INIT_GETGROUPS #endif #if SANITIZER_INTERCEPT_POLL static void read_pollfd(void *ctx, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds) { for (unsigned i = 0; i < nfds; ++i) { COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].fd, sizeof(fds[i].fd)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].events, sizeof(fds[i].events)); } } static void write_pollfd(void *ctx, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds) { for (unsigned i = 0; i < nfds; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &fds[i].revents, sizeof(fds[i].revents)); } INTERCEPTOR(int, poll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, int timeout) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, poll, fds, nfds, timeout); if (fds && nfds) read_pollfd(ctx, fds, nfds); int res = COMMON_INTERCEPTOR_BLOCK_REAL(poll)(fds, nfds, timeout); if (fds && nfds) write_pollfd(ctx, fds, nfds); return res; } #define INIT_POLL COMMON_INTERCEPT_FUNCTION(poll); #else #define INIT_POLL #endif #if SANITIZER_INTERCEPT_PPOLL INTERCEPTOR(int, ppoll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, void *timeout_ts, __sanitizer_sigset_t *sigmask) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ppoll, fds, nfds, timeout_ts, sigmask); if (fds && nfds) read_pollfd(ctx, fds, nfds); if (timeout_ts) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout_ts, struct_timespec_sz); // FIXME: read sigmask when all of sigemptyset, etc are intercepted. int res = COMMON_INTERCEPTOR_BLOCK_REAL(ppoll)(fds, nfds, timeout_ts, sigmask); if (fds && nfds) write_pollfd(ctx, fds, nfds); return res; } #define INIT_PPOLL COMMON_INTERCEPT_FUNCTION(ppoll); #else #define INIT_PPOLL #endif #if SANITIZER_INTERCEPT_WORDEXP INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wordexp, s, p, flags); if (s) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 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. int res = REAL(wordexp)(s, p, flags); if (!res && p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); if (p->we_wordc) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->we_wordv, sizeof(*p->we_wordv) * p->we_wordc); for (uptr i = 0; i < p->we_wordc; ++i) { char *w = p->we_wordv[i]; if (w) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, w, REAL(strlen)(w) + 1); } } return res; } #define INIT_WORDEXP COMMON_INTERCEPT_FUNCTION(wordexp); #else #define INIT_WORDEXP #endif #if SANITIZER_INTERCEPT_SIGWAIT INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigwait, set, sig); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // 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. int res = REAL(sigwait)(set, sig); if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig)); return res; } #define INIT_SIGWAIT COMMON_INTERCEPT_FUNCTION(sigwait); #else #define INIT_SIGWAIT #endif #if SANITIZER_INTERCEPT_SIGWAITINFO INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigwaitinfo, set, info); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // 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. int res = REAL(sigwaitinfo)(set, info); if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; } #define INIT_SIGWAITINFO COMMON_INTERCEPT_FUNCTION(sigwaitinfo); #else #define INIT_SIGWAITINFO #endif #if SANITIZER_INTERCEPT_SIGTIMEDWAIT INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info, void *timeout) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigtimedwait, set, info, timeout); if (timeout) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // 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. int res = REAL(sigtimedwait)(set, info, timeout); if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; } #define INIT_SIGTIMEDWAIT COMMON_INTERCEPT_FUNCTION(sigtimedwait); #else #define INIT_SIGTIMEDWAIT #endif #if SANITIZER_INTERCEPT_SIGSETOPS INTERCEPTOR(int, sigemptyset, __sanitizer_sigset_t *set) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigemptyset, set); // 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. int res = REAL(sigemptyset)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } INTERCEPTOR(int, sigfillset, __sanitizer_sigset_t *set) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigfillset, set); // 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. int res = REAL(sigfillset)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } #define INIT_SIGSETOPS \ COMMON_INTERCEPT_FUNCTION(sigemptyset); \ COMMON_INTERCEPT_FUNCTION(sigfillset); #else #define INIT_SIGSETOPS #endif #if SANITIZER_INTERCEPT_SIGPENDING INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigpending, set); // 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. int res = REAL(sigpending)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } #define INIT_SIGPENDING COMMON_INTERCEPT_FUNCTION(sigpending); #else #define INIT_SIGPENDING #endif #if SANITIZER_INTERCEPT_SIGPROCMASK INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // 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. int res = REAL(sigprocmask)(how, set, oldset); if (!res && oldset) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset)); return res; } #define INIT_SIGPROCMASK COMMON_INTERCEPT_FUNCTION(sigprocmask); #else #define INIT_SIGPROCMASK #endif #if SANITIZER_INTERCEPT_BACKTRACE INTERCEPTOR(int, backtrace, void **buffer, int size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size); // 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. int res = REAL(backtrace)(buffer, size); if (res && buffer) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer)); return res; } INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, backtrace_symbols, buffer, size); if (buffer && size) COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer)); // 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. char **res = REAL(backtrace_symbols)(buffer, size); if (res && size) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res)); for (int i = 0; i < size; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res[i], REAL(strlen(res[i])) + 1); } return res; } #define INIT_BACKTRACE \ COMMON_INTERCEPT_FUNCTION(backtrace); \ COMMON_INTERCEPT_FUNCTION(backtrace_symbols); #else #define INIT_BACKTRACE #endif #if SANITIZER_INTERCEPT__EXIT INTERCEPTOR(void, _exit, int status) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _exit, status); int status1 = COMMON_INTERCEPTOR_ON_EXIT(ctx); if (status == 0) status = status1; REAL(_exit)(status); } #define INIT__EXIT COMMON_INTERCEPT_FUNCTION(_exit); #else #define INIT__EXIT #endif #if SANITIZER_INTERCEPT_PHTREAD_MUTEX INTERCEPTOR(int, pthread_mutex_lock, void *m) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_lock, m); int res = REAL(pthread_mutex_lock)(m); if (res == errno_EOWNERDEAD) COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m); if (res == 0 || res == errno_EOWNERDEAD) COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m); return res; } INTERCEPTOR(int, pthread_mutex_unlock, void *m) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_unlock, m); COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); return REAL(pthread_mutex_unlock)(m); } #define INIT_PTHREAD_MUTEX_LOCK COMMON_INTERCEPT_FUNCTION(pthread_mutex_lock) #define INIT_PTHREAD_MUTEX_UNLOCK \ COMMON_INTERCEPT_FUNCTION(pthread_mutex_unlock) #else #define INIT_PTHREAD_MUTEX_LOCK #define INIT_PTHREAD_MUTEX_UNLOCK #endif #if SANITIZER_INTERCEPT_GETMNTENT || SANITIZER_INTERCEPT_GETMNTENT_R static void write_mntent(void *ctx, __sanitizer_mntent *mnt) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt, sizeof(*mnt)); if (mnt->mnt_fsname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_fsname, REAL(strlen)(mnt->mnt_fsname) + 1); if (mnt->mnt_dir) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_dir, REAL(strlen)(mnt->mnt_dir) + 1); if (mnt->mnt_type) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_type, REAL(strlen)(mnt->mnt_type) + 1); if (mnt->mnt_opts) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_opts, REAL(strlen)(mnt->mnt_opts) + 1); } #endif #if SANITIZER_INTERCEPT_GETMNTENT INTERCEPTOR(__sanitizer_mntent *, getmntent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getmntent, fp); __sanitizer_mntent *res = REAL(getmntent)(fp); if (res) write_mntent(ctx, res); return res; } #define INIT_GETMNTENT COMMON_INTERCEPT_FUNCTION(getmntent); #else #define INIT_GETMNTENT #endif #if SANITIZER_INTERCEPT_GETMNTENT_R INTERCEPTOR(__sanitizer_mntent *, getmntent_r, void *fp, __sanitizer_mntent *mntbuf, char *buf, int buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getmntent_r, fp, mntbuf, buf, buflen); __sanitizer_mntent *res = REAL(getmntent_r)(fp, mntbuf, buf, buflen); if (res) write_mntent(ctx, res); return res; } #define INIT_GETMNTENT_R COMMON_INTERCEPT_FUNCTION(getmntent_r); #else #define INIT_GETMNTENT_R #endif #if SANITIZER_INTERCEPT_STATFS INTERCEPTOR(int, statfs, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statfs, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 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. int res = REAL(statfs)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); return res; } INTERCEPTOR(int, fstatfs, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatfs, fd, buf); // 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. int res = REAL(fstatfs)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); return res; } #define INIT_STATFS \ COMMON_INTERCEPT_FUNCTION(statfs); \ COMMON_INTERCEPT_FUNCTION(fstatfs); #else #define INIT_STATFS #endif #if SANITIZER_INTERCEPT_STATFS64 INTERCEPTOR(int, statfs64, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statfs64, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 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. int res = REAL(statfs64)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); return res; } INTERCEPTOR(int, fstatfs64, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatfs64, fd, buf); // 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. int res = REAL(fstatfs64)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); return res; } #define INIT_STATFS64 \ COMMON_INTERCEPT_FUNCTION(statfs64); \ COMMON_INTERCEPT_FUNCTION(fstatfs64); #else #define INIT_STATFS64 #endif #if SANITIZER_INTERCEPT_STATVFS INTERCEPTOR(int, statvfs, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statvfs, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 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. int res = REAL(statvfs)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); return res; } INTERCEPTOR(int, fstatvfs, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf); // 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. int res = REAL(fstatvfs)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); return res; } #define INIT_STATVFS \ COMMON_INTERCEPT_FUNCTION(statvfs); \ COMMON_INTERCEPT_FUNCTION(fstatvfs); #else #define INIT_STATVFS #endif #if SANITIZER_INTERCEPT_STATVFS64 INTERCEPTOR(int, statvfs64, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statvfs64, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 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. int res = REAL(statvfs64)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); return res; } INTERCEPTOR(int, fstatvfs64, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs64, fd, buf); // 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. int res = REAL(fstatvfs64)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); return res; } #define INIT_STATVFS64 \ COMMON_INTERCEPT_FUNCTION(statvfs64); \ COMMON_INTERCEPT_FUNCTION(fstatvfs64); #else #define INIT_STATVFS64 #endif #if SANITIZER_INTERCEPT_INITGROUPS INTERCEPTOR(int, initgroups, char *user, u32 group) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, initgroups, user, group); if (user) COMMON_INTERCEPTOR_READ_RANGE(ctx, user, REAL(strlen)(user) + 1); int res = REAL(initgroups)(user, group); return res; } #define INIT_INITGROUPS COMMON_INTERCEPT_FUNCTION(initgroups); #else #define INIT_INITGROUPS #endif #if SANITIZER_INTERCEPT_ETHER_NTOA_ATON INTERCEPTOR(char *, ether_ntoa, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa, addr); if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); char *res = REAL(ether_ntoa)(addr); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(__sanitizer_ether_addr *, ether_aton, char *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_aton, buf); if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); __sanitizer_ether_addr *res = REAL(ether_aton)(buf); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, sizeof(*res)); return res; } #define INIT_ETHER_NTOA_ATON \ COMMON_INTERCEPT_FUNCTION(ether_ntoa); \ COMMON_INTERCEPT_FUNCTION(ether_aton); #else #define INIT_ETHER_NTOA_ATON #endif #if SANITIZER_INTERCEPT_ETHER_HOST INTERCEPTOR(int, ether_ntohost, char *hostname, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_ntohost, hostname, addr); if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); // 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. int res = REAL(ether_ntohost)(hostname, addr); if (!res && hostname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); return res; } INTERCEPTOR(int, ether_hostton, char *hostname, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_hostton, hostname, addr); if (hostname) COMMON_INTERCEPTOR_READ_RANGE(ctx, hostname, REAL(strlen)(hostname) + 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. int res = REAL(ether_hostton)(hostname, addr); if (!res && addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); return res; } INTERCEPTOR(int, ether_line, char *line, __sanitizer_ether_addr *addr, char *hostname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_line, line, addr, hostname); if (line) COMMON_INTERCEPTOR_READ_RANGE(ctx, line, REAL(strlen)(line) + 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. int res = REAL(ether_line)(line, addr, hostname); if (!res) { if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); if (hostname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); } return res; } #define INIT_ETHER_HOST \ COMMON_INTERCEPT_FUNCTION(ether_ntohost); \ COMMON_INTERCEPT_FUNCTION(ether_hostton); \ COMMON_INTERCEPT_FUNCTION(ether_line); #else #define INIT_ETHER_HOST #endif #if SANITIZER_INTERCEPT_ETHER_R INTERCEPTOR(char *, ether_ntoa_r, __sanitizer_ether_addr *addr, char *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa_r, addr, buf); if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); // 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. char *res = REAL(ether_ntoa_r)(addr, buf); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(__sanitizer_ether_addr *, ether_aton_r, char *buf, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_aton_r, buf, addr); if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 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. __sanitizer_ether_addr *res = REAL(ether_aton_r)(buf, addr); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(*res)); return res; } #define INIT_ETHER_R \ COMMON_INTERCEPT_FUNCTION(ether_ntoa_r); \ COMMON_INTERCEPT_FUNCTION(ether_aton_r); #else #define INIT_ETHER_R #endif #if SANITIZER_INTERCEPT_SHMCTL INTERCEPTOR(int, shmctl, int shmid, int cmd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, shmctl, shmid, cmd, buf); // 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. int res = REAL(shmctl)(shmid, cmd, buf); if (res >= 0) { unsigned sz = 0; if (cmd == shmctl_ipc_stat || cmd == shmctl_shm_stat) sz = sizeof(__sanitizer_shmid_ds); else if (cmd == shmctl_ipc_info) sz = struct_shminfo_sz; else if (cmd == shmctl_shm_info) sz = struct_shm_info_sz; if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); } return res; } #define INIT_SHMCTL COMMON_INTERCEPT_FUNCTION(shmctl); #else #define INIT_SHMCTL #endif #if SANITIZER_INTERCEPT_RANDOM_R INTERCEPTOR(int, random_r, void *buf, u32 *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, random_r, buf, result); // 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. int res = REAL(random_r)(buf, result); if (!res && result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } #define INIT_RANDOM_R COMMON_INTERCEPT_FUNCTION(random_r); #else #define INIT_RANDOM_R #endif // FIXME: under ASan the REAL() call below may write to freed memory and corrupt // its metadata. See // https://code.google.com/p/address-sanitizer/issues/detail?id=321. #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED || \ SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GET #define INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(fn, sz) \ INTERCEPTOR(int, fn, void *attr, void *r) { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, fn, attr, r); \ int res = REAL(fn)(attr, r); \ if (!res && r) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, r, sz); \ return res; \ } #define INTERCEPTOR_PTHREAD_ATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_attr_get##what, sz) #define INTERCEPTOR_PTHREAD_MUTEXATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_mutexattr_get##what, sz) #define INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_rwlockattr_get##what, sz) #define INTERCEPTOR_PTHREAD_CONDATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_condattr_get##what, sz) #define INTERCEPTOR_PTHREAD_BARRIERATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_barrierattr_get##what, sz) #endif #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET INTERCEPTOR_PTHREAD_ATTR_GET(detachstate, sizeof(int)) INTERCEPTOR_PTHREAD_ATTR_GET(guardsize, sizeof(SIZE_T)) INTERCEPTOR_PTHREAD_ATTR_GET(schedparam, struct_sched_param_sz) INTERCEPTOR_PTHREAD_ATTR_GET(schedpolicy, sizeof(int)) INTERCEPTOR_PTHREAD_ATTR_GET(scope, sizeof(int)) INTERCEPTOR_PTHREAD_ATTR_GET(stacksize, sizeof(SIZE_T)) INTERCEPTOR(int, pthread_attr_getstack, void *attr, void **addr, SIZE_T *size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getstack, attr, addr, size); // 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. int res = REAL(pthread_attr_getstack)(attr, addr, size); if (!res) { if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); if (size) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, size, sizeof(*size)); } return res; } // We may need to call the real pthread_attr_getstack from the run-time // in sanitizer_common, but we don't want to include the interception headers // there. So, just define this function here. namespace __sanitizer { extern "C" { int real_pthread_attr_getstack(void *attr, void **addr, SIZE_T *size) { return REAL(pthread_attr_getstack)(attr, addr, size); } } // extern "C" } // namespace __sanitizer #define INIT_PTHREAD_ATTR_GET \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getdetachstate); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getguardsize); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedparam); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedpolicy); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getscope); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getstacksize); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getstack); #else #define INIT_PTHREAD_ATTR_GET #endif #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED INTERCEPTOR_PTHREAD_ATTR_GET(inheritsched, sizeof(int)) #define INIT_PTHREAD_ATTR_GETINHERITSCHED \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getinheritsched); #else #define INIT_PTHREAD_ATTR_GETINHERITSCHED #endif #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP INTERCEPTOR(int, pthread_attr_getaffinity_np, void *attr, SIZE_T cpusetsize, void *cpuset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getaffinity_np, attr, cpusetsize, cpuset); // 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. int res = REAL(pthread_attr_getaffinity_np)(attr, cpusetsize, cpuset); if (!res && cpusetsize && cpuset) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cpuset, cpusetsize); return res; } #define INIT_PTHREAD_ATTR_GETAFFINITY_NP \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getaffinity_np); #else #define INIT_PTHREAD_ATTR_GETAFFINITY_NP #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED INTERCEPTOR_PTHREAD_MUTEXATTR_GET(pshared, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getpshared); #else #define INIT_PTHREAD_MUTEXATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE INTERCEPTOR_PTHREAD_MUTEXATTR_GET(type, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETTYPE \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_gettype); #else #define INIT_PTHREAD_MUTEXATTR_GETTYPE #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL INTERCEPTOR_PTHREAD_MUTEXATTR_GET(protocol, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETPROTOCOL \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getprotocol); #else #define INIT_PTHREAD_MUTEXATTR_GETPROTOCOL #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING INTERCEPTOR_PTHREAD_MUTEXATTR_GET(prioceiling, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getprioceiling); #else #define INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST INTERCEPTOR_PTHREAD_MUTEXATTR_GET(robust, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETROBUST \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getrobust); #else #define INIT_PTHREAD_MUTEXATTR_GETROBUST #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP INTERCEPTOR_PTHREAD_MUTEXATTR_GET(robust_np, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETROBUST_NP \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getrobust_np); #else #define INIT_PTHREAD_MUTEXATTR_GETROBUST_NP #endif #if SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(pshared, sizeof(int)) #define INIT_PTHREAD_RWLOCKATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_rwlockattr_getpshared); #else #define INIT_PTHREAD_RWLOCKATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(kind_np, sizeof(int)) #define INIT_PTHREAD_RWLOCKATTR_GETKIND_NP \ COMMON_INTERCEPT_FUNCTION(pthread_rwlockattr_getkind_np); #else #define INIT_PTHREAD_RWLOCKATTR_GETKIND_NP #endif #if SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED INTERCEPTOR_PTHREAD_CONDATTR_GET(pshared, sizeof(int)) #define INIT_PTHREAD_CONDATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_condattr_getpshared); #else #define INIT_PTHREAD_CONDATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK INTERCEPTOR_PTHREAD_CONDATTR_GET(clock, sizeof(int)) #define INIT_PTHREAD_CONDATTR_GETCLOCK \ COMMON_INTERCEPT_FUNCTION(pthread_condattr_getclock); #else #define INIT_PTHREAD_CONDATTR_GETCLOCK #endif #if SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED INTERCEPTOR_PTHREAD_BARRIERATTR_GET(pshared, sizeof(int)) // !mac !android #define INIT_PTHREAD_BARRIERATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_barrierattr_getpshared); #else #define INIT_PTHREAD_BARRIERATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_TMPNAM INTERCEPTOR(char *, tmpnam, char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tmpnam, s); char *res = REAL(tmpnam)(s); if (res) { if (s) // 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. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); else COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); } return res; } #define INIT_TMPNAM COMMON_INTERCEPT_FUNCTION(tmpnam); #else #define INIT_TMPNAM #endif #if SANITIZER_INTERCEPT_TMPNAM_R INTERCEPTOR(char *, tmpnam_r, char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tmpnam_r, s); // 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. char *res = REAL(tmpnam_r)(s); if (res && s) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); return res; } #define INIT_TMPNAM_R COMMON_INTERCEPT_FUNCTION(tmpnam_r); #else #define INIT_TMPNAM_R #endif #if SANITIZER_INTERCEPT_TEMPNAM INTERCEPTOR(char *, tempnam, char *dir, char *pfx) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tempnam, dir, pfx); if (dir) COMMON_INTERCEPTOR_READ_RANGE(ctx, dir, REAL(strlen)(dir) + 1); if (pfx) COMMON_INTERCEPTOR_READ_RANGE(ctx, pfx, REAL(strlen)(pfx) + 1); char *res = REAL(tempnam)(dir, pfx); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; } #define INIT_TEMPNAM COMMON_INTERCEPT_FUNCTION(tempnam); #else #define INIT_TEMPNAM #endif #if SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name); COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name); return REAL(pthread_setname_np)(thread, name); } #define INIT_PTHREAD_SETNAME_NP COMMON_INTERCEPT_FUNCTION(pthread_setname_np); #else #define INIT_PTHREAD_SETNAME_NP #endif #if SANITIZER_INTERCEPT_SINCOS INTERCEPTOR(void, sincos, double x, double *sin, double *cos) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sincos, x, sin, cos); // 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. REAL(sincos)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); } INTERCEPTOR(void, sincosf, float x, float *sin, float *cos) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sincosf, x, sin, cos); // 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. REAL(sincosf)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); } INTERCEPTOR(void, sincosl, long double x, long double *sin, long double *cos) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sincosl, x, sin, cos); // 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. REAL(sincosl)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); } #define INIT_SINCOS \ COMMON_INTERCEPT_FUNCTION(sincos); \ COMMON_INTERCEPT_FUNCTION(sincosf); \ COMMON_INTERCEPT_FUNCTION(sincosl); #else #define INIT_SINCOS #endif #if SANITIZER_INTERCEPT_REMQUO INTERCEPTOR(double, remquo, double x, double y, int *quo) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, remquo, x, y, quo); // 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. double res = REAL(remquo)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; } INTERCEPTOR(float, remquof, float x, float y, int *quo) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, remquof, x, y, quo); // 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. float res = REAL(remquof)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; } INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo); // 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. long double res = REAL(remquol)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; } #define INIT_REMQUO \ COMMON_INTERCEPT_FUNCTION(remquo); \ COMMON_INTERCEPT_FUNCTION(remquof); \ COMMON_INTERCEPT_FUNCTION(remquol); #else #define INIT_REMQUO #endif #if SANITIZER_INTERCEPT_LGAMMA extern int signgam; INTERCEPTOR(double, lgamma, double x) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgamma, x); double res = REAL(lgamma)(x); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); return res; } INTERCEPTOR(float, lgammaf, float x) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammaf, x); float res = REAL(lgammaf)(x); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); return res; } INTERCEPTOR(long double, lgammal, long double x) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammal, x); long double res = REAL(lgammal)(x); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); return res; } #define INIT_LGAMMA \ COMMON_INTERCEPT_FUNCTION(lgamma); \ COMMON_INTERCEPT_FUNCTION(lgammaf); \ COMMON_INTERCEPT_FUNCTION(lgammal); #else #define INIT_LGAMMA #endif #if SANITIZER_INTERCEPT_LGAMMA_R INTERCEPTOR(double, lgamma_r, double x, int *signp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgamma_r, x, signp); // 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. double res = REAL(lgamma_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; } INTERCEPTOR(float, lgammaf_r, float x, int *signp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammaf_r, x, signp); // 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. float res = REAL(lgammaf_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; } #define INIT_LGAMMA_R \ COMMON_INTERCEPT_FUNCTION(lgamma_r); \ COMMON_INTERCEPT_FUNCTION(lgammaf_r); #else #define INIT_LGAMMA_R #endif #if SANITIZER_INTERCEPT_LGAMMAL_R INTERCEPTOR(long double, lgammal_r, long double x, int *signp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammal_r, x, signp); // 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. long double res = REAL(lgammal_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; } #define INIT_LGAMMAL_R COMMON_INTERCEPT_FUNCTION(lgammal_r); #else #define INIT_LGAMMAL_R #endif #if SANITIZER_INTERCEPT_DRAND48_R INTERCEPTOR(int, drand48_r, void *buffer, double *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, drand48_r, buffer, result); // 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. int res = REAL(drand48_r)(buffer, result); if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, lrand48_r, void *buffer, long *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lrand48_r, buffer, result); // 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. int res = REAL(lrand48_r)(buffer, result); if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } #define INIT_DRAND48_R \ COMMON_INTERCEPT_FUNCTION(drand48_r); \ COMMON_INTERCEPT_FUNCTION(lrand48_r); #else #define INIT_DRAND48_R #endif #if SANITIZER_INTERCEPT_RAND_R INTERCEPTOR(int, rand_r, unsigned *seedp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, rand_r, seedp); COMMON_INTERCEPTOR_READ_RANGE(ctx, seedp, sizeof(*seedp)); return REAL(rand_r)(seedp); } #define INIT_RAND_R COMMON_INTERCEPT_FUNCTION(rand_r); #else #define INIT_RAND_R #endif #if SANITIZER_INTERCEPT_GETLINE INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getline, lineptr, n, 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(getline)(lineptr, n, 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; } 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); } return res; } INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim, void *stream) { return __getdelim(lineptr, n, delim, stream); } #define INIT_GETLINE \ COMMON_INTERCEPT_FUNCTION(getline); \ COMMON_INTERCEPT_FUNCTION(__getdelim); \ COMMON_INTERCEPT_FUNCTION(getdelim); #else #define INIT_GETLINE #endif #if SANITIZER_INTERCEPT_ICONV INTERCEPTOR(SIZE_T, iconv, void *cd, char **inbuf, SIZE_T *inbytesleft, char **outbuf, SIZE_T *outbytesleft) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, iconv, cd, inbuf, inbytesleft, outbuf, outbytesleft); if (inbytesleft) COMMON_INTERCEPTOR_READ_RANGE(ctx, inbytesleft, sizeof(*inbytesleft)); if (inbuf && inbytesleft) COMMON_INTERCEPTOR_READ_RANGE(ctx, *inbuf, *inbytesleft); if (outbytesleft) COMMON_INTERCEPTOR_READ_RANGE(ctx, outbytesleft, sizeof(*outbytesleft)); void *outbuf_orig = outbuf ? *outbuf : 0; // 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. SIZE_T res = REAL(iconv)(cd, inbuf, inbytesleft, outbuf, outbytesleft); if (res != (SIZE_T) - 1 && outbuf && *outbuf > outbuf_orig) { SIZE_T sz = (char *)*outbuf - (char *)outbuf_orig; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, outbuf_orig, sz); } return res; } #define INIT_ICONV COMMON_INTERCEPT_FUNCTION(iconv); #else #define INIT_ICONV #endif #if SANITIZER_INTERCEPT_TIMES INTERCEPTOR(__sanitizer_clock_t, times, void *tms) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, times, tms); // 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. __sanitizer_clock_t res = REAL(times)(tms); if (res != (__sanitizer_clock_t)-1 && tms) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tms, struct_tms_sz); return res; } #define INIT_TIMES COMMON_INTERCEPT_FUNCTION(times); #else #define INIT_TIMES #endif #if SANITIZER_INTERCEPT_TLS_GET_ADDR #define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_addr) +// If you see any crashes around this functions, there are 2 known issues with +// it: 1. __tls_get_addr can be called with mis-aligned stack due to: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066 +// 2. It can be called recursively if sanitizer code uses __tls_get_addr +// to access thread local variables (it should not happen normally, +// because sanitizers use initial-exec tls model). 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); if (dtv) { // New DTLS block has been allocated. COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size); } return res; } #else #define INIT_TLS_GET_ADDR #endif #if SANITIZER_INTERCEPT_LISTXATTR INTERCEPTOR(SSIZE_T, listxattr, const char *path, char *list, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, listxattr, path, list, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 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. SSIZE_T res = REAL(listxattr)(path, list, size); // Here and below, size == 0 is a special case where nothing is written to the // buffer, and res contains the desired buffer size. if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; } INTERCEPTOR(SSIZE_T, llistxattr, const char *path, char *list, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, llistxattr, path, list, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 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. SSIZE_T res = REAL(llistxattr)(path, list, size); if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; } INTERCEPTOR(SSIZE_T, flistxattr, int fd, char *list, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, flistxattr, fd, list, size); // 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(flistxattr)(fd, list, size); if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; } #define INIT_LISTXATTR \ COMMON_INTERCEPT_FUNCTION(listxattr); \ COMMON_INTERCEPT_FUNCTION(llistxattr); \ COMMON_INTERCEPT_FUNCTION(flistxattr); #else #define INIT_LISTXATTR #endif #if SANITIZER_INTERCEPT_GETXATTR INTERCEPTOR(SSIZE_T, getxattr, const char *path, const char *name, char *value, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getxattr, path, name, value, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 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. SSIZE_T res = REAL(getxattr)(path, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; } INTERCEPTOR(SSIZE_T, lgetxattr, const char *path, const char *name, char *value, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgetxattr, path, name, value, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 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. SSIZE_T res = REAL(lgetxattr)(path, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; } INTERCEPTOR(SSIZE_T, fgetxattr, int fd, const char *name, char *value, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetxattr, fd, name, value, size); if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 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. SSIZE_T res = REAL(fgetxattr)(fd, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; } #define INIT_GETXATTR \ COMMON_INTERCEPT_FUNCTION(getxattr); \ COMMON_INTERCEPT_FUNCTION(lgetxattr); \ COMMON_INTERCEPT_FUNCTION(fgetxattr); #else #define INIT_GETXATTR #endif #if SANITIZER_INTERCEPT_GETRESID INTERCEPTOR(int, getresuid, void *ruid, void *euid, void *suid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getresuid, ruid, euid, suid); // 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. int res = REAL(getresuid)(ruid, euid, suid); if (res >= 0) { if (ruid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ruid, uid_t_sz); if (euid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, euid, uid_t_sz); if (suid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, suid, uid_t_sz); } return res; } INTERCEPTOR(int, getresgid, void *rgid, void *egid, void *sgid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getresgid, rgid, egid, sgid); // 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. int res = REAL(getresgid)(rgid, egid, sgid); if (res >= 0) { if (rgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rgid, gid_t_sz); if (egid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, egid, gid_t_sz); if (sgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sgid, gid_t_sz); } return res; } #define INIT_GETRESID \ COMMON_INTERCEPT_FUNCTION(getresuid); \ COMMON_INTERCEPT_FUNCTION(getresgid); #else #define INIT_GETRESID #endif #if SANITIZER_INTERCEPT_GETIFADDRS // As long as getifaddrs()/freeifaddrs() use calloc()/free(), we don't need to // intercept freeifaddrs(). If that ceases to be the case, we might need to // intercept it to poison the memory again. INTERCEPTOR(int, getifaddrs, __sanitizer_ifaddrs **ifap) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getifaddrs, ifap); // 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. int res = REAL(getifaddrs)(ifap); if (res == 0 && ifap) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifap, sizeof(void *)); __sanitizer_ifaddrs *p = *ifap; while (p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(__sanitizer_ifaddrs)); if (p->ifa_name) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_name, REAL(strlen)(p->ifa_name) + 1); if (p->ifa_addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_addr, struct_sockaddr_sz); if (p->ifa_netmask) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_netmask, struct_sockaddr_sz); // On Linux this is a union, but the other member also points to a // struct sockaddr, so the following is sufficient. if (p->ifa_dstaddr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_dstaddr, struct_sockaddr_sz); // FIXME(smatveev): Unpoison p->ifa_data as well. p = p->ifa_next; } } return res; } #define INIT_GETIFADDRS \ COMMON_INTERCEPT_FUNCTION(getifaddrs); #else #define INIT_GETIFADDRS #endif #if SANITIZER_INTERCEPT_IF_INDEXTONAME INTERCEPTOR(char *, if_indextoname, unsigned int ifindex, char* ifname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, if_indextoname, ifindex, ifname); // 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. char *res = REAL(if_indextoname)(ifindex, ifname); if (res && ifname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1); return res; } INTERCEPTOR(unsigned int, if_nametoindex, const char* ifname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, if_nametoindex, ifname); if (ifname) COMMON_INTERCEPTOR_READ_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1); return REAL(if_nametoindex)(ifname); } #define INIT_IF_INDEXTONAME \ COMMON_INTERCEPT_FUNCTION(if_indextoname); \ COMMON_INTERCEPT_FUNCTION(if_nametoindex); #else #define INIT_IF_INDEXTONAME #endif #if SANITIZER_INTERCEPT_CAPGET INTERCEPTOR(int, capget, void *hdrp, void *datap) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, capget, hdrp, datap); if (hdrp) COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz); // 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. int res = REAL(capget)(hdrp, datap); if (res == 0 && datap) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datap, __user_cap_data_struct_sz); // We can also return -1 and write to hdrp->version if the version passed in // hdrp->version is unsupported. But that's not a trivial condition to check, // and anyway COMMON_INTERCEPTOR_READ_RANGE protects us to some extent. return res; } INTERCEPTOR(int, capset, void *hdrp, const void *datap) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, capset, hdrp, datap); if (hdrp) COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz); if (datap) COMMON_INTERCEPTOR_READ_RANGE(ctx, datap, __user_cap_data_struct_sz); return REAL(capset)(hdrp, datap); } #define INIT_CAPGET \ COMMON_INTERCEPT_FUNCTION(capget); \ COMMON_INTERCEPT_FUNCTION(capset); #else #define INIT_CAPGET #endif #if SANITIZER_INTERCEPT_AEABI_MEM DECLARE_REAL_AND_INTERCEPTOR(void *, memmove, void *, const void *, uptr); DECLARE_REAL_AND_INTERCEPTOR(void *, memcpy, void *, const void *, uptr); DECLARE_REAL_AND_INTERCEPTOR(void *, memset, void *, int, uptr); INTERCEPTOR(void *, __aeabi_memmove, void *to, const void *from, uptr size) { return WRAP(memmove)(to, from, size); } INTERCEPTOR(void *, __aeabi_memmove4, void *to, const void *from, uptr size) { return WRAP(memmove)(to, from, size); } INTERCEPTOR(void *, __aeabi_memmove8, void *to, const void *from, uptr size) { return WRAP(memmove)(to, from, size); } INTERCEPTOR(void *, __aeabi_memcpy, void *to, const void *from, uptr size) { return WRAP(memcpy)(to, from, size); } INTERCEPTOR(void *, __aeabi_memcpy4, void *to, const void *from, uptr size) { return WRAP(memcpy)(to, from, size); } INTERCEPTOR(void *, __aeabi_memcpy8, void *to, const void *from, uptr size) { return WRAP(memcpy)(to, from, size); } // Note the argument order. INTERCEPTOR(void *, __aeabi_memset, void *block, uptr size, int c) { return WRAP(memset)(block, c, size); } INTERCEPTOR(void *, __aeabi_memset4, void *block, uptr size, int c) { return WRAP(memset)(block, c, size); } INTERCEPTOR(void *, __aeabi_memset8, void *block, uptr size, int c) { return WRAP(memset)(block, c, size); } INTERCEPTOR(void *, __aeabi_memclr, void *block, uptr size) { return WRAP(memset)(block, 0, size); } INTERCEPTOR(void *, __aeabi_memclr4, void *block, uptr size) { return WRAP(memset)(block, 0, size); } INTERCEPTOR(void *, __aeabi_memclr8, void *block, uptr size) { return WRAP(memset)(block, 0, size); } #define INIT_AEABI_MEM \ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove8); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy8); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memset); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memset4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memset8); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr8); #else #define INIT_AEABI_MEM #endif // SANITIZER_INTERCEPT_AEABI_MEM #if SANITIZER_INTERCEPT___BZERO DECLARE_REAL_AND_INTERCEPTOR(void *, memset, void *, int, uptr); INTERCEPTOR(void *, __bzero, void *block, uptr size) { return WRAP(memset)(block, 0, size); } #define INIT___BZERO COMMON_INTERCEPT_FUNCTION(__bzero); #else #define INIT___BZERO #endif // SANITIZER_INTERCEPT___BZERO #if SANITIZER_INTERCEPT_FTIME INTERCEPTOR(int, ftime, __sanitizer_timeb *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ftime, tp); // 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. int res = REAL(ftime)(tp); if (tp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, sizeof(*tp)); return res; } #define INIT_FTIME COMMON_INTERCEPT_FUNCTION(ftime); #else #define INIT_FTIME #endif // SANITIZER_INTERCEPT_FTIME #if SANITIZER_INTERCEPT_XDR INTERCEPTOR(void, xdrmem_create, __sanitizer_XDR *xdrs, uptr addr, unsigned size, int op) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdrmem_create, xdrs, addr, size, op); // 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. REAL(xdrmem_create)(xdrs, addr, size, op); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs)); if (op == __sanitizer_XDR_ENCODE) { // It's not obvious how much data individual xdr_ routines write. // Simply unpoison the entire target buffer in advance. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (void *)addr, size); } } INTERCEPTOR(void, xdrstdio_create, __sanitizer_XDR *xdrs, void *file, int op) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdrstdio_create, xdrs, file, op); // 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. REAL(xdrstdio_create)(xdrs, file, op); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs)); } // 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 XDR_INTERCEPTOR(F, T) \ INTERCEPTOR(int, F, __sanitizer_XDR *xdrs, T *p) { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, F, xdrs, p); \ if (p && xdrs->x_op == __sanitizer_XDR_ENCODE) \ COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p)); \ int res = REAL(F)(xdrs, p); \ if (res && p && xdrs->x_op == __sanitizer_XDR_DECODE) \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); \ return res; \ } XDR_INTERCEPTOR(xdr_short, short) XDR_INTERCEPTOR(xdr_u_short, unsigned short) XDR_INTERCEPTOR(xdr_int, int) XDR_INTERCEPTOR(xdr_u_int, unsigned) XDR_INTERCEPTOR(xdr_long, long) XDR_INTERCEPTOR(xdr_u_long, unsigned long) XDR_INTERCEPTOR(xdr_hyper, long long) XDR_INTERCEPTOR(xdr_u_hyper, unsigned long long) XDR_INTERCEPTOR(xdr_longlong_t, long long) XDR_INTERCEPTOR(xdr_u_longlong_t, unsigned long long) XDR_INTERCEPTOR(xdr_int8_t, u8) XDR_INTERCEPTOR(xdr_uint8_t, u8) XDR_INTERCEPTOR(xdr_int16_t, u16) XDR_INTERCEPTOR(xdr_uint16_t, u16) XDR_INTERCEPTOR(xdr_int32_t, u32) XDR_INTERCEPTOR(xdr_uint32_t, u32) XDR_INTERCEPTOR(xdr_int64_t, u64) XDR_INTERCEPTOR(xdr_uint64_t, u64) XDR_INTERCEPTOR(xdr_quad_t, long long) XDR_INTERCEPTOR(xdr_u_quad_t, unsigned long long) XDR_INTERCEPTOR(xdr_bool, bool) XDR_INTERCEPTOR(xdr_enum, int) XDR_INTERCEPTOR(xdr_char, char) XDR_INTERCEPTOR(xdr_u_char, unsigned char) XDR_INTERCEPTOR(xdr_float, float) XDR_INTERCEPTOR(xdr_double, double) // FIXME: intercept xdr_array, opaque, union, vector, reference, pointer, // wrapstring, sizeof INTERCEPTOR(int, xdr_bytes, __sanitizer_XDR *xdrs, char **p, unsigned *sizep, unsigned maxsize) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdr_bytes, xdrs, p, sizep, maxsize); if (p && sizep && xdrs->x_op == __sanitizer_XDR_ENCODE) { COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p)); COMMON_INTERCEPTOR_READ_RANGE(ctx, sizep, sizeof(*sizep)); COMMON_INTERCEPTOR_READ_RANGE(ctx, *p, *sizep); } // 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. int res = REAL(xdr_bytes)(xdrs, p, sizep, maxsize); if (p && sizep && xdrs->x_op == __sanitizer_XDR_DECODE) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizep, sizeof(*sizep)); if (res && *p && *sizep) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, *sizep); } return res; } INTERCEPTOR(int, xdr_string, __sanitizer_XDR *xdrs, char **p, unsigned maxsize) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdr_string, xdrs, p, maxsize); if (p && xdrs->x_op == __sanitizer_XDR_ENCODE) { COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p)); COMMON_INTERCEPTOR_READ_RANGE(ctx, *p, REAL(strlen)(*p) + 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. int res = REAL(xdr_string)(xdrs, p, maxsize); if (p && xdrs->x_op == __sanitizer_XDR_DECODE) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); if (res && *p) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1); } return res; } #define INIT_XDR \ COMMON_INTERCEPT_FUNCTION(xdrmem_create); \ COMMON_INTERCEPT_FUNCTION(xdrstdio_create); \ COMMON_INTERCEPT_FUNCTION(xdr_short); \ COMMON_INTERCEPT_FUNCTION(xdr_u_short); \ COMMON_INTERCEPT_FUNCTION(xdr_int); \ COMMON_INTERCEPT_FUNCTION(xdr_u_int); \ COMMON_INTERCEPT_FUNCTION(xdr_long); \ COMMON_INTERCEPT_FUNCTION(xdr_u_long); \ COMMON_INTERCEPT_FUNCTION(xdr_hyper); \ COMMON_INTERCEPT_FUNCTION(xdr_u_hyper); \ COMMON_INTERCEPT_FUNCTION(xdr_longlong_t); \ COMMON_INTERCEPT_FUNCTION(xdr_u_longlong_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int8_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint8_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int16_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint16_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int32_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint32_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int64_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint64_t); \ COMMON_INTERCEPT_FUNCTION(xdr_quad_t); \ COMMON_INTERCEPT_FUNCTION(xdr_u_quad_t); \ COMMON_INTERCEPT_FUNCTION(xdr_bool); \ COMMON_INTERCEPT_FUNCTION(xdr_enum); \ COMMON_INTERCEPT_FUNCTION(xdr_char); \ COMMON_INTERCEPT_FUNCTION(xdr_u_char); \ COMMON_INTERCEPT_FUNCTION(xdr_float); \ COMMON_INTERCEPT_FUNCTION(xdr_double); \ COMMON_INTERCEPT_FUNCTION(xdr_bytes); \ COMMON_INTERCEPT_FUNCTION(xdr_string); #else #define INIT_XDR #endif // SANITIZER_INTERCEPT_XDR #if SANITIZER_INTERCEPT_TSEARCH INTERCEPTOR(void *, tsearch, void *key, void **rootp, int (*compar)(const void *, const void *)) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tsearch, key, rootp, compar); // 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. void *res = REAL(tsearch)(key, rootp, compar); if (res && *(void **)res == key) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(void *)); return res; } #define INIT_TSEARCH COMMON_INTERCEPT_FUNCTION(tsearch); #else #define INIT_TSEARCH #endif #if SANITIZER_INTERCEPT_LIBIO_INTERNALS || SANITIZER_INTERCEPT_FOPEN || \ SANITIZER_INTERCEPT_OPEN_MEMSTREAM void unpoison_file(__sanitizer_FILE *fp) { #if SANITIZER_HAS_STRUCT_FILE COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp, sizeof(*fp)); if (fp->_IO_read_base && fp->_IO_read_base < fp->_IO_read_end) COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_IO_read_base, fp->_IO_read_end - fp->_IO_read_base); #endif // SANITIZER_HAS_STRUCT_FILE } #endif #if SANITIZER_INTERCEPT_LIBIO_INTERNALS // These guys are called when a .c source is built with -O2. INTERCEPTOR(int, __uflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __uflow, fp); int res = REAL(__uflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __underflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __underflow, fp); int res = REAL(__underflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __overflow, __sanitizer_FILE *fp, int ch) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __overflow, fp, ch); int res = REAL(__overflow)(fp, ch); unpoison_file(fp); return res; } INTERCEPTOR(int, __wuflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __wuflow, fp); int res = REAL(__wuflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __wunderflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __wunderflow, fp); int res = REAL(__wunderflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __woverflow, __sanitizer_FILE *fp, int ch) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __woverflow, fp, ch); int res = REAL(__woverflow)(fp, ch); unpoison_file(fp); return res; } #define INIT_LIBIO_INTERNALS \ COMMON_INTERCEPT_FUNCTION(__uflow); \ COMMON_INTERCEPT_FUNCTION(__underflow); \ COMMON_INTERCEPT_FUNCTION(__overflow); \ COMMON_INTERCEPT_FUNCTION(__wuflow); \ COMMON_INTERCEPT_FUNCTION(__wunderflow); \ COMMON_INTERCEPT_FUNCTION(__woverflow); #else #define INIT_LIBIO_INTERNALS #endif #if SANITIZER_INTERCEPT_FOPEN INTERCEPTOR(__sanitizer_FILE *, fopen, const char *path, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fopen, path, mode); COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); __sanitizer_FILE *res = REAL(fopen)(path, mode); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } INTERCEPTOR(__sanitizer_FILE *, fdopen, int fd, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fdopen, fd, mode); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); __sanitizer_FILE *res = REAL(fdopen)(fd, mode); if (res) unpoison_file(res); return res; } INTERCEPTOR(__sanitizer_FILE *, freopen, const char *path, const char *mode, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, freopen, path, mode, fp); COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); __sanitizer_FILE *res = REAL(freopen)(path, mode, fp); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } #define INIT_FOPEN \ COMMON_INTERCEPT_FUNCTION(fopen); \ COMMON_INTERCEPT_FUNCTION(fdopen); \ COMMON_INTERCEPT_FUNCTION(freopen); #else #define INIT_FOPEN #endif #if SANITIZER_INTERCEPT_FOPEN64 INTERCEPTOR(__sanitizer_FILE *, fopen64, const char *path, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fopen64, path, mode); COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); __sanitizer_FILE *res = REAL(fopen64)(path, mode); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } INTERCEPTOR(__sanitizer_FILE *, freopen64, const char *path, const char *mode, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, freopen64, path, mode, fp); COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); __sanitizer_FILE *res = REAL(freopen64)(path, mode, fp); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } #define INIT_FOPEN64 \ COMMON_INTERCEPT_FUNCTION(fopen64); \ COMMON_INTERCEPT_FUNCTION(freopen64); #else #define INIT_FOPEN64 #endif #if SANITIZER_INTERCEPT_OPEN_MEMSTREAM INTERCEPTOR(__sanitizer_FILE *, open_memstream, char **ptr, SIZE_T *sizeloc) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, open_memstream, ptr, sizeloc); // 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. __sanitizer_FILE *res = REAL(open_memstream)(ptr, sizeloc); if (res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizeloc, sizeof(*sizeloc)); unpoison_file(res); FileMetadata file = {ptr, sizeloc}; SetInterceptorMetadata(res, file); } return res; } INTERCEPTOR(__sanitizer_FILE *, open_wmemstream, wchar_t **ptr, SIZE_T *sizeloc) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, open_wmemstream, ptr, sizeloc); __sanitizer_FILE *res = REAL(open_wmemstream)(ptr, sizeloc); if (res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizeloc, sizeof(*sizeloc)); unpoison_file(res); FileMetadata file = {(char **)ptr, sizeloc}; SetInterceptorMetadata(res, file); } return res; } INTERCEPTOR(__sanitizer_FILE *, fmemopen, void *buf, SIZE_T size, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fmemopen, buf, size, mode); // 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. __sanitizer_FILE *res = REAL(fmemopen)(buf, size, mode); if (res) unpoison_file(res); return res; } #define INIT_OPEN_MEMSTREAM \ COMMON_INTERCEPT_FUNCTION(open_memstream); \ COMMON_INTERCEPT_FUNCTION(open_wmemstream); \ COMMON_INTERCEPT_FUNCTION(fmemopen); #else #define INIT_OPEN_MEMSTREAM #endif #if SANITIZER_INTERCEPT_OBSTACK static void initialize_obstack(__sanitizer_obstack *obstack) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(obstack, sizeof(*obstack)); if (obstack->chunk) COMMON_INTERCEPTOR_INITIALIZE_RANGE(obstack->chunk, sizeof(*obstack->chunk)); } INTERCEPTOR(int, _obstack_begin_1, __sanitizer_obstack *obstack, int sz, int align, void *(*alloc_fn)(uptr arg, uptr sz), void (*free_fn)(uptr arg, void *p)) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin_1, obstack, sz, align, alloc_fn, free_fn); int res = REAL(_obstack_begin_1)(obstack, sz, align, alloc_fn, free_fn); if (res) initialize_obstack(obstack); return res; } INTERCEPTOR(int, _obstack_begin, __sanitizer_obstack *obstack, int sz, int align, void *(*alloc_fn)(uptr sz), void (*free_fn)(void *p)) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin, obstack, sz, align, alloc_fn, free_fn); int res = REAL(_obstack_begin)(obstack, sz, align, alloc_fn, free_fn); if (res) initialize_obstack(obstack); return res; } INTERCEPTOR(void, _obstack_newchunk, __sanitizer_obstack *obstack, int length) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _obstack_newchunk, obstack, length); REAL(_obstack_newchunk)(obstack, length); if (obstack->chunk) COMMON_INTERCEPTOR_INITIALIZE_RANGE( obstack->chunk, obstack->next_free - (char *)obstack->chunk); } #define INIT_OBSTACK \ COMMON_INTERCEPT_FUNCTION(_obstack_begin_1); \ COMMON_INTERCEPT_FUNCTION(_obstack_begin); \ COMMON_INTERCEPT_FUNCTION(_obstack_newchunk); #else #define INIT_OBSTACK #endif #if SANITIZER_INTERCEPT_FFLUSH INTERCEPTOR(int, fflush, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fflush, fp); int res = REAL(fflush)(fp); // FIXME: handle fp == NULL if (fp) { const FileMetadata *m = GetInterceptorMetadata(fp); if (m) COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size); } return res; } #define INIT_FFLUSH COMMON_INTERCEPT_FUNCTION(fflush); #else #define INIT_FFLUSH #endif #if SANITIZER_INTERCEPT_FCLOSE INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fclose, fp); if (fp) { COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); const FileMetadata *m = GetInterceptorMetadata(fp); if (m) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size); DeleteInterceptorMetadata(fp); } } return REAL(fclose)(fp); } #define INIT_FCLOSE COMMON_INTERCEPT_FUNCTION(fclose); #else #define INIT_FCLOSE #endif #if SANITIZER_INTERCEPT_DLOPEN_DLCLOSE INTERCEPTOR(void*, dlopen, const char *filename, int flag) { void *ctx; COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag); void *res = REAL(dlopen)(filename, flag); COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res); return res; } INTERCEPTOR(int, dlclose, void *handle) { void *ctx; COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlclose, handle); int res = REAL(dlclose)(handle); COMMON_INTERCEPTOR_LIBRARY_UNLOADED(); return res; } #define INIT_DLOPEN_DLCLOSE \ COMMON_INTERCEPT_FUNCTION(dlopen); \ COMMON_INTERCEPT_FUNCTION(dlclose); #else #define INIT_DLOPEN_DLCLOSE #endif #if SANITIZER_INTERCEPT_GETPASS INTERCEPTOR(char *, getpass, const char *prompt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpass, prompt); if (prompt) COMMON_INTERCEPTOR_READ_RANGE(ctx, prompt, REAL(strlen)(prompt)+1); char *res = REAL(getpass)(prompt); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res)+1); return res; } #define INIT_GETPASS COMMON_INTERCEPT_FUNCTION(getpass); #else #define INIT_GETPASS #endif #if SANITIZER_INTERCEPT_TIMERFD INTERCEPTOR(int, timerfd_settime, int fd, int flags, void *new_value, void *old_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, timerfd_settime, fd, flags, new_value, old_value); COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerspec_sz); int res = REAL(timerfd_settime)(fd, flags, new_value, old_value); if (res != -1 && old_value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerspec_sz); return res; } INTERCEPTOR(int, timerfd_gettime, int fd, void *curr_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, timerfd_gettime, fd, curr_value); int res = REAL(timerfd_gettime)(fd, curr_value); if (res != -1 && curr_value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerspec_sz); return res; } #define INIT_TIMERFD \ COMMON_INTERCEPT_FUNCTION(timerfd_settime); \ COMMON_INTERCEPT_FUNCTION(timerfd_gettime); #else #define INIT_TIMERFD #endif #if SANITIZER_INTERCEPT_MLOCKX // Linux kernel has a bug that leads to kernel deadlock if a process // maps TBs of memory and then calls mlock(). static void MlockIsUnsupported() { static atomic_uint8_t printed; if (atomic_exchange(&printed, 1, memory_order_relaxed)) return; VPrintf(1, "INFO: %s ignores mlock/mlockall/munlock/munlockall\n", SanitizerToolName); } INTERCEPTOR(int, mlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; } INTERCEPTOR(int, munlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; } INTERCEPTOR(int, mlockall, int flags) { MlockIsUnsupported(); return 0; } INTERCEPTOR(int, munlockall, void) { MlockIsUnsupported(); return 0; } #define INIT_MLOCKX \ COMMON_INTERCEPT_FUNCTION(mlock); \ COMMON_INTERCEPT_FUNCTION(munlock); \ COMMON_INTERCEPT_FUNCTION(mlockall); \ COMMON_INTERCEPT_FUNCTION(munlockall); #else #define INIT_MLOCKX #endif // SANITIZER_INTERCEPT_MLOCKX static void InitializeCommonInterceptors() { static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1]; interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap(); INIT_TEXTDOMAIN; INIT_STRCMP; INIT_STRNCMP; INIT_STRCASECMP; INIT_STRNCASECMP; INIT_MEMCHR; INIT_MEMRCHR; INIT_READ; INIT_PREAD; INIT_PREAD64; INIT_READV; INIT_PREADV; INIT_PREADV64; INIT_WRITE; INIT_PWRITE; INIT_PWRITE64; INIT_WRITEV; INIT_PWRITEV; INIT_PWRITEV64; INIT_PRCTL; INIT_LOCALTIME_AND_FRIENDS; INIT_STRPTIME; INIT_SCANF; INIT_ISOC99_SCANF; INIT_PRINTF; + INIT_PRINTF_L; INIT_ISOC99_PRINTF; INIT_FREXP; INIT_FREXPF_FREXPL; INIT_GETPWNAM_AND_FRIENDS; INIT_GETPWNAM_R_AND_FRIENDS; INIT_GETPWENT; INIT_FGETPWENT; INIT_GETPWENT_R; INIT_SETPWENT; INIT_CLOCK_GETTIME; INIT_GETITIMER; INIT_TIME; INIT_GLOB; INIT_WAIT; INIT_WAIT4; INIT_INET; INIT_PTHREAD_GETSCHEDPARAM; INIT_GETADDRINFO; INIT_GETNAMEINFO; INIT_GETSOCKNAME; INIT_GETHOSTBYNAME; INIT_GETHOSTBYNAME_R; INIT_GETHOSTBYNAME2_R; INIT_GETHOSTBYADDR_R; INIT_GETHOSTENT_R; INIT_GETSOCKOPT; INIT_ACCEPT; INIT_ACCEPT4; INIT_MODF; INIT_RECVMSG; INIT_GETPEERNAME; INIT_IOCTL; INIT_INET_ATON; INIT_SYSINFO; INIT_READDIR; INIT_READDIR64; INIT_PTRACE; INIT_SETLOCALE; INIT_GETCWD; INIT_GET_CURRENT_DIR_NAME; INIT_STRTOIMAX; INIT_MBSTOWCS; INIT_MBSNRTOWCS; INIT_WCSTOMBS; INIT_WCSNRTOMBS; INIT_TCGETATTR; INIT_REALPATH; INIT_CANONICALIZE_FILE_NAME; INIT_CONFSTR; INIT_SCHED_GETAFFINITY; + INIT_SCHED_GETPARAM; INIT_STRERROR; INIT_STRERROR_R; INIT_XPG_STRERROR_R; INIT_SCANDIR; INIT_SCANDIR64; INIT_GETGROUPS; INIT_POLL; INIT_PPOLL; INIT_WORDEXP; INIT_SIGWAIT; INIT_SIGWAITINFO; INIT_SIGTIMEDWAIT; INIT_SIGSETOPS; INIT_SIGPENDING; INIT_SIGPROCMASK; INIT_BACKTRACE; INIT__EXIT; INIT_PTHREAD_MUTEX_LOCK; INIT_PTHREAD_MUTEX_UNLOCK; INIT_GETMNTENT; INIT_GETMNTENT_R; INIT_STATFS; INIT_STATFS64; INIT_STATVFS; INIT_STATVFS64; INIT_INITGROUPS; INIT_ETHER_NTOA_ATON; INIT_ETHER_HOST; INIT_ETHER_R; INIT_SHMCTL; INIT_RANDOM_R; INIT_PTHREAD_ATTR_GET; INIT_PTHREAD_ATTR_GETINHERITSCHED; INIT_PTHREAD_ATTR_GETAFFINITY_NP; INIT_PTHREAD_MUTEXATTR_GETPSHARED; INIT_PTHREAD_MUTEXATTR_GETTYPE; INIT_PTHREAD_MUTEXATTR_GETPROTOCOL; INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING; INIT_PTHREAD_MUTEXATTR_GETROBUST; INIT_PTHREAD_MUTEXATTR_GETROBUST_NP; INIT_PTHREAD_RWLOCKATTR_GETPSHARED; INIT_PTHREAD_RWLOCKATTR_GETKIND_NP; INIT_PTHREAD_CONDATTR_GETPSHARED; INIT_PTHREAD_CONDATTR_GETCLOCK; INIT_PTHREAD_BARRIERATTR_GETPSHARED; INIT_TMPNAM; INIT_TMPNAM_R; INIT_TEMPNAM; INIT_PTHREAD_SETNAME_NP; INIT_SINCOS; INIT_REMQUO; INIT_LGAMMA; INIT_LGAMMA_R; INIT_LGAMMAL_R; INIT_DRAND48_R; INIT_RAND_R; INIT_GETLINE; INIT_ICONV; INIT_TIMES; INIT_TLS_GET_ADDR; INIT_LISTXATTR; INIT_GETXATTR; INIT_GETRESID; INIT_GETIFADDRS; INIT_IF_INDEXTONAME; INIT_CAPGET; INIT_AEABI_MEM; INIT___BZERO; INIT_FTIME; INIT_XDR; INIT_TSEARCH; INIT_LIBIO_INTERNALS; INIT_FOPEN; INIT_FOPEN64; INIT_OPEN_MEMSTREAM; INIT_OBSTACK; INIT_FFLUSH; INIT_FCLOSE; INIT_DLOPEN_DLCLOSE; INIT_GETPASS; INIT_TIMERFD; INIT_MLOCKX; } Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_common_libcdep.cc (revision 279191) @@ -1,69 +1,135 @@ //===-- sanitizer_common_libcdep.cc ---------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. //===----------------------------------------------------------------------===// #include "sanitizer_common.h" #include "sanitizer_flags.h" +#include "sanitizer_stackdepot.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" namespace __sanitizer { bool ReportFile::PrintsToTty() { SpinMutexLock l(mu); ReopenIfNecessary(); return internal_isatty(fd) != 0; } bool ColorizeReports() { // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color // printing on Windows. if (SANITIZER_WINDOWS) return false; const char *flag = common_flags()->color; return internal_strcmp(flag, "always") == 0 || (internal_strcmp(flag, "auto") == 0 && report_file.PrintsToTty()); } static void (*sandboxing_callback)(); void SetSandboxingCallback(void (*f)()) { sandboxing_callback = 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(); } #else AddressInfo ai; ReportErrorSummary(error_type, ai.file, ai.line, ai.function); #endif +} + +static void (*SoftRssLimitExceededCallback)(bool exceeded); +void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) { + CHECK_EQ(SoftRssLimitExceededCallback, nullptr); + SoftRssLimitExceededCallback = Callback; +} + +void BackgroundThread(void *arg) { + uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb; + uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb; + uptr prev_reported_rss = 0; + uptr prev_reported_stack_depot_size = 0; + bool reached_soft_rss_limit = false; + while (true) { + SleepForMillis(100); + uptr current_rss_mb = GetRSS() >> 20; + if (Verbosity()) { + // If RSS has grown 10% since last time, print some information. + if (prev_reported_rss * 11 / 10 < current_rss_mb) { + Printf("%s: RSS: %zdMb\n", SanitizerToolName, current_rss_mb); + prev_reported_rss = current_rss_mb; + } + // If stack depot has grown 10% since last time, print it too. + StackDepotStats *stack_depot_stats = StackDepotGetStats(); + if (prev_reported_stack_depot_size * 11 / 10 < + stack_depot_stats->allocated) { + Printf("%s: StackDepot: %zd ids; %zdM allocated\n", + SanitizerToolName, + stack_depot_stats->n_uniq_ids, + stack_depot_stats->allocated >> 20); + prev_reported_stack_depot_size = stack_depot_stats->allocated; + } + } + // Check RSS against the limit. + if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) { + Report("%s: hard rss limit exhausted (%zdMb vs %zdMb)\n", + SanitizerToolName, hard_rss_limit_mb, current_rss_mb); + DumpProcessMap(); + Die(); + } + if (soft_rss_limit_mb) { + if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) { + reached_soft_rss_limit = true; + Report("%s: soft rss limit exhausted (%zdMb vs %zdMb)\n", + SanitizerToolName, soft_rss_limit_mb, current_rss_mb); + if (SoftRssLimitExceededCallback) + SoftRssLimitExceededCallback(true); + } else if (soft_rss_limit_mb >= current_rss_mb && + reached_soft_rss_limit) { + reached_soft_rss_limit = false; + if (SoftRssLimitExceededCallback) + SoftRssLimitExceededCallback(false); + } + } + } +} + +void MaybeStartBackgroudThread() { + if (!SANITIZER_LINUX) return; // 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); } } // namespace __sanitizer void NOINLINE __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args) { PrepareForSandboxing(args); if (sandboxing_callback) sandboxing_callback(); } Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_libcdep.cc (revision 279191) @@ -1,581 +1,718 @@ //===-- sanitizer_coverage.cc ---------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Sanitizer Coverage. // This file implements run-time support for a poor man's coverage tool. // // Compiler instrumentation: // For every interesting basic block the compiler injects the following code: -// if (Guard) { +// if (Guard < 0) { // __sanitizer_cov(&Guard); // } +// At the module start up time __sanitizer_cov_module_init sets the guards +// to consecutive negative numbers (-1, -2, -3, ...). // It's fine to call __sanitizer_cov more than once for a given block. // // Run-time: // - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC). -// and atomically set Guard to 1. +// and atomically set Guard to -Guard. // - __sanitizer_cov_dump: dump the coverage data to disk. // For every module of the current process that has coverage data // this will create a file module_name.PID.sancov. The file format is simple: // it's just a sorted sequence of 4-byte offsets in the module. // // Eventually, this coverage implementation should be obsoleted by a more // powerful general purpose Clang/LLVM coverage instrumentation. // Consider this implementation as prototype. // // FIXME: support (or at least test with) dlclose. //===----------------------------------------------------------------------===// #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_mutex.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" #include "sanitizer_flags.h" static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once. static atomic_uintptr_t coverage_counter; // pc_array is the array containing the covered PCs. // To make the pc_array thread- and async-signal-safe it has to be large enough. // 128M counters "ought to be enough for anybody" (4M on 32-bit). // With coverage_direct=1 in ASAN_OPTIONS, pc_array memory is mapped to a file. // In this mode, __sanitizer_cov_dump does nothing, and CovUpdateMapping() // dump current memory layout to another file. static bool cov_sandboxed = false; static int cov_fd = kInvalidFd; static unsigned int cov_max_block_size = 0; +static bool coverage_enabled = false; +static const char *coverage_dir; namespace __sanitizer { class CoverageData { public: void Init(); + void Enable(); + void Disable(); + void ReInit(); void BeforeFork(); void AfterFork(int child_pid); void Extend(uptr npcs); - void Add(uptr pc, u8 *guard); + void Add(uptr pc, u32 *guard); void IndirCall(uptr caller, uptr callee, uptr callee_cache[], uptr cache_size); void DumpCallerCalleePairs(); void DumpTrace(); ALWAYS_INLINE - void TraceBasicaBlock(uptr *cache); + void TraceBasicBlock(s32 *id); + void InitializeGuardArray(s32 *guards); + void InitializeGuards(s32 *guards, uptr n); + void ReinitializeGuards(); + uptr *data(); uptr size(); private: // Maximal size pc array may ever grow. // We MmapNoReserve this space to ensure that the array is contiguous. - static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27); + static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 24, 1 << 27); // The amount file mapping for the pc array is grown by. static const uptr kPcArrayMmapSize = 64 * 1024; // pc_array is allocated with MmapNoReserveOrDie and so it uses only as // much RAM as it really needs. uptr *pc_array; // Index of the first available pc_array slot. atomic_uintptr_t pc_array_index; // Array size. atomic_uintptr_t pc_array_size; // Current file mapped size of the pc array. uptr pc_array_mapped_size; // Descriptor of the file mapped pc array. int pc_fd; + // Vector of coverage guard arrays, protected by mu. + InternalMmapVectorNoCtor guard_array_vec; + // Caller-Callee (cc) array, size and current index. static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24); uptr **cc_array; atomic_uintptr_t cc_array_index; atomic_uintptr_t cc_array_size; - // Tracing (tr) pc and event arrays, their size and current index. + // Tracing event array, size and current pointer. // We record all events (basic block entries) in a global buffer of u32 - // values. Each such value is an index in the table of TracedPc objects. + // values. Each such value is the index in pc_array. // So far the tracing is highly experimental: // - not thread-safe; // - does not support long traces; // - not tuned for performance. - struct TracedPc { - uptr pc; - const char *module_name; - uptr module_offset; - }; static const uptr kTrEventArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 30); u32 *tr_event_array; uptr tr_event_array_size; - uptr tr_event_array_index; + u32 *tr_event_pointer; static const uptr kTrPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27); - TracedPc *tr_pc_array; - uptr tr_pc_array_size; - uptr tr_pc_array_index; StaticSpinMutex mu; void DirectOpen(); - void ReInit(); }; static CoverageData coverage_data; +void CovUpdateMapping(const char *path, uptr caller_pc = 0); + void CoverageData::DirectOpen() { InternalScopedString path(kMaxPathLength); internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw", - common_flags()->coverage_dir, internal_getpid()); + coverage_dir, internal_getpid()); pc_fd = OpenFile(path.data(), true); if (internal_iserror(pc_fd)) { Report(" Coverage: failed to open %s for writing\n", path.data()); Die(); } pc_array_mapped_size = 0; - CovUpdateMapping(); + CovUpdateMapping(coverage_dir); } void CoverageData::Init() { + pc_fd = kInvalidFd; +} + +void CoverageData::Enable() { + if (pc_array) + return; pc_array = reinterpret_cast( MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit")); - pc_fd = kInvalidFd; + atomic_store(&pc_array_index, 0, memory_order_relaxed); if (common_flags()->coverage_direct) { atomic_store(&pc_array_size, 0, memory_order_relaxed); - atomic_store(&pc_array_index, 0, memory_order_relaxed); } else { atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed); - atomic_store(&pc_array_index, 0, memory_order_relaxed); } cc_array = reinterpret_cast(MmapNoReserveOrDie( sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array")); atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed); atomic_store(&cc_array_index, 0, memory_order_relaxed); - tr_event_array = reinterpret_cast( - MmapNoReserveOrDie(sizeof(tr_event_array[0]) * kTrEventArrayMaxSize, - "CovInit::tr_event_array")); + // Allocate tr_event_array with a guard page at the end. + tr_event_array = reinterpret_cast(MmapNoReserveOrDie( + sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(), + "CovInit::tr_event_array")); + Mprotect(reinterpret_cast(&tr_event_array[kTrEventArrayMaxSize]), + GetMmapGranularity()); tr_event_array_size = kTrEventArrayMaxSize; - tr_event_array_index = 0; + tr_event_pointer = tr_event_array; +} - tr_pc_array = reinterpret_cast(MmapNoReserveOrDie( - sizeof(tr_pc_array[0]) * kTrEventArrayMaxSize, "CovInit::tr_pc_array")); - tr_pc_array_size = kTrEventArrayMaxSize; - tr_pc_array_index = 0; +void CoverageData::InitializeGuardArray(s32 *guards) { + Enable(); // Make sure coverage is enabled at this point. + s32 n = guards[0]; + for (s32 j = 1; j <= n; j++) { + uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); + guards[j] = -static_cast(idx + 1); + } } +void CoverageData::Disable() { + if (pc_array) { + internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize); + pc_array = nullptr; + } + if (cc_array) { + internal_munmap(cc_array, sizeof(uptr *) * kCcArrayMaxSize); + cc_array = nullptr; + } + if (tr_event_array) { + internal_munmap(tr_event_array, + sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + + GetMmapGranularity()); + tr_event_array = nullptr; + tr_event_pointer = nullptr; + } + if (pc_fd != kInvalidFd) { + internal_close(pc_fd); + pc_fd = kInvalidFd; + } +} + +void CoverageData::ReinitializeGuards() { + // Assuming single thread. + atomic_store(&pc_array_index, 0, memory_order_relaxed); + for (uptr i = 0; i < guard_array_vec.size(); i++) + InitializeGuardArray(guard_array_vec[i]); +} + void CoverageData::ReInit() { - internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize); - if (pc_fd != kInvalidFd) internal_close(pc_fd); - if (common_flags()->coverage_direct) { - // In memory-mapped mode we must extend the new file to the known array - // size. - uptr size = atomic_load(&pc_array_size, memory_order_relaxed); - Init(); - if (size) Extend(size); - } else { - Init(); + Disable(); + if (coverage_enabled) { + if (common_flags()->coverage_direct) { + // In memory-mapped mode we must extend the new file to the known array + // size. + uptr size = atomic_load(&pc_array_size, memory_order_relaxed); + Enable(); + if (size) Extend(size); + if (coverage_enabled) CovUpdateMapping(coverage_dir); + } else { + Enable(); + } } + // Re-initialize the guards. + // We are single-threaded now, no need to grab any lock. + CHECK_EQ(atomic_load(&pc_array_index, memory_order_relaxed), 0); + ReinitializeGuards(); } void CoverageData::BeforeFork() { mu.Lock(); } void CoverageData::AfterFork(int child_pid) { // We are single-threaded so it's OK to release the lock early. mu.Unlock(); if (child_pid == 0) ReInit(); } // Extend coverage PC array to fit additional npcs elements. void CoverageData::Extend(uptr npcs) { if (!common_flags()->coverage_direct) return; SpinMutexLock l(&mu); - if (pc_fd == kInvalidFd) DirectOpen(); - CHECK_NE(pc_fd, kInvalidFd); - uptr size = atomic_load(&pc_array_size, memory_order_relaxed); size += npcs * sizeof(uptr); - if (size > pc_array_mapped_size) { + if (coverage_enabled && size > pc_array_mapped_size) { + if (pc_fd == kInvalidFd) DirectOpen(); + CHECK_NE(pc_fd, kInvalidFd); + uptr new_mapped_size = pc_array_mapped_size; while (size > new_mapped_size) new_mapped_size += kPcArrayMmapSize; + CHECK_LE(new_mapped_size, sizeof(uptr) * kPcArrayMaxSize); // Extend the file and map the new space at the end of pc_array. uptr res = internal_ftruncate(pc_fd, new_mapped_size); int err; if (internal_iserror(res, &err)) { Printf("failed to extend raw coverage file: %d\n", err); Die(); } - void *p = MapWritableFileToMemory(pc_array + pc_array_mapped_size, + + uptr next_map_base = ((uptr)pc_array) + pc_array_mapped_size; + void *p = MapWritableFileToMemory((void *)next_map_base, new_mapped_size - pc_array_mapped_size, pc_fd, pc_array_mapped_size); - CHECK_EQ(p, pc_array + pc_array_mapped_size); + CHECK_EQ((uptr)p, next_map_base); pc_array_mapped_size = new_mapped_size; } atomic_store(&pc_array_size, size, memory_order_release); } -// Atomically add the pc to the vector. The atomically set the guard to 1. -// If the function is called more than once for a given PC it will -// be inserted multiple times, which is fine. -void CoverageData::Add(uptr pc, u8 *guard) { +void CoverageData::InitializeGuards(s32 *guards, uptr n) { + // The array 'guards' has n+1 elements, we use the element zero + // to store 'n'. + CHECK_LT(n, 1 << 30); + guards[0] = static_cast(n); + InitializeGuardArray(guards); + SpinMutexLock l(&mu); + guard_array_vec.push_back(guards); +} + +// If guard is negative, atomically set it to -guard and store the PC in +// pc_array. +void CoverageData::Add(uptr pc, u32 *guard) { + atomic_uint32_t *atomic_guard = reinterpret_cast(guard); + s32 guard_value = atomic_load(atomic_guard, memory_order_relaxed); + if (guard_value >= 0) return; + + atomic_store(atomic_guard, -guard_value, memory_order_relaxed); if (!pc_array) return; - uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); + + uptr idx = -guard_value - 1; + if (idx >= atomic_load(&pc_array_index, memory_order_acquire)) + return; // May happen after fork when pc_array_index becomes 0. CHECK_LT(idx * sizeof(uptr), atomic_load(&pc_array_size, memory_order_acquire)); pc_array[idx] = pc; atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); - // Set the guard. - atomic_uint8_t *atomic_guard = reinterpret_cast(guard); - atomic_store(atomic_guard, 1, memory_order_relaxed); } // Registers a pair caller=>callee. // When a given caller is seen for the first time, the callee_cache is added // to the global array cc_array, callee_cache[0] is set to caller and // callee_cache[1] is set to cache_size. // Then we are trying to add callee to callee_cache [2,cache_size) if it is // not there yet. // If the cache is full we drop the callee (may want to fix this later). void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[], uptr cache_size) { if (!cc_array) return; atomic_uintptr_t *atomic_callee_cache = reinterpret_cast(callee_cache); uptr zero = 0; if (atomic_compare_exchange_strong(&atomic_callee_cache[0], &zero, caller, memory_order_seq_cst)) { uptr idx = atomic_fetch_add(&cc_array_index, 1, memory_order_relaxed); CHECK_LT(idx * sizeof(uptr), atomic_load(&cc_array_size, memory_order_acquire)); callee_cache[1] = cache_size; cc_array[idx] = callee_cache; } CHECK_EQ(atomic_load(&atomic_callee_cache[0], memory_order_relaxed), caller); for (uptr i = 2; i < cache_size; i++) { uptr was = 0; if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee, memory_order_seq_cst)) { atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); return; } if (was == callee) // Already have this callee. return; } } uptr *CoverageData::data() { return pc_array; } uptr CoverageData::size() { return atomic_load(&pc_array_index, memory_order_relaxed); } // Block layout for packed file format: header, followed by module name (no // trailing zero), followed by data blob. struct CovHeader { int pid; unsigned int module_name_length; unsigned int data_length; }; static void CovWritePacked(int pid, const char *module, const void *blob, unsigned int blob_size) { if (cov_fd < 0) return; unsigned module_name_length = internal_strlen(module); CovHeader header = {pid, module_name_length, blob_size}; if (cov_max_block_size == 0) { // Writing to a file. Just go ahead. internal_write(cov_fd, &header, sizeof(header)); internal_write(cov_fd, module, module_name_length); internal_write(cov_fd, blob, blob_size); } else { // Writing to a socket. We want to split the data into appropriately sized // blocks. InternalScopedBuffer block(cov_max_block_size); CHECK_EQ((uptr)block.data(), (uptr)(CovHeader *)block.data()); uptr header_size_with_module = sizeof(header) + module_name_length; CHECK_LT(header_size_with_module, cov_max_block_size); unsigned int max_payload_size = cov_max_block_size - header_size_with_module; char *block_pos = block.data(); internal_memcpy(block_pos, &header, sizeof(header)); block_pos += sizeof(header); internal_memcpy(block_pos, module, module_name_length); block_pos += module_name_length; char *block_data_begin = block_pos; const char *blob_pos = (const char *)blob; while (blob_size > 0) { unsigned int payload_size = Min(blob_size, max_payload_size); blob_size -= payload_size; internal_memcpy(block_data_begin, blob_pos, payload_size); blob_pos += payload_size; ((CovHeader *)block.data())->data_length = payload_size; internal_write(cov_fd, block.data(), header_size_with_module + payload_size); } } } // If packed = false: .. (name = module name). // If packed = true and name == 0: ... // If packed = true and name != 0: .. (name is // user-supplied). -static int CovOpenFile(bool packed, const char* name) { +static int CovOpenFile(bool packed, const char *name, + const char *extension = "sancov") { InternalScopedString path(kMaxPathLength); if (!packed) { CHECK(name); - path.append("%s/%s.%zd.sancov", common_flags()->coverage_dir, name, - internal_getpid()); + path.append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(), + extension); } else { if (!name) - path.append("%s/%zd.sancov.packed", common_flags()->coverage_dir, - internal_getpid()); + path.append("%s/%zd.%s.packed", coverage_dir, internal_getpid(), + extension); else - path.append("%s/%s.sancov.packed", common_flags()->coverage_dir, name); + path.append("%s/%s.%s.packed", coverage_dir, name, extension); } uptr fd = OpenFile(path.data(), true); if (internal_iserror(fd)) { Report(" SanitizerCoverage: failed to open %s for writing\n", path.data()); return -1; } return fd; } // Dump trace PCs and trace events into two separate files. void CoverageData::DumpTrace() { - uptr max_idx = tr_event_array_index; + uptr max_idx = tr_event_pointer - tr_event_array; if (!max_idx) return; auto sym = Symbolizer::GetOrInit(); if (!sym) return; InternalScopedString out(32 << 20); - for (uptr i = 0; i < max_idx; i++) { - u32 pc_idx = tr_event_array[i]; - TracedPc *t = &tr_pc_array[pc_idx]; - if (!t->module_name) { - const char *module_name = ""; - uptr module_address = 0; - sym->GetModuleNameAndOffsetForPC(t->pc, &module_name, &module_address); - t->module_name = internal_strdup(module_name); - t->module_offset = module_address; - out.append("%s 0x%zx\n", t->module_name, t->module_offset); - } + for (uptr i = 0, n = size(); i < n; i++) { + const char *module_name = ""; + uptr module_address = 0; + sym->GetModuleNameAndOffsetForPC(pc_array[i], &module_name, + &module_address); + out.append("%s 0x%zx\n", module_name, module_address); } int fd = CovOpenFile(false, "trace-points"); if (fd < 0) return; internal_write(fd, out.data(), out.length()); internal_close(fd); fd = CovOpenFile(false, "trace-events"); if (fd < 0) return; - internal_write(fd, tr_event_array, max_idx * sizeof(tr_event_array[0])); + uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]); + u8 *event_bytes = reinterpret_cast(tr_event_array); + // The trace file could be huge, and may not be written with a single syscall. + while (bytes_to_write) { + uptr actually_written = internal_write(fd, event_bytes, bytes_to_write); + if (actually_written <= bytes_to_write) { + bytes_to_write -= actually_written; + event_bytes += actually_written; + } else { + break; + } + } internal_close(fd); - VReport(1, " CovDump: Trace: %zd PCs written\n", tr_pc_array_index); - VReport(1, " CovDump: Trace: %zd Events written\n", tr_event_array_index); + VReport(1, " CovDump: Trace: %zd PCs written\n", size()); + VReport(1, " CovDump: Trace: %zd Events written\n", max_idx); } // This function dumps the caller=>callee pairs into a file as a sequence of // lines like "module_name offset". void CoverageData::DumpCallerCalleePairs() { uptr max_idx = atomic_load(&cc_array_index, memory_order_relaxed); if (!max_idx) return; auto sym = Symbolizer::GetOrInit(); if (!sym) return; InternalScopedString out(32 << 20); uptr total = 0; for (uptr i = 0; i < max_idx; i++) { uptr *cc_cache = cc_array[i]; CHECK(cc_cache); uptr caller = cc_cache[0]; uptr n_callees = cc_cache[1]; const char *caller_module_name = ""; uptr caller_module_address = 0; sym->GetModuleNameAndOffsetForPC(caller, &caller_module_name, &caller_module_address); for (uptr j = 2; j < n_callees; j++) { uptr callee = cc_cache[j]; if (!callee) break; total++; const char *callee_module_name = ""; uptr callee_module_address = 0; sym->GetModuleNameAndOffsetForPC(callee, &callee_module_name, &callee_module_address); out.append("%s 0x%zx\n%s 0x%zx\n", caller_module_name, caller_module_address, callee_module_name, callee_module_address); } } int fd = CovOpenFile(false, "caller-callee"); if (fd < 0) return; internal_write(fd, out.data(), out.length()); internal_close(fd); VReport(1, " CovDump: %zd caller-callee pairs written\n", total); } // Record the current PC into the event buffer. // Every event is a u32 value (index in tr_pc_array_index) so we compute // it once and then cache in the provided 'cache' storage. -void CoverageData::TraceBasicaBlock(uptr *cache) { - CHECK(common_flags()->coverage); - uptr idx = *cache; - if (!idx) { - CHECK_LT(tr_pc_array_index, kTrPcArrayMaxSize); - idx = tr_pc_array_index++; - TracedPc *t = &tr_pc_array[idx]; - t->pc = GET_CALLER_PC(); - *cache = idx; - CHECK_LT(idx, 1U << 31); +// +// This function will eventually be inlined by the compiler. +void CoverageData::TraceBasicBlock(s32 *id) { + // Will trap here if + // 1. coverage is not enabled at run-time. + // 2. The array tr_event_array is full. + *tr_event_pointer = static_cast(*id - 1); + tr_event_pointer++; +} + +static void CovDumpAsBitSet() { + if (!common_flags()->coverage_bitset) return; + if (!coverage_data.size()) return; + int fd = CovOpenFile(/* packed */false, "combined", "bitset-sancov"); + if (fd < 0) return; + uptr n = coverage_data.size(); + uptr n_set_bits = 0; + InternalScopedBuffer out(n); + for (uptr i = 0; i < n; i++) { + uptr pc = coverage_data.data()[i]; + out[i] = pc ? '1' : '0'; + if (pc) + n_set_bits++; } - CHECK_LT(tr_event_array_index, tr_event_array_size); - tr_event_array[tr_event_array_index] = static_cast(idx); - tr_event_array_index++; + internal_write(fd, out.data(), n); + internal_close(fd); + VReport(1, " CovDump: bitset of %zd bits written, %zd bits are set\n", n, + n_set_bits); } // Dump the coverage on disk. static void CovDump() { - if (!common_flags()->coverage || common_flags()->coverage_direct) return; + if (!coverage_enabled || common_flags()->coverage_direct) return; #if !SANITIZER_WINDOWS if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) return; + CovDumpAsBitSet(); + coverage_data.DumpTrace(); + if (!common_flags()->coverage_pcs) return; uptr size = coverage_data.size(); InternalMmapVector offsets(size); uptr *vb = coverage_data.data(); uptr *ve = vb + size; SortArray(vb, size); MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr mb, me, off, prot; InternalScopedString module(kMaxPathLength); InternalScopedString path(kMaxPathLength); for (int i = 0; proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot); i++) { if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) continue; while (vb < ve && *vb < mb) vb++; if (vb >= ve) break; if (*vb < me) { offsets.clear(); const uptr *old_vb = vb; CHECK_LE(off, *vb); for (; vb < ve && *vb < me; vb++) { uptr diff = *vb - (i ? mb : 0) + off; CHECK_LE(diff, 0xffffffffU); offsets.push_back(static_cast(diff)); } const char *module_name = StripModuleName(module.data()); if (cov_sandboxed) { if (cov_fd >= 0) { CovWritePacked(internal_getpid(), module_name, offsets.data(), offsets.size() * sizeof(u32)); VReport(1, " CovDump: %zd PCs written to packed file\n", vb - old_vb); } } else { // One file per module per process. path.clear(); - path.append("%s/%s.%zd.sancov", common_flags()->coverage_dir, - module_name, internal_getpid()); + path.append("%s/%s.%zd.sancov", coverage_dir, module_name, + internal_getpid()); int fd = CovOpenFile(false /* packed */, module_name); if (fd > 0) { internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); internal_close(fd); VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), vb - old_vb); } } } } if (cov_fd >= 0) internal_close(cov_fd); coverage_data.DumpCallerCalleePairs(); - coverage_data.DumpTrace(); #endif // !SANITIZER_WINDOWS } void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { if (!args) return; - if (!common_flags()->coverage) return; + if (!coverage_enabled) return; cov_sandboxed = args->coverage_sandboxed; if (!cov_sandboxed) return; cov_fd = args->coverage_fd; cov_max_block_size = args->coverage_max_block_size; if (cov_fd < 0) // Pre-open the file now. The sandbox won't allow us to do it later. cov_fd = CovOpenFile(true /* packed */, 0); } int MaybeOpenCovFile(const char *name) { CHECK(name); - if (!common_flags()->coverage) return -1; + if (!coverage_enabled) return -1; return CovOpenFile(true /* packed */, name); } void CovBeforeFork() { coverage_data.BeforeFork(); } void CovAfterFork(int child_pid) { coverage_data.AfterFork(child_pid); } +void InitializeCoverage(bool enabled, const char *dir) { + if (coverage_enabled) + return; // May happen if two sanitizer enable coverage in the same process. + coverage_enabled = enabled; + coverage_dir = dir; + coverage_data.Init(); + if (enabled) coverage_data.Enable(); +#if !SANITIZER_WINDOWS + if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump); +#endif +} + +void ReInitializeCoverage(bool enabled, const char *dir) { + coverage_enabled = enabled; + coverage_dir = dir; + coverage_data.ReInit(); +} + +void CoverageUpdateMapping() { + if (coverage_enabled) + CovUpdateMapping(coverage_dir); +} + } // namespace __sanitizer extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u8 *guard) { +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) { coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()), guard); } +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) { + atomic_uint32_t *atomic_guard = reinterpret_cast(guard); + if (__sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) + __sanitizer_cov(guard); +} SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_indir_call16(uptr callee, uptr callee_cache16[]) { coverage_data.IndirCall(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()), callee, callee_cache16, 16); } -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() { + coverage_enabled = true; + coverage_dir = common_flags()->coverage_dir; coverage_data.Init(); } -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(uptr npcs) { - if (!common_flags()->coverage || !common_flags()->coverage_direct) return; - if (SANITIZER_ANDROID) { +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(s32 *guards, + uptr npcs) { + coverage_data.InitializeGuards(guards, npcs); + if (!common_flags()->coverage_direct) return; + if (SANITIZER_ANDROID && coverage_enabled) { // dlopen/dlclose interceptors do not work on Android, so we rely on // Extend() calls to update .sancov.map. - CovUpdateMapping(GET_CALLER_PC()); + CovUpdateMapping(coverage_dir, GET_CALLER_PC()); } coverage_data.Extend(npcs); } SANITIZER_INTERFACE_ATTRIBUTE sptr __sanitizer_maybe_open_cov_file(const char *name) { return MaybeOpenCovFile(name); } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_total_unique_coverage() { return atomic_load(&coverage_counter, memory_order_relaxed); } SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_cov_trace_func_enter(uptr *cache) { - coverage_data.TraceBasicaBlock(cache); +void __sanitizer_cov_trace_func_enter(s32 *id) { + coverage_data.TraceBasicBlock(id); } SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_cov_trace_basic_block(uptr *cache) { - coverage_data.TraceBasicaBlock(cache); +void __sanitizer_cov_trace_basic_block(s32 *id) { + coverage_data.TraceBasicBlock(id); +} +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_reset_coverage() { + coverage_data.ReinitializeGuards(); + internal_bzero_aligned16( + coverage_data.data(), + RoundUpTo(coverage_data.size() * sizeof(coverage_data.data()[0]), 16)); +} +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_coverage_guards(uptr **data) { + *data = coverage_data.data(); + return coverage_data.size(); } } // extern "C" Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc (revision 279191) @@ -1,125 +1,130 @@ //===-- sanitizer_coverage_mapping.cc -------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Mmap-based implementation of sanitizer coverage. // // This is part of the implementation of code coverage that does not require // __sanitizer_cov_dump() call. Data is stored in 2 files per process. // // $pid.sancov.map describes process memory layout in the following text-based // format: // // 1 line, 32 or 64 // // repeated // ... // Mapping lines are NOT sorted. This file is updated every time memory layout // is changed (i.e. in dlopen() and dlclose() interceptors). // // $pid.sancov.raw is a binary dump of PC values, sizeof(uptr) each. Again, not // sorted. This file is extended by 64Kb at a time and mapped into memory. It // contains one or more 0 words at the end, up to the next 64Kb aligned offset. // // To convert these 2 files to the usual .sancov format, run sancov.py rawunpack // $pid.sancov.raw. // //===----------------------------------------------------------------------===// #include "sanitizer_allocator_internal.h" #include "sanitizer_libc.h" #include "sanitizer_procmaps.h" namespace __sanitizer { static const uptr kMaxNumberOfModules = 1 << 14; static const uptr kMaxTextSize = 64 * 1024; struct CachedMapping { public: bool NeedsUpdate(uptr pc) { int new_pid = internal_getpid(); if (last_pid == new_pid && pc && pc >= last_range_start && pc < last_range_end) return false; last_pid = new_pid; return true; } void SetModuleRange(uptr start, uptr end) { last_range_start = start; last_range_end = end; } private: uptr last_range_start, last_range_end; int last_pid; }; static CachedMapping cached_mapping; static StaticSpinMutex mapping_mu; -void CovUpdateMapping(uptr caller_pc) { - if (!common_flags()->coverage || !common_flags()->coverage_direct) return; +void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) { + if (!common_flags()->coverage_direct) return; SpinMutexLock l(&mapping_mu); if (!cached_mapping.NeedsUpdate(caller_pc)) return; InternalScopedString text(kMaxTextSize); - InternalScopedBuffer modules(kMaxNumberOfModules); - CHECK(modules.data()); - int n_modules = GetListOfModules(modules.data(), kMaxNumberOfModules, - /* filter */ 0); - text.append("%d\n", sizeof(uptr) * 8); - for (int i = 0; i < n_modules; ++i) { - const char *module_name = StripModuleName(modules[i].full_name()); - for (unsigned j = 0; j < modules[i].n_ranges(); ++j) { - if (modules[i].address_range_executable(j)) { - uptr start = modules[i].address_range_start(j); - uptr end = modules[i].address_range_end(j); - uptr base = modules[i].base_address(); - text.append("%zx %zx %zx %s\n", start, end, base, module_name); - if (caller_pc && caller_pc >= start && caller_pc < end) - cached_mapping.SetModuleRange(start, end); + { + InternalScopedBuffer modules(kMaxNumberOfModules); + CHECK(modules.data()); + int n_modules = GetListOfModules(modules.data(), kMaxNumberOfModules, + /* filter */ 0); + + text.append("%d\n", sizeof(uptr) * 8); + for (int i = 0; i < n_modules; ++i) { + const char *module_name = StripModuleName(modules[i].full_name()); + uptr base = modules[i].base_address(); + for (auto iter = modules[i].ranges(); iter.hasNext();) { + const auto *range = iter.next(); + if (range->executable) { + uptr start = range->beg; + uptr end = range->end; + text.append("%zx %zx %zx %s\n", start, end, base, module_name); + if (caller_pc && caller_pc >= start && caller_pc < end) + cached_mapping.SetModuleRange(start, end); + } } + modules[i].clear(); } } int err; - InternalScopedString tmp_path(64 + - internal_strlen(common_flags()->coverage_dir)); + InternalScopedString tmp_path(64 + internal_strlen(coverage_dir)); uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(), - "%s/%zd.sancov.map.tmp", common_flags()->coverage_dir, - internal_getpid()); + "%s/%zd.sancov.map.tmp", coverage_dir, + internal_getpid()); CHECK_LE(res, tmp_path.size()); uptr map_fd = OpenFile(tmp_path.data(), true); - if (internal_iserror(map_fd)) { - Report(" Coverage: failed to open %s for writing\n", tmp_path.data()); + if (internal_iserror(map_fd, &err)) { + Report(" Coverage: failed to open %s for writing: %d\n", tmp_path.data(), + err); Die(); } res = internal_write(map_fd, text.data(), text.length()); if (internal_iserror(res, &err)) { Printf("sancov.map write failed: %d\n", err); Die(); } internal_close(map_fd); - InternalScopedString path(64 + internal_strlen(common_flags()->coverage_dir)); + InternalScopedString path(64 + internal_strlen(coverage_dir)); res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map", - common_flags()->coverage_dir, internal_getpid()); + coverage_dir, internal_getpid()); CHECK_LE(res, path.size()); res = internal_rename(tmp_path.data(), path.data()); if (internal_iserror(res, &err)) { Printf("sancov.map rename failed: %d\n", err); Die(); } } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_deadlock_detector.h (revision 279191) @@ -1,412 +1,416 @@ //===-- sanitizer_deadlock_detector.h ---------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of Sanitizer runtime. // The deadlock detector maintains a directed graph of lock acquisitions. // When a lock event happens, the detector checks if the locks already held by // the current thread are reachable from the newly acquired lock. // // The detector can handle only a fixed amount of simultaneously live locks // (a lock is alive if it has been locked at least once and has not been // destroyed). When the maximal number of locks is reached the entire graph // is flushed and the new lock epoch is started. The node ids from the old // epochs can not be used with any of the detector methods except for // nodeBelongsToCurrentEpoch(). // // FIXME: this is work in progress, nothing really works yet. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_DEADLOCK_DETECTOR_H #define SANITIZER_DEADLOCK_DETECTOR_H #include "sanitizer_common.h" #include "sanitizer_bvgraph.h" namespace __sanitizer { // Thread-local state for DeadlockDetector. // It contains the locks currently held by the owning thread. template class DeadlockDetectorTLS { public: // No CTOR. void clear() { bv_.clear(); epoch_ = 0; n_recursive_locks = 0; n_all_locks_ = 0; } bool empty() const { return bv_.empty(); } void ensureCurrentEpoch(uptr current_epoch) { if (epoch_ == current_epoch) return; bv_.clear(); epoch_ = current_epoch; + n_recursive_locks = 0; + n_all_locks_ = 0; } uptr getEpoch() const { return epoch_; } // Returns true if this is the first (non-recursive) acquisition of this lock. bool addLock(uptr lock_id, uptr current_epoch, u32 stk) { // Printf("addLock: %zx %zx stk %u\n", lock_id, current_epoch, stk); CHECK_EQ(epoch_, current_epoch); if (!bv_.setBit(lock_id)) { // The lock is already held by this thread, it must be recursive. CHECK_LT(n_recursive_locks, ARRAY_SIZE(recursive_locks)); recursive_locks[n_recursive_locks++] = lock_id; return false; } CHECK_LT(n_all_locks_, ARRAY_SIZE(all_locks_with_contexts_)); // lock_id < BV::kSize, can cast to a smaller int. u32 lock_id_short = static_cast(lock_id); LockWithContext l = {lock_id_short, stk}; all_locks_with_contexts_[n_all_locks_++] = l; return true; } void removeLock(uptr lock_id) { if (n_recursive_locks) { for (sptr i = n_recursive_locks - 1; i >= 0; i--) { if (recursive_locks[i] == lock_id) { n_recursive_locks--; Swap(recursive_locks[i], recursive_locks[n_recursive_locks]); return; } } } // Printf("remLock: %zx %zx\n", lock_id, epoch_); - CHECK(bv_.clearBit(lock_id)); + if (!bv_.clearBit(lock_id)) + return; // probably addLock happened before flush if (n_all_locks_) { for (sptr i = n_all_locks_ - 1; i >= 0; i--) { if (all_locks_with_contexts_[i].lock == static_cast(lock_id)) { Swap(all_locks_with_contexts_[i], all_locks_with_contexts_[n_all_locks_ - 1]); n_all_locks_--; break; } } } } u32 findLockContext(uptr lock_id) { for (uptr i = 0; i < n_all_locks_; i++) if (all_locks_with_contexts_[i].lock == static_cast(lock_id)) return all_locks_with_contexts_[i].stk; return 0; } const BV &getLocks(uptr current_epoch) const { CHECK_EQ(epoch_, current_epoch); return bv_; } uptr getNumLocks() const { return n_all_locks_; } uptr getLock(uptr idx) const { return all_locks_with_contexts_[idx].lock; } private: BV bv_; uptr epoch_; uptr recursive_locks[64]; uptr n_recursive_locks; struct LockWithContext { u32 lock; u32 stk; }; LockWithContext all_locks_with_contexts_[64]; uptr n_all_locks_; }; // DeadlockDetector. // For deadlock detection to work we need one global DeadlockDetector object // and one DeadlockDetectorTLS object per evey thread. // This class is not thread safe, all concurrent accesses should be guarded // by an external lock. // Most of the methods of this class are not thread-safe (i.e. should // be protected by an external lock) unless explicitly told otherwise. template class DeadlockDetector { public: typedef BV BitVector; uptr size() const { return g_.size(); } // No CTOR. void clear() { current_epoch_ = 0; available_nodes_.clear(); recycled_nodes_.clear(); g_.clear(); n_edges_ = 0; } // Allocate new deadlock detector node. // If we are out of available nodes first try to recycle some. // If there is nothing to recycle, flush the graph and increment the epoch. // Associate 'data' (opaque user's object) with the new node. uptr newNode(uptr data) { if (!available_nodes_.empty()) return getAvailableNode(data); if (!recycled_nodes_.empty()) { // Printf("recycling: n_edges_ %zd\n", n_edges_); for (sptr i = n_edges_ - 1; i >= 0; i--) { if (recycled_nodes_.getBit(edges_[i].from) || recycled_nodes_.getBit(edges_[i].to)) { Swap(edges_[i], edges_[n_edges_ - 1]); n_edges_--; } } CHECK(available_nodes_.empty()); // removeEdgesFrom was called in removeNode. g_.removeEdgesTo(recycled_nodes_); available_nodes_.setUnion(recycled_nodes_); recycled_nodes_.clear(); return getAvailableNode(data); } // We are out of vacant nodes. Flush and increment the current_epoch_. current_epoch_ += size(); recycled_nodes_.clear(); available_nodes_.setAll(); g_.clear(); + n_edges_ = 0; return getAvailableNode(data); } // Get data associated with the node created by newNode(). uptr getData(uptr node) const { return data_[nodeToIndex(node)]; } bool nodeBelongsToCurrentEpoch(uptr node) { return node && (node / size() * size()) == current_epoch_; } void removeNode(uptr node) { uptr idx = nodeToIndex(node); CHECK(!available_nodes_.getBit(idx)); CHECK(recycled_nodes_.setBit(idx)); g_.removeEdgesFrom(idx); } void ensureCurrentEpoch(DeadlockDetectorTLS *dtls) { dtls->ensureCurrentEpoch(current_epoch_); } // Returns true if there is a cycle in the graph after this lock event. // Ideally should be called before the lock is acquired so that we can // report a deadlock before a real deadlock happens. bool onLockBefore(DeadlockDetectorTLS *dtls, uptr cur_node) { ensureCurrentEpoch(dtls); uptr cur_idx = nodeToIndex(cur_node); return g_.isReachable(cur_idx, dtls->getLocks(current_epoch_)); } u32 findLockContext(DeadlockDetectorTLS *dtls, uptr node) { return dtls->findLockContext(nodeToIndex(node)); } // Add cur_node to the set of locks held currently by dtls. void onLockAfter(DeadlockDetectorTLS *dtls, uptr cur_node, u32 stk = 0) { ensureCurrentEpoch(dtls); uptr cur_idx = nodeToIndex(cur_node); dtls->addLock(cur_idx, current_epoch_, stk); } // Experimental *racy* fast path function. // Returns true if all edges from the currently held locks to cur_node exist. bool hasAllEdges(DeadlockDetectorTLS *dtls, uptr cur_node) { uptr local_epoch = dtls->getEpoch(); // Read from current_epoch_ is racy. if (cur_node && local_epoch == current_epoch_ && local_epoch == nodeToEpoch(cur_node)) { uptr cur_idx = nodeToIndexUnchecked(cur_node); for (uptr i = 0, n = dtls->getNumLocks(); i < n; i++) { if (!g_.hasEdge(dtls->getLock(i), cur_idx)) return false; } return true; } return false; } // Adds edges from currently held locks to cur_node, // returns the number of added edges, and puts the sources of added edges // into added_edges[]. // Should be called before onLockAfter. uptr addEdges(DeadlockDetectorTLS *dtls, uptr cur_node, u32 stk, int unique_tid) { ensureCurrentEpoch(dtls); uptr cur_idx = nodeToIndex(cur_node); uptr added_edges[40]; uptr n_added_edges = g_.addEdges(dtls->getLocks(current_epoch_), cur_idx, added_edges, ARRAY_SIZE(added_edges)); for (uptr i = 0; i < n_added_edges; i++) { if (n_edges_ < ARRAY_SIZE(edges_)) { Edge e = {(u16)added_edges[i], (u16)cur_idx, dtls->findLockContext(added_edges[i]), stk, unique_tid}; edges_[n_edges_++] = e; } // Printf("Edge%zd: %u %zd=>%zd in T%d\n", // n_edges_, stk, added_edges[i], cur_idx, unique_tid); } return n_added_edges; } bool findEdge(uptr from_node, uptr to_node, u32 *stk_from, u32 *stk_to, int *unique_tid) { uptr from_idx = nodeToIndex(from_node); uptr to_idx = nodeToIndex(to_node); for (uptr i = 0; i < n_edges_; i++) { if (edges_[i].from == from_idx && edges_[i].to == to_idx) { *stk_from = edges_[i].stk_from; *stk_to = edges_[i].stk_to; *unique_tid = edges_[i].unique_tid; return true; } } return false; } // Test-only function. Handles the before/after lock events, // returns true if there is a cycle. bool onLock(DeadlockDetectorTLS *dtls, uptr cur_node, u32 stk = 0) { ensureCurrentEpoch(dtls); bool is_reachable = !isHeld(dtls, cur_node) && onLockBefore(dtls, cur_node); addEdges(dtls, cur_node, stk, 0); onLockAfter(dtls, cur_node, stk); return is_reachable; } // Handles the try_lock event, returns false. // When a try_lock event happens (i.e. a try_lock call succeeds) we need // to add this lock to the currently held locks, but we should not try to // change the lock graph or to detect a cycle. We may want to investigate // whether a more aggressive strategy is possible for try_lock. bool onTryLock(DeadlockDetectorTLS *dtls, uptr cur_node, u32 stk = 0) { ensureCurrentEpoch(dtls); uptr cur_idx = nodeToIndex(cur_node); dtls->addLock(cur_idx, current_epoch_, stk); return false; } // Returns true iff dtls is empty (no locks are currently held) and we can // add the node to the currently held locks w/o chanding the global state. // This operation is thread-safe as it only touches the dtls. bool onFirstLock(DeadlockDetectorTLS *dtls, uptr node, u32 stk = 0) { if (!dtls->empty()) return false; if (dtls->getEpoch() && dtls->getEpoch() == nodeToEpoch(node)) { dtls->addLock(nodeToIndexUnchecked(node), nodeToEpoch(node), stk); return true; } return false; } // Finds a path between the lock 'cur_node' (currently not held in dtls) // and some currently held lock, returns the length of the path // or 0 on failure. uptr findPathToLock(DeadlockDetectorTLS *dtls, uptr cur_node, uptr *path, uptr path_size) { tmp_bv_.copyFrom(dtls->getLocks(current_epoch_)); uptr idx = nodeToIndex(cur_node); CHECK(!tmp_bv_.getBit(idx)); uptr res = g_.findShortestPath(idx, tmp_bv_, path, path_size); for (uptr i = 0; i < res; i++) path[i] = indexToNode(path[i]); if (res) CHECK_EQ(path[0], cur_node); return res; } // Handle the unlock event. // This operation is thread-safe as it only touches the dtls. void onUnlock(DeadlockDetectorTLS *dtls, uptr node) { if (dtls->getEpoch() == nodeToEpoch(node)) dtls->removeLock(nodeToIndexUnchecked(node)); } // Tries to handle the lock event w/o writing to global state. // Returns true on success. // This operation is thread-safe as it only touches the dtls // (modulo racy nature of hasAllEdges). bool onLockFast(DeadlockDetectorTLS *dtls, uptr node, u32 stk = 0) { if (hasAllEdges(dtls, node)) { dtls->addLock(nodeToIndexUnchecked(node), nodeToEpoch(node), stk); return true; } return false; } bool isHeld(DeadlockDetectorTLS *dtls, uptr node) const { return dtls->getLocks(current_epoch_).getBit(nodeToIndex(node)); } uptr testOnlyGetEpoch() const { return current_epoch_; } bool testOnlyHasEdge(uptr l1, uptr l2) { return g_.hasEdge(nodeToIndex(l1), nodeToIndex(l2)); } // idx1 and idx2 are raw indices to g_, not lock IDs. bool testOnlyHasEdgeRaw(uptr idx1, uptr idx2) { return g_.hasEdge(idx1, idx2); } void Print() { for (uptr from = 0; from < size(); from++) for (uptr to = 0; to < size(); to++) if (g_.hasEdge(from, to)) Printf(" %zx => %zx\n", from, to); } private: void check_idx(uptr idx) const { CHECK_LT(idx, size()); } void check_node(uptr node) const { CHECK_GE(node, size()); CHECK_EQ(current_epoch_, nodeToEpoch(node)); } uptr indexToNode(uptr idx) const { check_idx(idx); return idx + current_epoch_; } uptr nodeToIndexUnchecked(uptr node) const { return node % size(); } uptr nodeToIndex(uptr node) const { check_node(node); return nodeToIndexUnchecked(node); } uptr nodeToEpoch(uptr node) const { return node / size() * size(); } uptr getAvailableNode(uptr data) { uptr idx = available_nodes_.getAndClearFirstOne(); data_[idx] = data; return indexToNode(idx); } struct Edge { u16 from; u16 to; u32 stk_from; u32 stk_to; int unique_tid; }; uptr current_epoch_; BV available_nodes_; BV recycled_nodes_; BV tmp_bv_; BVGraph g_; uptr data_[BV::kSize]; Edge edges_[BV::kSize * 32]; uptr n_edges_; }; } // namespace __sanitizer #endif // SANITIZER_DEADLOCK_DETECTOR_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.cc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.cc (revision 279191) @@ -0,0 +1,153 @@ +//===-- sanitizer_flag_parser.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_flag_parser.h" + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_flags.h" +#include "sanitizer_flag_parser.h" + +namespace __sanitizer { + +LowLevelAllocator FlagParser::Alloc; + +class UnknownFlags { + static const int kMaxUnknownFlags = 20; + const char *unknown_flags_[kMaxUnknownFlags]; + int n_unknown_flags_; + + public: + void Add(const char *name) { + CHECK_LT(n_unknown_flags_, kMaxUnknownFlags); + unknown_flags_[n_unknown_flags_++] = name; + } + + void Report() { + if (!n_unknown_flags_) return; + Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_); + for (int i = 0; i < n_unknown_flags_; ++i) + Printf(" %s\n", unknown_flags_[i]); + n_unknown_flags_ = 0; + } +}; + +UnknownFlags unknown_flags; + +void ReportUnrecognizedFlags() { + unknown_flags.Report(); +} + +char *FlagParser::ll_strndup(const char *s, uptr n) { + uptr len = internal_strnlen(s, n); + char *s2 = (char*)Alloc.Allocate(len + 1); + internal_memcpy(s2, s, len); + s2[len] = 0; + return s2; +} + +void FlagParser::PrintFlagDescriptions() { + Printf("Available flags for %s:\n", SanitizerToolName); + for (int i = 0; i < n_flags_; ++i) + Printf("\t%s\n\t\t- %s\n", flags_[i].name, flags_[i].desc); +} + +void FlagParser::fatal_error(const char *err) { + Printf("ERROR: %s\n", err); + Die(); +} + +bool FlagParser::is_space(char c) { + return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' || + c == '\r'; +} + +void FlagParser::skip_whitespace() { + while (is_space(buf_[pos_])) ++pos_; +} + +void FlagParser::parse_flag() { + uptr name_start = pos_; + while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_; + if (buf_[pos_] != '=') fatal_error("expected '='"); + char *name = ll_strndup(buf_ + name_start, pos_ - name_start); + + uptr value_start = ++pos_; + char *value; + if (buf_[pos_] == '\'' || buf_[pos_] == '"') { + char quote = buf_[pos_++]; + while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_; + if (buf_[pos_] == 0) fatal_error("unterminated string"); + value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1); + ++pos_; // consume the closing quote + } else { + while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_; + if (buf_[pos_] != 0 && !is_space(buf_[pos_])) + fatal_error("expected separator or eol"); + value = ll_strndup(buf_ + value_start, pos_ - value_start); + } + + bool res = run_handler(name, value); + if (!res) fatal_error("Flag parsing failed."); +} + +void FlagParser::parse_flags() { + while (true) { + skip_whitespace(); + if (buf_[pos_] == 0) break; + parse_flag(); + } + + // Do a sanity check for certain flags. + if (common_flags_dont_use.malloc_context_size < 1) + common_flags_dont_use.malloc_context_size = 1; +} + +void FlagParser::ParseString(const char *s) { + if (!s) return; + // Backup current parser state to allow nested ParseString() calls. + const char *old_buf_ = buf_; + uptr old_pos_ = pos_; + buf_ = s; + pos_ = 0; + + parse_flags(); + + buf_ = old_buf_; + pos_ = old_pos_; +} + +bool FlagParser::run_handler(const char *name, const char *value) { + for (int i = 0; i < n_flags_; ++i) { + if (internal_strcmp(name, flags_[i].name) == 0) + return flags_[i].handler->Parse(value); + } + // Unrecognized flag. This is not a fatal error, we may print a warning later. + unknown_flags.Add(name); + return true; +} + +void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler, + const char *desc) { + CHECK_LT(n_flags_, kMaxFlags); + flags_[n_flags_].name = name; + flags_[n_flags_].desc = desc; + flags_[n_flags_].handler = handler; + ++n_flags_; +} + +FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) { + flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags); +} + +} // namespace __sanitizer Property changes on: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.h (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.h (revision 279191) @@ -0,0 +1,121 @@ +//===-- sanitizer_flag_parser.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_FLAG_REGISTRY_H +#define SANITIZER_FLAG_REGISTRY_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_common.h" + +namespace __sanitizer { + +class FlagHandlerBase { + public: + virtual bool Parse(const char *value) { return false; } +}; + +template +class FlagHandler : public FlagHandlerBase { + T *t_; + + public: + explicit FlagHandler(T *t) : t_(t) {} + bool Parse(const char *value); +}; + +template <> +inline bool FlagHandler::Parse(const char *value) { + if (internal_strcmp(value, "0") == 0 || + internal_strcmp(value, "no") == 0 || + internal_strcmp(value, "false") == 0) { + *t_ = false; + return true; + } + if (internal_strcmp(value, "1") == 0 || + internal_strcmp(value, "yes") == 0 || + internal_strcmp(value, "true") == 0) { + *t_ = true; + return true; + } + Printf("ERROR: Invalid value for bool option: '%s'\n", value); + return false; +} + +template <> +inline bool FlagHandler::Parse(const char *value) { + *t_ = internal_strdup(value); + return true; +} + +template <> +inline bool FlagHandler::Parse(const char *value) { + char *value_end; + *t_ = internal_simple_strtoll(value, &value_end, 10); + bool ok = *value_end == 0; + if (!ok) Printf("ERROR: Invalid value for int option: '%s'\n", value); + return ok; +} + +template <> +inline bool FlagHandler::Parse(const char *value) { + char *value_end; + *t_ = internal_simple_strtoll(value, &value_end, 10); + bool ok = *value_end == 0; + if (!ok) Printf("ERROR: Invalid value for uptr option: '%s'\n", value); + return ok; +} + +class FlagParser { + static const int kMaxFlags = 200; + struct Flag { + const char *name; + const char *desc; + FlagHandlerBase *handler; + } *flags_; + int n_flags_; + + const char *buf_; + uptr pos_; + + public: + FlagParser(); + void RegisterHandler(const char *name, FlagHandlerBase *handler, + const char *desc); + void ParseString(const char *s); + void PrintFlagDescriptions(); + + static LowLevelAllocator Alloc; + + private: + void fatal_error(const char *err); + bool is_space(char c); + void skip_whitespace(); + void parse_flags(); + void parse_flag(); + bool run_handler(const char *name, const char *value); + char *ll_strndup(const char *s, uptr n); +}; + +template +static void RegisterFlag(FlagParser *parser, const char *name, const char *desc, + T *var) { + FlagHandler *fh = new (FlagParser::Alloc) FlagHandler(var); // NOLINT + parser->RegisterHandler(name, fh, desc); +} + +void ReportUnrecognizedFlags(); + +} // namespace __sanitizer + +#endif // SANITIZER_FLAG_REGISTRY_H Property changes on: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flag_parser.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.cc (revision 279191) @@ -1,307 +1,87 @@ //===-- sanitizer_flags.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer/AddressSanitizer runtime. // //===----------------------------------------------------------------------===// #include "sanitizer_flags.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_list.h" +#include "sanitizer_flag_parser.h" namespace __sanitizer { CommonFlags common_flags_dont_use; struct FlagDescription { const char *name; const char *description; FlagDescription *next; }; IntrusiveList flag_descriptions; // If set, the tool will install its own SEGV signal handler by default. #ifndef SANITIZER_NEEDS_SEGV # define SANITIZER_NEEDS_SEGV 1 #endif -void SetCommonFlagsDefaults(CommonFlags *f) { - f->symbolize = true; - f->external_symbolizer_path = 0; - f->allow_addr2line = false; - f->strip_path_prefix = ""; - f->fast_unwind_on_check = false; - f->fast_unwind_on_fatal = false; - f->fast_unwind_on_malloc = true; - f->handle_ioctl = false; - f->malloc_context_size = 1; - f->log_path = "stderr"; - f->verbosity = 0; - f->detect_leaks = true; - f->leak_check_at_exit = true; - f->allocator_may_return_null = false; - f->print_summary = true; - f->check_printf = true; - // TODO(glider): tools may want to set different defaults for handle_segv. - f->handle_segv = SANITIZER_NEEDS_SEGV; - f->allow_user_segv_handler = false; - f->use_sigaltstack = true; - f->detect_deadlocks = false; - f->clear_shadow_mmap_threshold = 64 * 1024; - f->color = "auto"; - f->legacy_pthread_cond = false; - f->intercept_tls_get_addr = false; - f->coverage = false; - f->coverage_direct = SANITIZER_ANDROID; - f->coverage_dir = "."; - f->full_address_space = false; - f->suppressions = ""; - f->print_suppressions = true; - f->disable_coredump = (SANITIZER_WORDSIZE == 64); - f->symbolize_inline_frames = true; - f->stack_trace_format = "DEFAULT"; +void CommonFlags::SetDefaults() { +#define COMMON_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "sanitizer_flags.inc" +#undef COMMON_FLAG } -void ParseCommonFlagsFromString(CommonFlags *f, const char *str) { - ParseFlag(str, &f->symbolize, "symbolize", - "If set, use the online symbolizer from common sanitizer runtime to turn " - "virtual addresses to file/line locations."); - ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path", - "Path to external symbolizer. If empty, the tool will search $PATH for " - "the symbolizer."); - ParseFlag(str, &f->allow_addr2line, "allow_addr2line", - "If set, allows online symbolizer to run addr2line binary to symbolize " - "stack traces (addr2line will only be used if llvm-symbolizer binary is " - "unavailable."); - ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix", - "Strips this prefix from file paths in error reports."); - ParseFlag(str, &f->fast_unwind_on_check, "fast_unwind_on_check", - "If available, use the fast frame-pointer-based unwinder on " - "internal CHECK failures."); - ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal", - "If available, use the fast frame-pointer-based unwinder on fatal " - "errors."); - ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc", - "If available, use the fast frame-pointer-based unwinder on " - "malloc/free."); - ParseFlag(str, &f->handle_ioctl, "handle_ioctl", - "Intercept and handle ioctl requests."); - ParseFlag(str, &f->malloc_context_size, "malloc_context_size", - "Max number of stack frames kept for each allocation/deallocation."); - ParseFlag(str, &f->log_path, "log_path", - "Write logs to \"log_path.pid\". The special values are \"stdout\" and " - "\"stderr\". The default is \"stderr\"."); - ParseFlag(str, &f->verbosity, "verbosity", - "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output)."); - ParseFlag(str, &f->detect_leaks, "detect_leaks", - "Enable memory leak detection."); - ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit", - "Invoke leak checking in an atexit handler. Has no effect if " - "detect_leaks=false, or if __lsan_do_leak_check() is called before the " - "handler has a chance to run."); - ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null", - "If false, the allocator will crash instead of returning 0 on " - "out-of-memory."); - ParseFlag(str, &f->print_summary, "print_summary", - "If false, disable printing error summaries in addition to error " - "reports."); - ParseFlag(str, &f->check_printf, "check_printf", - "Check printf arguments."); - ParseFlag(str, &f->handle_segv, "handle_segv", - "If set, registers the tool's custom SEGV handler (both SIGBUS and " - "SIGSEGV on OSX)."); - ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler", - "If set, allows user to register a SEGV handler even if the tool " - "registers one."); - ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack", - "If set, uses alternate stack for signal handling."); - ParseFlag(str, &f->detect_deadlocks, "detect_deadlocks", - "If set, deadlock detection is enabled."); - ParseFlag(str, &f->clear_shadow_mmap_threshold, - "clear_shadow_mmap_threshold", - "Large shadow regions are zero-filled using mmap(NORESERVE) instead of " - "memset(). This is the threshold size in bytes."); - ParseFlag(str, &f->color, "color", - "Colorize reports: (always|never|auto)."); - ParseFlag(str, &f->legacy_pthread_cond, "legacy_pthread_cond", - "Enables support for dynamic libraries linked with libpthread 2.2.5."); - ParseFlag(str, &f->intercept_tls_get_addr, "intercept_tls_get_addr", - "Intercept __tls_get_addr."); - ParseFlag(str, &f->help, "help", "Print the flag descriptions."); - ParseFlag(str, &f->mmap_limit_mb, "mmap_limit_mb", - "Limit the amount of mmap-ed memory (excluding shadow) in Mb; " - "not a user-facing flag, used mosly for testing the tools"); - ParseFlag(str, &f->coverage, "coverage", - "If set, coverage information will be dumped at program shutdown (if the " - "coverage instrumentation was enabled at compile time)."); - ParseFlag(str, &f->coverage_direct, "coverage_direct", - "If set, coverage information will be dumped directly to a memory " - "mapped file. This way data is not lost even if the process is " - "suddenly killed."); - ParseFlag(str, &f->coverage_dir, "coverage_dir", - "Target directory for coverage dumps. Defaults to the current " - "directory."); - ParseFlag(str, &f->full_address_space, "full_address_space", - "Sanitize complete address space; " - "by default kernel area on 32-bit platforms will not be sanitized"); - ParseFlag(str, &f->suppressions, "suppressions", "Suppressions file name."); - ParseFlag(str, &f->print_suppressions, "print_suppressions", - "Print matched suppressions at exit."); - ParseFlag(str, &f->disable_coredump, "disable_coredump", - "Disable core dumping. By default, disable_core=1 on 64-bit to avoid " - "dumping a 16T+ core file. Ignored on OSes that don't dump core by" - "default and for sanitizers that don't reserve lots of virtual memory."); - ParseFlag(str, &f->symbolize_inline_frames, "symbolize_inline_frames", - "Print inlined frames in stacktraces. Defaults to true."); - ParseFlag(str, &f->stack_trace_format, "stack_trace_format", - "Format string used to render stack frames. " - "See sanitizer_stacktrace_printer.h for the format description. " - "Use DEFAULT to get default format."); - - // Do a sanity check for certain flags. - if (f->malloc_context_size < 1) - f->malloc_context_size = 1; +void CommonFlags::CopyFrom(const CommonFlags &other) { + internal_memcpy(this, &other, sizeof(*this)); } -static bool GetFlagValue(const char *env, const char *name, - const char **value, int *value_length) { - if (env == 0) - return false; - const char *pos = 0; - for (;;) { - pos = internal_strstr(env, name); - if (pos == 0) +class FlagHandlerInclude : public FlagHandlerBase { + static const uptr kMaxIncludeSize = 1 << 15; + FlagParser *parser_; + + public: + explicit FlagHandlerInclude(FlagParser *parser) : parser_(parser) {} + bool Parse(const char *value) { + char *data; + uptr data_mapped_size; + int err; + uptr len = + ReadFileToBuffer(value, &data, &data_mapped_size, + Max(kMaxIncludeSize, GetPageSizeCached()), &err); + if (!len) { + Printf("Failed to read options from '%s': error %d\n", value, err); return false; - const char *name_end = pos + internal_strlen(name); - if ((pos != env && - ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) || - *name_end != '=') { - // Seems to be middle of another flag name or value. - env = pos + 1; - continue; } - pos = name_end; - break; + parser_->ParseString(data); + UnmapOrDie(data, data_mapped_size); + return true; } - const char *end; - if (pos[0] != '=') { - end = pos; - } else { - pos += 1; - if (pos[0] == '"') { - pos += 1; - end = internal_strchr(pos, '"'); - } else if (pos[0] == '\'') { - pos += 1; - end = internal_strchr(pos, '\''); - } else { - // Read until the next space or colon. - end = pos + internal_strcspn(pos, " :"); - } - if (end == 0) - end = pos + internal_strlen(pos); - } - *value = pos; - *value_length = end - pos; - return true; -} +}; -static bool StartsWith(const char *flag, int flag_length, const char *value) { - if (!flag || !value) - return false; - int value_length = internal_strlen(value); - return (flag_length >= value_length) && - (0 == internal_strncmp(flag, value, value_length)); +void RegisterIncludeFlag(FlagParser *parser, CommonFlags *cf) { + FlagHandlerInclude *fh_include = + new (FlagParser::Alloc) FlagHandlerInclude(parser); // NOLINT + parser->RegisterHandler("include", fh_include, + "read more options from the given file"); } -static LowLevelAllocator allocator_for_flags; +void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) { +#define COMMON_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &cf->Name); +#include "sanitizer_flags.inc" +#undef COMMON_FLAG -// The linear scan is suboptimal, but the number of flags is relatively small. -bool FlagInDescriptionList(const char *name) { - IntrusiveList::Iterator it(&flag_descriptions); - while (it.hasNext()) { - if (!internal_strcmp(it.next()->name, name)) return true; - } - return false; -} - -void AddFlagDescription(const char *name, const char *description) { - if (FlagInDescriptionList(name)) return; - FlagDescription *new_description = new(allocator_for_flags) FlagDescription; - new_description->name = name; - new_description->description = description; - flag_descriptions.push_back(new_description); -} - -// TODO(glider): put the descriptions inside CommonFlags. -void PrintFlagDescriptions() { - IntrusiveList::Iterator it(&flag_descriptions); - Printf("Available flags for %s:\n", SanitizerToolName); - while (it.hasNext()) { - FlagDescription *descr = it.next(); - Printf("\t%s\n\t\t- %s\n", descr->name, descr->description); - } -} - -void ParseFlag(const char *env, bool *flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - if (StartsWith(value, value_length, "0") || - StartsWith(value, value_length, "no") || - StartsWith(value, value_length, "false")) - *flag = false; - if (StartsWith(value, value_length, "1") || - StartsWith(value, value_length, "yes") || - StartsWith(value, value_length, "true")) - *flag = true; -} - -void ParseFlag(const char *env, int *flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - *flag = static_cast(internal_atoll(value)); -} - -void ParseFlag(const char *env, uptr *flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - *flag = static_cast(internal_atoll(value)); -} - -void ParseFlag(const char *env, const char **flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - // Copy the flag value. Don't use locks here, as flags are parsed at - // tool startup. - char *value_copy = (char*)(allocator_for_flags.Allocate(value_length + 1)); - internal_memcpy(value_copy, value, value_length); - value_copy[value_length] = '\0'; - *flag = value_copy; + RegisterIncludeFlag(parser, cf); } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.h (revision 279191) @@ -1,79 +1,55 @@ //===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer/AddressSanitizer runtime. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_FLAGS_H #define SANITIZER_FLAGS_H #include "sanitizer_internal_defs.h" namespace __sanitizer { -void ParseFlag(const char *env, bool *flag, - const char *name, const char *descr); -void ParseFlag(const char *env, int *flag, - const char *name, const char *descr); -void ParseFlag(const char *env, uptr *flag, - const char *name, const char *descr); -void ParseFlag(const char *env, const char **flag, - const char *name, const char *descr); - struct CommonFlags { - bool symbolize; - const char *external_symbolizer_path; - bool allow_addr2line; - const char *strip_path_prefix; - bool fast_unwind_on_check; - bool fast_unwind_on_fatal; - bool fast_unwind_on_malloc; - bool handle_ioctl; - int malloc_context_size; - const char *log_path; - int verbosity; - bool detect_leaks; - bool leak_check_at_exit; - bool allocator_may_return_null; - bool print_summary; - bool check_printf; - bool handle_segv; - bool allow_user_segv_handler; - bool use_sigaltstack; - bool detect_deadlocks; - uptr clear_shadow_mmap_threshold; - const char *color; - bool legacy_pthread_cond; - bool intercept_tls_get_addr; - bool help; - uptr mmap_limit_mb; - bool coverage; - bool coverage_direct; - const char *coverage_dir; - bool full_address_space; - const char *suppressions; - bool print_suppressions; - bool disable_coredump; - bool symbolize_inline_frames; - const char *stack_trace_format; +#define COMMON_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "sanitizer_flags.inc" +#undef COMMON_FLAG + + void SetDefaults(); + void CopyFrom(const CommonFlags &other); }; -inline CommonFlags *common_flags() { - extern CommonFlags common_flags_dont_use; +// Functions to get/set global CommonFlags shared by all sanitizer runtimes: +extern CommonFlags common_flags_dont_use; +inline const CommonFlags *common_flags() { return &common_flags_dont_use; } -void SetCommonFlagsDefaults(CommonFlags *f); -void ParseCommonFlagsFromString(CommonFlags *f, const char *str); -void PrintFlagDescriptions(); +inline void SetCommonFlagsDefaults() { + common_flags_dont_use.SetDefaults(); +} +// This function can only be used to setup tool-specific overrides for +// CommonFlags defaults. Generally, it should only be used right after +// SetCommonFlagsDefaults(), but before ParseCommonFlagsFromString(), and +// only during the flags initialization (i.e. before they are used for +// the first time). +inline void OverrideCommonFlags(const CommonFlags &cf) { + common_flags_dont_use.CopyFrom(cf); +} + +class FlagParser; +void RegisterCommonFlags(FlagParser *parser, + CommonFlags *cf = &common_flags_dont_use); +void RegisterIncludeFlag(FlagParser *parser, CommonFlags *cf); } // namespace __sanitizer #endif // SANITIZER_FLAGS_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc (revision 279191) @@ -0,0 +1,149 @@ +//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes common flags available in all sanitizers. +// +//===----------------------------------------------------------------------===// +#ifndef COMMON_FLAG +#error "Define COMMON_FLAG prior to including this file!" +#endif + +// COMMON_FLAG(Type, Name, DefaultValue, Description) +// Supported types: bool, const char *, int, uptr. +// Default value must be a compile-time constant. +// Description must be a string literal. + +COMMON_FLAG( + bool, symbolize, true, + "If set, use the online symbolizer from common sanitizer runtime to turn " + "virtual addresses to file/line locations.") +COMMON_FLAG( + const char *, external_symbolizer_path, 0, + "Path to external symbolizer. If empty, the tool will search $PATH for " + "the symbolizer.") +COMMON_FLAG( + bool, allow_addr2line, false, + "If set, allows online symbolizer to run addr2line binary to symbolize " + "stack traces (addr2line will only be used if llvm-symbolizer binary is " + "unavailable.") +COMMON_FLAG(const char *, strip_path_prefix, "", + "Strips this prefix from file paths in error reports.") +COMMON_FLAG(bool, fast_unwind_on_check, false, + "If available, use the fast frame-pointer-based unwinder on " + "internal CHECK failures.") +COMMON_FLAG(bool, fast_unwind_on_fatal, false, + "If available, use the fast frame-pointer-based unwinder on fatal " + "errors.") +COMMON_FLAG(bool, fast_unwind_on_malloc, true, + "If available, use the fast frame-pointer-based unwinder on " + "malloc/free.") +COMMON_FLAG(bool, handle_ioctl, false, "Intercept and handle ioctl requests.") +COMMON_FLAG(int, malloc_context_size, 1, + "Max number of stack frames kept for each allocation/deallocation.") +COMMON_FLAG( + const char *, log_path, "stderr", + "Write logs to \"log_path.pid\". The special values are \"stdout\" and " + "\"stderr\". The default is \"stderr\".") +COMMON_FLAG( + int, verbosity, 0, + "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).") +COMMON_FLAG(bool, detect_leaks, true, "Enable memory leak detection.") +COMMON_FLAG( + bool, leak_check_at_exit, true, + "Invoke leak checking in an atexit handler. Has no effect if " + "detect_leaks=false, or if __lsan_do_leak_check() is called before the " + "handler has a chance to run.") +COMMON_FLAG(bool, allocator_may_return_null, false, + "If false, the allocator will crash instead of returning 0 on " + "out-of-memory.") +COMMON_FLAG(bool, print_summary, true, + "If false, disable printing error summaries in addition to error " + "reports.") +COMMON_FLAG(bool, check_printf, true, "Check printf arguments.") +COMMON_FLAG(bool, handle_segv, SANITIZER_NEEDS_SEGV, + "If set, registers the tool's custom SEGV handler (both SIGBUS and " + "SIGSEGV on OSX).") +COMMON_FLAG(bool, allow_user_segv_handler, false, + "If set, allows user to register a SEGV handler even if the tool " + "registers one.") +COMMON_FLAG(bool, use_sigaltstack, true, + "If set, uses alternate stack for signal handling.") +COMMON_FLAG(bool, detect_deadlocks, false, + "If set, deadlock detection is enabled.") +COMMON_FLAG( + uptr, clear_shadow_mmap_threshold, 64 * 1024, + "Large shadow regions are zero-filled using mmap(NORESERVE) instead of " + "memset(). This is the threshold size in bytes.") +COMMON_FLAG(const char *, color, "auto", + "Colorize reports: (always|never|auto).") +COMMON_FLAG( + bool, legacy_pthread_cond, false, + "Enables support for dynamic libraries linked with libpthread 2.2.5.") +COMMON_FLAG(bool, intercept_tls_get_addr, false, "Intercept __tls_get_addr.") +COMMON_FLAG(bool, help, false, "Print the flag descriptions.") +COMMON_FLAG(uptr, mmap_limit_mb, 0, + "Limit the amount of mmap-ed memory (excluding shadow) in Mb; " + "not a user-facing flag, used mosly for testing the tools") +COMMON_FLAG(uptr, hard_rss_limit_mb, 0, + "Hard RSS limit in Mb." + " If non-zero, a background thread is spawned at startup" + " which periodically reads RSS and aborts the process if the" + " limit is reached") +COMMON_FLAG(uptr, soft_rss_limit_mb, 0, + "Soft RSS limit in Mb." + " If non-zero, a background thread is spawned at startup" + " which periodically reads RSS. If the limit is reached" + " all subsequent malloc/new calls will fail or return NULL" + " (depending on the value of allocator_may_return_null)" + " until the RSS goes below the soft limit." + " This limit does not affect memory allocations other than" + " malloc/new.") +COMMON_FLAG(bool, can_use_proc_maps_statm, true, + "If false, do not attempt to read /proc/maps/statm." + " Mostly useful for testing sanitizers.") +COMMON_FLAG( + bool, coverage, false, + "If set, coverage information will be dumped at program shutdown (if the " + "coverage instrumentation was enabled at compile time).") +// On by default, but works only if coverage == true. +COMMON_FLAG(bool, coverage_pcs, true, + "If set (and if 'coverage' is set too), the coverage information " + "will be dumped as a set of PC offsets for every module.") +COMMON_FLAG(bool, coverage_bitset, false, + "If set (and if 'coverage' is set too), the coverage information " + "will also be dumped as a bitset to a separate file.") +COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID, + "If set, coverage information will be dumped directly to a memory " + "mapped file. This way data is not lost even if the process is " + "suddenly killed.") +COMMON_FLAG(const char *, coverage_dir, ".", + "Target directory for coverage dumps. Defaults to the current " + "directory.") +COMMON_FLAG(bool, full_address_space, false, + "Sanitize complete address space; " + "by default kernel area on 32-bit platforms will not be sanitized") +COMMON_FLAG(const char *, suppressions, "", "Suppressions file name.") +COMMON_FLAG(bool, print_suppressions, true, + "Print matched suppressions at exit.") +COMMON_FLAG( + bool, disable_coredump, (SANITIZER_WORDSIZE == 64), + "Disable core dumping. By default, disable_core=1 on 64-bit to avoid " + "dumping a 16T+ core file. Ignored on OSes that don't dump core by" + "default and for sanitizers that don't reserve lots of virtual memory.") +COMMON_FLAG(bool, use_madv_dontdump, true, + "If set, instructs kernel to not store the (huge) shadow " + "in core file.") +COMMON_FLAG(bool, symbolize_inline_frames, true, + "Print inlined frames in stacktraces. Defaults to true.") +COMMON_FLAG(const char *, stack_trace_format, "DEFAULT", + "Format string used to render stack frames. " + "See sanitizer_stacktrace_printer.h for the format description. " + "Use DEFAULT to get default format.") +COMMON_FLAG(bool, no_huge_pages_for_shadow, true, + "If true, the shadow is not allowed to use huge pages. ") Property changes on: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h (revision 279191) @@ -1,328 +1,335 @@ //===-- sanitizer_internal_defs.h -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer. // It contains macro used in run-time libraries code. //===----------------------------------------------------------------------===// #ifndef SANITIZER_DEFS_H #define SANITIZER_DEFS_H #include "sanitizer_platform.h" +#ifndef SANITIZER_DEBUG +# define SANITIZER_DEBUG 0 +#endif + // Only use SANITIZER_*ATTRIBUTE* before the function return type! #if SANITIZER_WINDOWS # define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport) // FIXME find out what we need on Windows, if anything. # define SANITIZER_WEAK_ATTRIBUTE #elif defined(SANITIZER_GO) # define SANITIZER_INTERFACE_ATTRIBUTE # define SANITIZER_WEAK_ATTRIBUTE #else # define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) # define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) #endif #if SANITIZER_LINUX && !defined(SANITIZER_GO) # define SANITIZER_SUPPORTS_WEAK_HOOKS 1 #else # define SANITIZER_SUPPORTS_WEAK_HOOKS 0 #endif // We can use .preinit_array section on Linux to call sanitizer initialization // functions very early in the process startup (unless PIC macro is defined). // FIXME: do we have anything like this on Mac? #if SANITIZER_LINUX && !SANITIZER_ANDROID && !defined(PIC) # define SANITIZER_CAN_USE_PREINIT_ARRAY 1 #else # define SANITIZER_CAN_USE_PREINIT_ARRAY 0 #endif // GCC does not understand __has_feature #if !defined(__has_feature) # define __has_feature(x) 0 #endif -// Enable sanitizer compilation for pre-C++11 -#if __cplusplus < 201103L -#define nullptr 0 -#endif - // For portability reasons we do not include stddef.h, stdint.h or any other // system header, but we do need some basic types that are not defined // in a portable way by the language itself. namespace __sanitizer { #if defined(_WIN64) // 64-bit Windows uses LLP64 data model. typedef unsigned long long uptr; // NOLINT typedef signed long long sptr; // NOLINT #else typedef unsigned long uptr; // NOLINT typedef signed long sptr; // NOLINT #endif // defined(_WIN64) #if defined(__x86_64__) // Since x32 uses ILP32 data model in 64-bit hardware mode, we must use // 64-bit pointer to unwind stack frame. typedef unsigned long long uhwptr; // NOLINT #else typedef uptr uhwptr; // NOLINT #endif typedef unsigned char u8; typedef unsigned short u16; // NOLINT typedef unsigned int u32; typedef unsigned long long u64; // NOLINT typedef signed char s8; typedef signed short s16; // NOLINT typedef signed int s32; typedef signed long long s64; // NOLINT typedef int fd_t; // WARNING: OFF_T may be different from OS type off_t, depending on the value of // _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls // like pread and mmap, as opposed to pread64 and mmap64. -// Mac and Linux/x86-64 are special. -#if SANITIZER_MAC || (SANITIZER_LINUX && defined(__x86_64__)) +// FreeBSD, Mac and Linux/x86-64 are special. +#if SANITIZER_FREEBSD || SANITIZER_MAC || \ + (SANITIZER_LINUX && defined(__x86_64__)) typedef u64 OFF_T; #else typedef uptr OFF_T; #endif typedef u64 OFF64_T; #if (SANITIZER_WORDSIZE == 64) || SANITIZER_MAC typedef uptr operator_new_size_type; #else typedef u32 operator_new_size_type; #endif } // namespace __sanitizer extern "C" { // Tell the tools to write their reports to "path." instead of stderr. // The special values are "stdout" and "stderr". SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_set_report_path(const char *path); typedef struct { int coverage_sandboxed; __sanitizer::sptr coverage_fd; unsigned int coverage_max_block_size; } __sanitizer_sandbox_arguments; // Notify the tools that the sandbox is going to be turned on. SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args); // This function is called by the tool when it has just finished reporting // an error. 'error_summary' is a one-line string that summarizes // the error message. This function can be overridden by the client. SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_report_error_summary(const char *error_summary); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init(); - SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u8 *guard); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_annotate_contiguous_container(const void *beg, const void *end, const void *old_mid, const void *new_mid); SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, const void *end); } // extern "C" using namespace __sanitizer; // NOLINT // ----------- ATTENTION ------------- // This header should NOT include any other headers to avoid portability issues. // Common defs. #define INLINE inline #define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE #define WEAK SANITIZER_WEAK_ATTRIBUTE // Platform-specific defs. #if defined(_MSC_VER) # define ALWAYS_INLINE __forceinline // FIXME(timurrrr): do we need this on Windows? # define ALIAS(x) # define ALIGNED(x) __declspec(align(x)) # define FORMAT(f, a) # define NOINLINE __declspec(noinline) # define NORETURN __declspec(noreturn) # define THREADLOCAL __declspec(thread) # define NOTHROW # define LIKELY(x) (x) # define UNLIKELY(x) (x) # define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */ #else // _MSC_VER # define ALWAYS_INLINE inline __attribute__((always_inline)) # define ALIAS(x) __attribute__((alias(x))) // Please only use the ALIGNED macro before the type. // Using ALIGNED after the variable declaration is not portable! # define ALIGNED(x) __attribute__((aligned(x))) # define FORMAT(f, a) __attribute__((format(printf, f, a))) # define NOINLINE __attribute__((noinline)) # define NORETURN __attribute__((noreturn)) # define THREADLOCAL __thread # define NOTHROW throw() # define LIKELY(x) __builtin_expect(!!(x), 1) # define UNLIKELY(x) __builtin_expect(!!(x), 0) # if defined(__i386__) || defined(__x86_64__) // __builtin_prefetch(x) generates prefetchnt0 on x86 # define PREFETCH(x) __asm__("prefetchnta (%0)" : : "r" (x)) # else # define PREFETCH(x) __builtin_prefetch(x) # endif #endif // _MSC_VER #if !defined(_MSC_VER) || defined(__clang__) # define UNUSED __attribute__((unused)) # define USED __attribute__((used)) #else # define UNUSED # define USED #endif // Unaligned versions of basic types. typedef ALIGNED(1) u16 uu16; typedef ALIGNED(1) u32 uu32; typedef ALIGNED(1) u64 uu64; typedef ALIGNED(1) s16 us16; typedef ALIGNED(1) s32 us32; typedef ALIGNED(1) s64 us64; #if SANITIZER_WINDOWS typedef unsigned long DWORD; // NOLINT typedef DWORD thread_return_t; # define THREAD_CALLING_CONV __stdcall #else // _WIN32 typedef void* thread_return_t; # define THREAD_CALLING_CONV #endif // _WIN32 typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg); // NOTE: Functions below must be defined in each run-time. namespace __sanitizer { void NORETURN Die(); // FIXME: No, this shouldn't be in the sanitizer interface. SANITIZER_INTERFACE_ATTRIBUTE void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); } // namespace __sanitizer // Check macro #define RAW_CHECK_MSG(expr, msg) do { \ if (UNLIKELY(!(expr))) { \ RawWrite(msg); \ Die(); \ } \ } while (0) #define RAW_CHECK(expr) RAW_CHECK_MSG(expr, #expr) #define CHECK_IMPL(c1, op, c2) \ do { \ __sanitizer::u64 v1 = (u64)(c1); \ __sanitizer::u64 v2 = (u64)(c2); \ if (UNLIKELY(!(v1 op v2))) \ __sanitizer::CheckFailed(__FILE__, __LINE__, \ "(" #c1 ") " #op " (" #c2 ")", v1, v2); \ } while (false) \ /**/ #define CHECK(a) CHECK_IMPL((a), !=, 0) #define CHECK_EQ(a, b) CHECK_IMPL((a), ==, (b)) #define CHECK_NE(a, b) CHECK_IMPL((a), !=, (b)) #define CHECK_LT(a, b) CHECK_IMPL((a), <, (b)) #define CHECK_LE(a, b) CHECK_IMPL((a), <=, (b)) #define CHECK_GT(a, b) CHECK_IMPL((a), >, (b)) #define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b)) -#if TSAN_DEBUG +#if SANITIZER_DEBUG #define DCHECK(a) CHECK(a) #define DCHECK_EQ(a, b) CHECK_EQ(a, b) #define DCHECK_NE(a, b) CHECK_NE(a, b) #define DCHECK_LT(a, b) CHECK_LT(a, b) #define DCHECK_LE(a, b) CHECK_LE(a, b) #define DCHECK_GT(a, b) CHECK_GT(a, b) #define DCHECK_GE(a, b) CHECK_GE(a, b) #else #define DCHECK(a) #define DCHECK_EQ(a, b) #define DCHECK_NE(a, b) #define DCHECK_LT(a, b) #define DCHECK_LE(a, b) #define DCHECK_GT(a, b) #define DCHECK_GE(a, b) #endif #define UNREACHABLE(msg) do { \ CHECK(0 && msg); \ Die(); \ } while (0) #define UNIMPLEMENTED() UNREACHABLE("unimplemented") #define COMPILER_CHECK(pred) IMPL_COMPILER_ASSERT(pred, __LINE__) #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) #define IMPL_PASTE(a, b) a##b #define IMPL_COMPILER_ASSERT(pred, line) \ typedef char IMPL_PASTE(assertion_failed_##_, line)[2*(int)(pred)-1] // Limits for integral types. We have to redefine it in case we don't // have stdint.h (like in Visual Studio 9). #undef __INT64_C #undef __UINT64_C #if SANITIZER_WORDSIZE == 64 # define __INT64_C(c) c ## L # define __UINT64_C(c) c ## UL #else # define __INT64_C(c) c ## LL # define __UINT64_C(c) c ## ULL #endif // SANITIZER_WORDSIZE == 64 #undef INT32_MIN #define INT32_MIN (-2147483647-1) #undef INT32_MAX #define INT32_MAX (2147483647) #undef UINT32_MAX #define UINT32_MAX (4294967295U) #undef INT64_MIN #define INT64_MIN (-__INT64_C(9223372036854775807)-1) #undef INT64_MAX #define INT64_MAX (__INT64_C(9223372036854775807)) #undef UINT64_MAX #define UINT64_MAX (__UINT64_C(18446744073709551615)) enum LinkerInitialized { LINKER_INITIALIZED = 0 }; #if !defined(_MSC_VER) || defined(__clang__) # define GET_CALLER_PC() (uptr)__builtin_return_address(0) # define GET_CURRENT_FRAME() (uptr)__builtin_frame_address(0) #else extern "C" void* _ReturnAddress(void); # pragma intrinsic(_ReturnAddress) # define GET_CALLER_PC() (uptr)_ReturnAddress() // CaptureStackBackTrace doesn't need to know BP on Windows. // FIXME: This macro is still used when printing error reports though it's not // clear if the BP value is needed in the ASan reports on Windows. # define GET_CURRENT_FRAME() (uptr)0xDEADBEEF #endif #define HANDLE_EINTR(res, f) \ { \ int rverrno; \ do { \ res = (f); \ } while (internal_iserror(res, &rverrno) && rverrno == EINTR); \ } + +// Forces the compiler to generate a frame pointer in the function. +#define ENABLE_FRAME_POINTER \ + do { \ + volatile uptr enable_fp; \ + enable_fp = GET_CURRENT_FRAME(); \ + } while (0) #endif // SANITIZER_DEFS_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.cc (revision 279191) @@ -1,244 +1,261 @@ //===-- sanitizer_libc.cc -------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. See sanitizer_libc.h for details. //===----------------------------------------------------------------------===// #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" namespace __sanitizer { s64 internal_atoll(const char *nptr) { return internal_simple_strtoll(nptr, (char**)0, 10); } void *internal_memchr(const void *s, int c, uptr n) { const char *t = (const char *)s; for (uptr i = 0; i < n; ++i, ++t) if (*t == c) return reinterpret_cast(const_cast(t)); return 0; } +void *internal_memrchr(const void *s, int c, uptr n) { + const char *t = (const char *)s; + void *res = nullptr; + for (uptr i = 0; i < n; ++i, ++t) { + if (*t == c) res = reinterpret_cast(const_cast(t)); + } + return res; +} + int internal_memcmp(const void* s1, const void* s2, uptr n) { const char *t1 = (const char *)s1; const char *t2 = (const char *)s2; for (uptr i = 0; i < n; ++i, ++t1, ++t2) if (*t1 != *t2) return *t1 < *t2 ? -1 : 1; return 0; } void *internal_memcpy(void *dest, const void *src, uptr n) { char *d = (char*)dest; const char *s = (const char *)src; for (uptr i = 0; i < n; ++i) d[i] = s[i]; return dest; } void *internal_memmove(void *dest, const void *src, uptr n) { char *d = (char*)dest; const char *s = (const char *)src; sptr i, signed_n = (sptr)n; CHECK_GE(signed_n, 0); if (d < s) { for (i = 0; i < signed_n; ++i) d[i] = s[i]; } else { if (d > s && signed_n > 0) for (i = signed_n - 1; i >= 0 ; --i) { d[i] = s[i]; } } return dest; } // Semi-fast bzero for 16-aligned data. Still far from peak performance. void internal_bzero_aligned16(void *s, uptr n) { struct S16 { u64 a, b; } ALIGNED(16); CHECK_EQ((reinterpret_cast(s) | n) & 15, 0); for (S16 *p = reinterpret_cast(s), *end = p + n / 16; p < end; p++) { p->a = p->b = 0; SanitizerBreakOptimization(0); // Make sure this does not become memset. } } void *internal_memset(void* s, int c, uptr n) { // The next line prevents Clang from making a call to memset() instead of the // loop below. // FIXME: building the runtime with -ffreestanding is a better idea. However // there currently are linktime problems due to PR12396. char volatile *t = (char*)s; for (uptr i = 0; i < n; ++i, ++t) { *t = c; } return s; } uptr internal_strcspn(const char *s, const char *reject) { uptr i; for (i = 0; s[i]; i++) { if (internal_strchr(reject, s[i]) != 0) return i; } return i; } char* internal_strdup(const char *s) { uptr len = internal_strlen(s); + char *s2 = (char*)InternalAlloc(len + 1); + internal_memcpy(s2, s, len); + s2[len] = 0; + return s2; +} + +char* internal_strndup(const char *s, uptr n) { + uptr len = internal_strnlen(s, n); char *s2 = (char*)InternalAlloc(len + 1); internal_memcpy(s2, s, len); s2[len] = 0; return s2; } int internal_strcmp(const char *s1, const char *s2) { while (true) { unsigned c1 = *s1; unsigned c2 = *s2; if (c1 != c2) return (c1 < c2) ? -1 : 1; if (c1 == 0) break; s1++; s2++; } return 0; } int internal_strncmp(const char *s1, const char *s2, uptr n) { for (uptr i = 0; i < n; i++) { unsigned c1 = *s1; unsigned c2 = *s2; if (c1 != c2) return (c1 < c2) ? -1 : 1; if (c1 == 0) break; s1++; s2++; } return 0; } char* internal_strchr(const char *s, int c) { while (true) { if (*s == (char)c) return const_cast(s); if (*s == 0) return 0; s++; } } char *internal_strchrnul(const char *s, int c) { char *res = internal_strchr(s, c); if (!res) res = const_cast(s) + internal_strlen(s); return res; } char *internal_strrchr(const char *s, int c) { const char *res = 0; for (uptr i = 0; s[i]; i++) { if (s[i] == c) res = s + i; } return const_cast(res); } uptr internal_strlen(const char *s) { uptr i = 0; while (s[i]) i++; return i; } char *internal_strncat(char *dst, const char *src, uptr n) { uptr len = internal_strlen(dst); uptr i; for (i = 0; i < n && src[i]; i++) dst[len + i] = src[i]; dst[len + i] = 0; return dst; } char *internal_strncpy(char *dst, const char *src, uptr n) { uptr i; for (i = 0; i < n && src[i]; i++) dst[i] = src[i]; internal_memset(dst + i, '\0', n - i); return dst; } uptr internal_strnlen(const char *s, uptr maxlen) { uptr i = 0; while (i < maxlen && s[i]) i++; return i; } char *internal_strstr(const char *haystack, const char *needle) { // This is O(N^2), but we are not using it in hot places. uptr len1 = internal_strlen(haystack); uptr len2 = internal_strlen(needle); if (len1 < len2) return 0; for (uptr pos = 0; pos <= len1 - len2; pos++) { if (internal_memcmp(haystack + pos, needle, len2) == 0) return const_cast(haystack) + pos; } return 0; } s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) { CHECK_EQ(base, 10); while (IsSpace(*nptr)) nptr++; int sgn = 1; u64 res = 0; bool have_digits = false; char *old_nptr = const_cast(nptr); if (*nptr == '+') { sgn = 1; nptr++; } else if (*nptr == '-') { sgn = -1; nptr++; } while (IsDigit(*nptr)) { res = (res <= UINT64_MAX / 10) ? res * 10 : UINT64_MAX; int digit = ((*nptr) - '0'); res = (res <= UINT64_MAX - digit) ? res + digit : UINT64_MAX; have_digits = true; nptr++; } if (endptr != 0) { *endptr = (have_digits) ? const_cast(nptr) : old_nptr; } if (sgn > 0) { return (s64)(Min((u64)INT64_MAX, res)); } else { return (res > INT64_MAX) ? INT64_MIN : ((s64)res * -1); } } bool mem_is_zero(const char *beg, uptr size) { CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40)); // Sanity check. const char *end = beg + size; uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr)); uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr)); uptr all = 0; // Prologue. for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++) all |= *mem; // Aligned loop. for (; aligned_beg < aligned_end; aligned_beg++) all |= *aligned_beg; // Epilogue. if ((char*)aligned_end >= beg) for (const char *mem = (char*)aligned_end; mem < end; mem++) all |= *mem; return all == 0; } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_libc.h (revision 279191) @@ -1,108 +1,129 @@ //===-- sanitizer_libc.h ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. // These tools can not use some of the libc functions directly because those // functions are intercepted. Instead, we implement a tiny subset of libc here. //===----------------------------------------------------------------------===// #ifndef SANITIZER_LIBC_H #define SANITIZER_LIBC_H // ----------- ATTENTION ------------- // This header should NOT include any other headers from sanitizer runtime. #include "sanitizer_internal_defs.h" namespace __sanitizer { // internal_X() is a custom implementation of X() for use in RTL. // String functions s64 internal_atoll(const char *nptr); void *internal_memchr(const void *s, int c, uptr n); +void *internal_memrchr(const void *s, int c, uptr n); int internal_memcmp(const void* s1, const void* s2, uptr n); void *internal_memcpy(void *dest, const void *src, uptr n); void *internal_memmove(void *dest, const void *src, uptr n); // Set [s, s + n) to 0. Both s and n should be 16-aligned. void internal_bzero_aligned16(void *s, uptr n); // Should not be used in performance-critical places. void *internal_memset(void *s, int c, uptr n); char* internal_strchr(const char *s, int c); char *internal_strchrnul(const char *s, int c); int internal_strcmp(const char *s1, const char *s2); uptr internal_strcspn(const char *s, const char *reject); char *internal_strdup(const char *s); +char *internal_strndup(const char *s, uptr n); uptr internal_strlen(const char *s); char *internal_strncat(char *dst, const char *src, uptr n); int internal_strncmp(const char *s1, const char *s2, uptr n); char *internal_strncpy(char *dst, const char *src, uptr n); uptr internal_strnlen(const char *s, uptr maxlen); char *internal_strrchr(const char *s, int c); // This is O(N^2), but we are not using it in hot places. char *internal_strstr(const char *haystack, const char *needle); // Works only for base=10 and doesn't set errno. s64 internal_simple_strtoll(const char *nptr, char **endptr, int base); int internal_snprintf(char *buffer, uptr length, const char *format, ...); // Return true if all bytes in [mem, mem+size) are zero. // Optimized for the case when the result is true. bool mem_is_zero(const char *mem, uptr size); // Memory uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, u64 offset); uptr internal_munmap(void *addr, uptr length); // I/O const fd_t kInvalidFd = -1; const fd_t kStdinFd = 0; const fd_t kStdoutFd = 1; const fd_t kStderrFd = 2; uptr internal_close(fd_t fd); int internal_isatty(fd_t fd); // Use __sanitizer::OpenFile() instead. uptr internal_open(const char *filename, int flags); uptr internal_open(const char *filename, int flags, u32 mode); uptr internal_read(fd_t fd, void *buf, uptr count); uptr internal_write(fd_t fd, const void *buf, uptr count); uptr internal_ftruncate(fd_t fd, uptr size); // OS uptr internal_filesize(fd_t fd); // -1 on error. uptr internal_stat(const char *path, void *buf); uptr internal_lstat(const char *path, void *buf); uptr internal_fstat(fd_t fd, void *buf); uptr internal_dup2(int oldfd, int newfd); uptr internal_readlink(const char *path, char *buf, uptr bufsize); uptr internal_unlink(const char *path); uptr internal_rename(const char *oldpath, const char *newpath); void NORETURN internal__exit(int exitcode); uptr internal_lseek(fd_t fd, OFF_T offset, int whence); uptr internal_ptrace(int request, int pid, void *addr, void *data); uptr internal_waitpid(int pid, int *status, int options); uptr internal_getpid(); uptr internal_getppid(); int internal_fork(); // Threading uptr internal_sched_yield(); + +// These functions call appropriate pthread_ functions directly, bypassing +// the interceptor. They are weak and may not be present in some tools. +SANITIZER_WEAK_ATTRIBUTE +int real_pthread_create(void *th, void *attr, void *(*callback)(void *), + void *param); +SANITIZER_WEAK_ATTRIBUTE +int real_pthread_join(void *th, void **ret); + +#define DEFINE_REAL_PTHREAD_FUNCTIONS \ + namespace __sanitizer { \ + int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \ + void *param) { \ + return REAL(pthread_create)(th, attr, callback, param); \ + } \ + int real_pthread_join(void *th, void **ret) { \ + return REAL(pthread_join(th, ret)); \ + } \ + } // namespace __sanitizer // Error handling bool internal_iserror(uptr retval, int *rverrno = 0); int internal_sigaction(int signum, const void *act, void *oldact); } // namespace __sanitizer #endif // SANITIZER_LIBC_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc (revision 279191) @@ -1,904 +1,926 @@ //===-- sanitizer_linux.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements linux-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" #include "sanitizer_linux.h" #include "sanitizer_mutex.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" #if !SANITIZER_FREEBSD #include #endif +// For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat' +// format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To +// access stat from asm/stat.h, without conflicting with definition in +// sys/stat.h, we use this trick. +#if defined(__mips64) +#include +#define stat kernel_stat +#include +#undef stat +#endif + #include #include #include #if !SANITIZER_ANDROID #include #endif #include #include #include #include #include #include #include #include #include #include #if SANITIZER_FREEBSD #include #include extern "C" { // must be included after and on // FreeBSD 9.2 and 10.0. #include } extern char **environ; // provided by crt1 #endif // SANITIZER_FREEBSD #if !SANITIZER_ANDROID #include #endif #if SANITIZER_ANDROID #include #include #endif #if SANITIZER_LINUX // struct kernel_timeval { long tv_sec; long tv_usec; }; // is broken on some linux distributions. const int FUTEX_WAIT = 0; const int FUTEX_WAKE = 1; #endif // SANITIZER_LINUX // Are we using 32-bit or 64-bit Linux syscalls? // x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32 // but it still needs to use 64-bit syscalls. #if SANITIZER_LINUX && (defined(__x86_64__) || SANITIZER_WORDSIZE == 64) # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1 #else # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0 #endif namespace __sanitizer { #if SANITIZER_LINUX && defined(__x86_64__) #include "sanitizer_syscall_linux_x86_64.inc" #else #include "sanitizer_syscall_generic.inc" #endif // --------------- sanitizer_libc.h -uptr internal_mmap(void *addr, uptr length, int prot, int flags, - int fd, u64 offset) { +uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, + u64 offset) { #if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd, offset); #else + // mmap2 specifies file offset in 4096-byte units. + CHECK(IsAligned(offset, 4096)); return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd, - offset); + offset / 4096); #endif } uptr internal_munmap(void *addr, uptr length) { return internal_syscall(SYSCALL(munmap), (uptr)addr, length); } uptr internal_close(fd_t fd) { return internal_syscall(SYSCALL(close), fd); } uptr internal_open(const char *filename, int flags) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags); #else return internal_syscall(SYSCALL(open), (uptr)filename, flags); #endif } uptr internal_open(const char *filename, int flags, u32 mode) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags, mode); #else return internal_syscall(SYSCALL(open), (uptr)filename, flags, mode); #endif } uptr OpenFile(const char *filename, bool write) { return internal_open(filename, write ? O_RDWR | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); } uptr internal_read(fd_t fd, void *buf, uptr count) { sptr res; HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf, count)); return res; } uptr internal_write(fd_t fd, const void *buf, uptr count) { sptr res; HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(write), fd, (uptr)buf, count)); return res; } uptr internal_ftruncate(fd_t fd, uptr size) { sptr res; HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, size)); return res; } #if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && !SANITIZER_FREEBSD static void stat64_to_stat(struct stat64 *in, struct stat *out) { internal_memset(out, 0, sizeof(*out)); out->st_dev = in->st_dev; out->st_ino = in->st_ino; out->st_mode = in->st_mode; out->st_nlink = in->st_nlink; out->st_uid = in->st_uid; out->st_gid = in->st_gid; out->st_rdev = in->st_rdev; out->st_size = in->st_size; out->st_blksize = in->st_blksize; out->st_blocks = in->st_blocks; out->st_atime = in->st_atime; out->st_mtime = in->st_mtime; out->st_ctime = in->st_ctime; out->st_ino = in->st_ino; } #endif +#if defined(__mips64) +static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) { + internal_memset(out, 0, sizeof(*out)); + out->st_dev = in->st_dev; + out->st_ino = in->st_ino; + out->st_mode = in->st_mode; + out->st_nlink = in->st_nlink; + out->st_uid = in->st_uid; + out->st_gid = in->st_gid; + out->st_rdev = in->st_rdev; + out->st_size = in->st_size; + out->st_blksize = in->st_blksize; + out->st_blocks = in->st_blocks; + out->st_atime = in->st_atime_nsec; + out->st_mtime = in->st_mtime_nsec; + out->st_ctime = in->st_ctime_nsec; + out->st_ino = in->st_ino; +} +#endif + uptr internal_stat(const char *path, void *buf) { #if SANITIZER_FREEBSD return internal_syscall(SYSCALL(stat), path, buf); #elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0); #elif SANITIZER_LINUX_USES_64BIT_SYSCALLS +# if defined(__mips64) + // For mips64, stat syscall fills buffer in the format of kernel_stat + struct kernel_stat kbuf; + int res = internal_syscall(SYSCALL(stat), path, &kbuf); + kernel_stat_to_stat(&kbuf, (struct stat *)buf); + return res; +# else return internal_syscall(SYSCALL(stat), (uptr)path, (uptr)buf); +# endif #else struct stat64 buf64; int res = internal_syscall(SYSCALL(stat64), path, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; #endif } uptr internal_lstat(const char *path, void *buf) { #if SANITIZER_FREEBSD return internal_syscall(SYSCALL(lstat), path, buf); #elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, AT_SYMLINK_NOFOLLOW); #elif SANITIZER_LINUX_USES_64BIT_SYSCALLS return internal_syscall(SYSCALL(lstat), (uptr)path, (uptr)buf); #else struct stat64 buf64; int res = internal_syscall(SYSCALL(lstat64), path, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; #endif } uptr internal_fstat(fd_t fd, void *buf) { #if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS return internal_syscall(SYSCALL(fstat), fd, (uptr)buf); #else struct stat64 buf64; int res = internal_syscall(SYSCALL(fstat64), fd, &buf64); stat64_to_stat(&buf64, (struct stat *)buf); return res; #endif } uptr internal_filesize(fd_t fd) { struct stat st; if (internal_fstat(fd, &st)) return -1; return (uptr)st.st_size; } uptr internal_dup2(int oldfd, int newfd) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(dup3), oldfd, newfd, 0); #else return internal_syscall(SYSCALL(dup2), oldfd, newfd); #endif } uptr internal_readlink(const char *path, char *buf, uptr bufsize) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf, bufsize); #else return internal_syscall(SYSCALL(readlink), (uptr)path, (uptr)buf, bufsize); #endif } uptr internal_unlink(const char *path) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0); #else return internal_syscall(SYSCALL(unlink), (uptr)path); #endif } uptr internal_rename(const char *oldpath, const char *newpath) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD, (uptr)newpath); #else return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath); #endif } uptr internal_sched_yield() { return internal_syscall(SYSCALL(sched_yield)); } void internal__exit(int exitcode) { #if SANITIZER_FREEBSD internal_syscall(SYSCALL(exit), exitcode); #else internal_syscall(SYSCALL(exit_group), exitcode); #endif Die(); // Unreachable. } uptr internal_execve(const char *filename, char *const argv[], char *const envp[]) { return internal_syscall(SYSCALL(execve), (uptr)filename, (uptr)argv, (uptr)envp); } // ----------------- sanitizer_common.h bool FileExists(const char *filename) { struct stat st; #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS if (internal_syscall(SYSCALL(newfstatat), AT_FDCWD, filename, &st, 0)) #else if (internal_stat(filename, &st)) #endif return false; // Sanity check: filename is a regular file. return S_ISREG(st.st_mode); } uptr GetTid() { #if SANITIZER_FREEBSD return (uptr)pthread_self(); #else return internal_syscall(SYSCALL(gettid)); #endif } u64 NanoTime() { #if SANITIZER_FREEBSD timeval tv; #else kernel_timeval tv; #endif internal_memset(&tv, 0, sizeof(tv)); internal_syscall(SYSCALL(gettimeofday), (uptr)&tv, 0); return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000; } // Like getenv, but reads env directly from /proc (on Linux) or parses the // 'environ' array (on FreeBSD) and does not use libc. This function should be // called first inside __asan_init. const char *GetEnv(const char *name) { #if SANITIZER_FREEBSD if (::environ != 0) { uptr NameLen = internal_strlen(name); for (char **Env = ::environ; *Env != 0; Env++) { if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=') return (*Env) + NameLen + 1; } } return 0; // Not found. #elif SANITIZER_LINUX static char *environ; static uptr len; static bool inited; if (!inited) { inited = true; uptr environ_size; len = ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, 1 << 26); } if (!environ || len == 0) return 0; uptr namelen = internal_strlen(name); const char *p = environ; while (*p != '\0') { // will happen at the \0\0 that terminates the buffer // proc file has the format NAME=value\0NAME=value\0NAME=value\0... const char* endp = (char*)internal_memchr(p, '\0', len - (p - environ)); if (endp == 0) // this entry isn't NUL terminated return 0; else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match. return p + namelen + 1; // point after = p = endp + 1; } return 0; // Not found. #else #error "Unsupported platform" #endif } extern "C" { SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end; } #if !SANITIZER_GO static void ReadNullSepFileToArray(const char *path, char ***arr, int arr_size) { char *buff; uptr buff_size = 0; *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray"); ReadFileToBuffer(path, &buff, &buff_size, 1024 * 1024); (*arr)[0] = buff; int count, i; for (count = 1, i = 1; ; i++) { if (buff[i] == 0) { if (buff[i+1] == 0) break; (*arr)[count] = &buff[i+1]; CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible. count++; } } (*arr)[count] = 0; } #endif -uptr GetRSS() { - uptr fd = OpenFile("/proc/self/statm", false); - if ((sptr)fd < 0) - return 0; - char buf[64]; - uptr len = internal_read(fd, buf, sizeof(buf) - 1); - internal_close(fd); - if ((sptr)len <= 0) - return 0; - buf[len] = 0; - // The format of the file is: - // 1084 89 69 11 0 79 0 - // We need the second number which is RSS in 4K units. - char *pos = buf; - // Skip the first number. - while (*pos >= '0' && *pos <= '9') - pos++; - // Skip whitespaces. - while (!(*pos >= '0' && *pos <= '9') && *pos != 0) - pos++; - // Read the number. - uptr rss = 0; - while (*pos >= '0' && *pos <= '9') - rss = rss * 10 + *pos++ - '0'; - return rss * 4096; -} - static void GetArgsAndEnv(char*** argv, char*** envp) { #if !SANITIZER_GO if (&__libc_stack_end) { #endif uptr* stack_end = (uptr*)__libc_stack_end; int argc = *stack_end; *argv = (char**)(stack_end + 1); *envp = (char**)(stack_end + argc + 2); #if !SANITIZER_GO } else { static const int kMaxArgv = 2000, kMaxEnvp = 2000; ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv); ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp); } #endif } void ReExec() { char **argv, **envp; GetArgsAndEnv(&argv, &envp); uptr rv = internal_execve("/proc/self/exe", argv, envp); int rverrno; CHECK_EQ(internal_iserror(rv, &rverrno), true); Printf("execve failed, errno %d\n", rverrno); Die(); } -// Stub implementation of GetThreadStackAndTls for Go. -#if SANITIZER_GO -void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, - uptr *tls_addr, uptr *tls_size) { - *stk_addr = 0; - *stk_size = 0; - *tls_addr = 0; - *tls_size = 0; -} -#endif // SANITIZER_GO - enum MutexState { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 }; -BlockingMutex::BlockingMutex(LinkerInitialized) { - CHECK_EQ(owner_, 0); -} - BlockingMutex::BlockingMutex() { internal_memset(this, 0, sizeof(*this)); } void BlockingMutex::Lock() { + CHECK_EQ(owner_, 0); atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked) return; while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) { #if SANITIZER_FREEBSD _umtx_op(m, UMTX_OP_WAIT_UINT, MtxSleeping, 0, 0); #else internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAIT, MtxSleeping, 0, 0, 0); #endif } } void BlockingMutex::Unlock() { atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed); CHECK_NE(v, MtxUnlocked); if (v == MtxSleeping) { #if SANITIZER_FREEBSD _umtx_op(m, UMTX_OP_WAKE, 1, 0, 0); #else internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAKE, 1, 0, 0, 0); #endif } } void BlockingMutex::CheckLocked() { atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed)); } // ----------------- sanitizer_linux.h // The actual size of this structure is specified by d_reclen. // Note that getdents64 uses a different structure format. We only provide the // 32-bit syscall here. struct linux_dirent { #if SANITIZER_X32 u64 d_ino; u64 d_off; #else unsigned long d_ino; unsigned long d_off; #endif unsigned short d_reclen; char d_name[256]; }; // Syscall wrappers. uptr internal_ptrace(int request, int pid, void *addr, void *data) { return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr, (uptr)data); } uptr internal_waitpid(int pid, int *status, int options) { return internal_syscall(SYSCALL(wait4), pid, (uptr)status, options, 0 /* rusage */); } uptr internal_getpid() { return internal_syscall(SYSCALL(getpid)); } uptr internal_getppid() { return internal_syscall(SYSCALL(getppid)); } uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(getdents64), fd, (uptr)dirp, count); #else return internal_syscall(SYSCALL(getdents), fd, (uptr)dirp, count); #endif } uptr internal_lseek(fd_t fd, OFF_T offset, int whence) { return internal_syscall(SYSCALL(lseek), fd, offset, whence); } #if SANITIZER_LINUX uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) { return internal_syscall(SYSCALL(prctl), option, arg2, arg3, arg4, arg5); } #endif uptr internal_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss) { return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss); } int internal_fork() { #if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS return internal_syscall(SYSCALL(clone), SIGCHLD, 0); #else return internal_syscall(SYSCALL(fork)); #endif } #if SANITIZER_LINUX // Doesn't set sa_restorer, use with caution (see below). int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { __sanitizer_kernel_sigaction_t k_act, k_oldact; internal_memset(&k_act, 0, sizeof(__sanitizer_kernel_sigaction_t)); internal_memset(&k_oldact, 0, sizeof(__sanitizer_kernel_sigaction_t)); const __sanitizer_sigaction *u_act = (const __sanitizer_sigaction *)act; __sanitizer_sigaction *u_oldact = (__sanitizer_sigaction *)oldact; if (u_act) { k_act.handler = u_act->handler; k_act.sigaction = u_act->sigaction; internal_memcpy(&k_act.sa_mask, &u_act->sa_mask, sizeof(__sanitizer_kernel_sigset_t)); k_act.sa_flags = u_act->sa_flags; // FIXME: most often sa_restorer is unset, however the kernel requires it // to point to a valid signal restorer that calls the rt_sigreturn syscall. // If sa_restorer passed to the kernel is NULL, the program may crash upon // signal delivery or fail to unwind the stack in the signal handler. // libc implementation of sigaction() passes its own restorer to // rt_sigaction, so we need to do the same (we'll need to reimplement the // restorers; for x86_64 the restorer address can be obtained from // oldact->sa_restorer upon a call to sigaction(xxx, NULL, oldact). k_act.sa_restorer = u_act->sa_restorer; } uptr result = internal_syscall(SYSCALL(rt_sigaction), (uptr)signum, (uptr)(u_act ? &k_act : NULL), (uptr)(u_oldact ? &k_oldact : NULL), (uptr)sizeof(__sanitizer_kernel_sigset_t)); if ((result == 0) && u_oldact) { u_oldact->handler = k_oldact.handler; u_oldact->sigaction = k_oldact.sigaction; internal_memcpy(&u_oldact->sa_mask, &k_oldact.sa_mask, sizeof(__sanitizer_kernel_sigset_t)); u_oldact->sa_flags = k_oldact.sa_flags; u_oldact->sa_restorer = k_oldact.sa_restorer; } return result; } #endif // SANITIZER_LINUX uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) { #if SANITIZER_FREEBSD return internal_syscall(SYSCALL(sigprocmask), how, set, oldset); #else __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set; __sanitizer_kernel_sigset_t *k_oldset = (__sanitizer_kernel_sigset_t *)oldset; return internal_syscall(SYSCALL(rt_sigprocmask), (uptr)how, (uptr)&k_set->sig[0], (uptr)&k_oldset->sig[0], sizeof(__sanitizer_kernel_sigset_t)); #endif } void internal_sigfillset(__sanitizer_sigset_t *set) { internal_memset(set, 0xff, sizeof(*set)); } #if SANITIZER_LINUX void internal_sigdelset(__sanitizer_sigset_t *set, int signum) { signum -= 1; CHECK_GE(signum, 0); CHECK_LT(signum, sizeof(*set) * 8); __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set; const uptr idx = signum / (sizeof(k_set->sig[0]) * 8); const uptr bit = signum % (sizeof(k_set->sig[0]) * 8); k_set->sig[idx] &= ~(1 << bit); } #endif // SANITIZER_LINUX // ThreadLister implementation. ThreadLister::ThreadLister(int pid) : pid_(pid), descriptor_(-1), buffer_(4096), error_(true), entry_((struct linux_dirent *)buffer_.data()), bytes_read_(0) { char task_directory_path[80]; internal_snprintf(task_directory_path, sizeof(task_directory_path), "/proc/%d/task/", pid); uptr openrv = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY); if (internal_iserror(openrv)) { error_ = true; Report("Can't open /proc/%d/task for reading.\n", pid); } else { error_ = false; descriptor_ = openrv; } } int ThreadLister::GetNextTID() { int tid = -1; do { if (error_) return -1; if ((char *)entry_ >= &buffer_[bytes_read_] && !GetDirectoryEntries()) return -1; if (entry_->d_ino != 0 && entry_->d_name[0] >= '0' && entry_->d_name[0] <= '9') { // Found a valid tid. tid = (int)internal_atoll(entry_->d_name); } entry_ = (struct linux_dirent *)(((char *)entry_) + entry_->d_reclen); } while (tid < 0); return tid; } void ThreadLister::Reset() { if (error_ || descriptor_ < 0) return; internal_lseek(descriptor_, 0, SEEK_SET); } ThreadLister::~ThreadLister() { if (descriptor_ >= 0) internal_close(descriptor_); } bool ThreadLister::error() { return error_; } bool ThreadLister::GetDirectoryEntries() { CHECK_GE(descriptor_, 0); CHECK_NE(error_, true); bytes_read_ = internal_getdents(descriptor_, (struct linux_dirent *)buffer_.data(), buffer_.size()); if (internal_iserror(bytes_read_)) { Report("Can't read directory entries from /proc/%d/task.\n", pid_); error_ = true; return false; } else if (bytes_read_ == 0) { return false; } entry_ = (struct linux_dirent *)buffer_.data(); return true; } uptr GetPageSize() { #if SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__)) return EXEC_PAGESIZE; #else return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy. #endif } static char proc_self_exe_cache_str[kMaxPathLength]; static uptr proc_self_exe_cache_len = 0; uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { if (proc_self_exe_cache_len > 0) { // If available, use the cached module name. uptr module_name_len = internal_snprintf(buf, buf_len, "%s", proc_self_exe_cache_str); CHECK_LT(module_name_len, buf_len); return module_name_len; } #if SANITIZER_FREEBSD const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; size_t Size = buf_len; bool IsErr = (sysctl(Mib, 4, buf, &Size, NULL, 0) != 0); int readlink_error = IsErr ? errno : 0; uptr module_name_len = Size; #else uptr module_name_len = internal_readlink( "/proc/self/exe", buf, buf_len); int readlink_error; bool IsErr = internal_iserror(module_name_len, &readlink_error); #endif if (IsErr) { // We can't read /proc/self/exe for some reason, assume the name of the // binary is unknown. Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, " "some stack frames may not be symbolized\n", readlink_error); module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe"); CHECK_LT(module_name_len, buf_len); } return module_name_len; } void CacheBinaryName() { if (!proc_self_exe_cache_len) { proc_self_exe_cache_len = ReadBinaryName(proc_self_exe_cache_str, kMaxPathLength); } } // Match full names of the form /path/to/base_name{-,.}* bool LibraryNameIs(const char *full_name, const char *base_name) { const char *name = full_name; // Strip path. while (*name != '\0') name++; while (name > full_name && *name != '/') name--; if (*name == '/') name++; uptr base_name_length = internal_strlen(base_name); if (internal_strncmp(name, base_name, base_name_length)) return false; return (name[base_name_length] == '-' || name[base_name_length] == '.'); } #if !SANITIZER_ANDROID // Call cb for each region mapped by map. void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) { + CHECK_NE(map, nullptr); #if !SANITIZER_FREEBSD typedef ElfW(Phdr) Elf_Phdr; typedef ElfW(Ehdr) Elf_Ehdr; #endif // !SANITIZER_FREEBSD char *base = (char *)map->l_addr; Elf_Ehdr *ehdr = (Elf_Ehdr *)base; char *phdrs = base + ehdr->e_phoff; char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize; // Find the segment with the minimum base so we can "relocate" the p_vaddr // fields. Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC // objects have a non-zero base. uptr preferred_base = (uptr)-1; for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { Elf_Phdr *phdr = (Elf_Phdr *)iter; if (phdr->p_type == PT_LOAD && preferred_base > (uptr)phdr->p_vaddr) preferred_base = (uptr)phdr->p_vaddr; } // Compute the delta from the real base to get a relocation delta. sptr delta = (uptr)base - preferred_base; // Now we can figure out what the loader really mapped. for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { Elf_Phdr *phdr = (Elf_Phdr *)iter; if (phdr->p_type == PT_LOAD) { uptr seg_start = phdr->p_vaddr + delta; uptr seg_end = seg_start + phdr->p_memsz; // None of these values are aligned. We consider the ragged edges of the // load command as defined, since they are mapped from the file. seg_start = RoundDownTo(seg_start, GetPageSizeCached()); seg_end = RoundUpTo(seg_end, GetPageSizeCached()); cb((void *)seg_start, seg_end - seg_start); } } } #endif #if defined(__x86_64__) && SANITIZER_LINUX // We cannot use glibc's clone wrapper, because it messes with the child // task's TLS. It writes the PID and TID of the child task to its thread // descriptor, but in our case the child task shares the thread descriptor with // the parent (because we don't know how to allocate a new thread // descriptor to keep glibc happy). So the stock version of clone(), when // used with CLONE_VM, would end up corrupting the parent's thread descriptor. uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { long long res; if (!fn || !child_stack) return -EINVAL; CHECK_EQ(0, (uptr)child_stack % 16); child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); ((unsigned long long *)child_stack)[0] = (uptr)fn; ((unsigned long long *)child_stack)[1] = (uptr)arg; register void *r8 __asm__("r8") = newtls; register int *r10 __asm__("r10") = child_tidptr; __asm__ __volatile__( /* %rax = syscall(%rax = SYSCALL(clone), * %rdi = flags, * %rsi = child_stack, * %rdx = parent_tidptr, * %r8 = new_tls, * %r10 = child_tidptr) */ "syscall\n" /* if (%rax != 0) * return; */ "testq %%rax,%%rax\n" "jnz 1f\n" /* In the child. Terminate unwind chain. */ // XXX: We should also terminate the CFI unwind chain // here. Unfortunately clang 3.2 doesn't support the // necessary CFI directives, so we skip that part. "xorq %%rbp,%%rbp\n" /* Call "fn(arg)". */ "popq %%rax\n" "popq %%rdi\n" "call *%%rax\n" /* Call _exit(%rax). */ "movq %%rax,%%rdi\n" "movq %2,%%rax\n" "syscall\n" /* Return to parent. */ "1:\n" : "=a" (res) : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)), "S"(child_stack), "D"(flags), "d"(parent_tidptr), "r"(r8), "r"(r10) : "rsp", "memory", "r11", "rcx"); return res; } #endif // defined(__x86_64__) && SANITIZER_LINUX #if SANITIZER_ANDROID static atomic_uint8_t android_log_initialized; void AndroidLogInit() { atomic_store(&android_log_initialized, 1, memory_order_release); } // This thing is not, strictly speaking, async signal safe, but it does not seem // to cause any issues. Alternative is writing to log devices directly, but // their location and message format might change in the future, so we'd really // like to avoid that. void AndroidLogWrite(const char *buffer) { if (!atomic_load(&android_log_initialized, memory_order_acquire)) return; char *copy = internal_strdup(buffer); char *p = copy; char *q; // __android_log_write has an implicit message length limit. // Print one line at a time. do { q = internal_strchr(p, '\n'); if (q) *q = '\0'; __android_log_write(ANDROID_LOG_INFO, NULL, p); if (q) p = q + 1; } while (q); InternalFree(copy); } void GetExtraActivationFlags(char *buf, uptr size) { CHECK(size > PROP_VALUE_MAX); __system_property_get("asan.options", buf); } #endif bool IsDeadlySignal(int signum) { - return (signum == SIGSEGV) && common_flags()->handle_segv; + return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; } + +#ifndef SANITIZER_GO +void *internal_start_thread(void(*func)(void *arg), void *arg) { + // Start the thread with signals blocked, otherwise it can steal user signals. + __sanitizer_sigset_t set, old; + internal_sigfillset(&set); + internal_sigprocmask(SIG_SETMASK, &set, &old); + void *th; + real_pthread_create(&th, 0, (void*(*)(void *arg))func, arg); + internal_sigprocmask(SIG_SETMASK, &old, 0); + return th; +} + +void internal_join_thread(void *th) { + real_pthread_join(th, 0); +} +#else +void *internal_start_thread(void (*func)(void *), void *arg) { return 0; } + +void internal_join_thread(void *th) {} +#endif } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cc (revision 279191) @@ -1,425 +1,479 @@ //===-- sanitizer_linux_libcdep.cc ----------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements linux-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_freebsd.h" #include "sanitizer_linux.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include "sanitizer_atomic.h" #include "sanitizer_symbolizer.h" #if SANITIZER_ANDROID || SANITIZER_FREEBSD #include // for dlsym() #endif #include #include #include #if SANITIZER_FREEBSD #include #include #define pthread_getattr_np pthread_attr_get_np #endif #if SANITIZER_LINUX #include #endif #if !SANITIZER_ANDROID #include #include #include #endif namespace __sanitizer { // This function is defined elsewhere if we intercepted pthread_attr_getstack. extern "C" { SANITIZER_WEAK_ATTRIBUTE int real_pthread_attr_getstack(void *attr, void **addr, size_t *size); } // extern "C" static int my_pthread_attr_getstack(void *attr, void **addr, size_t *size) { - if (real_pthread_attr_getstack) +#if !SANITIZER_GO + if (&real_pthread_attr_getstack) return real_pthread_attr_getstack((pthread_attr_t *)attr, addr, size); +#endif return pthread_attr_getstack((pthread_attr_t *)attr, addr, size); } SANITIZER_WEAK_ATTRIBUTE int real_sigaction(int signum, const void *act, void *oldact); int internal_sigaction(int signum, const void *act, void *oldact) { - if (real_sigaction) +#if !SANITIZER_GO + if (&real_sigaction) return real_sigaction(signum, act, oldact); +#endif return sigaction(signum, (const struct sigaction *)act, (struct sigaction *)oldact); } void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom) { CHECK(stack_top); CHECK(stack_bottom); if (at_initialization) { // This is the main thread. Libpthread may not be initialized yet. struct rlimit rl; CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); // Find the mapping that contains a stack variable. MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end, offset; uptr prev_end = 0; while (proc_maps.Next(&start, &end, &offset, 0, 0, /* protection */0)) { if ((uptr)&rl < end) break; prev_end = end; } CHECK((uptr)&rl >= start && (uptr)&rl < end); // Get stacksize from rlimit, but clip it so that it does not overlap // with other mappings. uptr stacksize = rl.rlim_cur; if (stacksize > end - prev_end) stacksize = end - prev_end; // When running with unlimited stack size, we still want to set some limit. // The unlimited stack size is caused by 'ulimit -s unlimited'. // Also, for some reason, GNU make spawns subprocesses with unlimited stack. if (stacksize > kMaxThreadStackSize) stacksize = kMaxThreadStackSize; *stack_top = end; *stack_bottom = end - stacksize; return; } pthread_attr_t attr; pthread_attr_init(&attr); CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); uptr stacksize = 0; void *stackaddr = 0; my_pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); pthread_attr_destroy(&attr); CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check. *stack_top = (uptr)stackaddr + stacksize; *stack_bottom = (uptr)stackaddr; } +#if !SANITIZER_GO bool SetEnv(const char *name, const char *value) { void *f = dlsym(RTLD_NEXT, "setenv"); if (f == 0) return false; typedef int(*setenv_ft)(const char *name, const char *value, int overwrite); setenv_ft setenv_f; CHECK_EQ(sizeof(setenv_f), sizeof(f)); internal_memcpy(&setenv_f, &f, sizeof(f)); return setenv_f(name, value, 1) == 0; } +#endif bool SanitizerSetThreadName(const char *name) { #ifdef PR_SET_NAME return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); // NOLINT #else return false; #endif } bool SanitizerGetThreadName(char *name, int max_len) { #ifdef PR_GET_NAME char buff[17]; if (prctl(PR_GET_NAME, (unsigned long)buff, 0, 0, 0)) // NOLINT return false; internal_strncpy(name, buff, max_len); name[max_len] = 0; return true; #else return false; #endif } #if !SANITIZER_FREEBSD static uptr g_tls_size; #endif #ifdef __i386__ # define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall)) #else # define DL_INTERNAL_FUNCTION #endif void InitTlsSize() { -#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID +#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION; get_tls_func get_tls; void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr)); internal_memcpy(&get_tls, &get_tls_static_info_ptr, sizeof(get_tls_static_info_ptr)); CHECK_NE(get_tls, 0); size_t tls_size = 0; size_t tls_align = 0; get_tls(&tls_size, &tls_align); g_tls_size = tls_size; #endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID } #if (defined(__x86_64__) || defined(__i386__)) && SANITIZER_LINUX // sizeof(struct thread) from glibc. static atomic_uintptr_t kThreadDescriptorSize; uptr ThreadDescriptorSize() { uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed); if (val) return val; #ifdef _CS_GNU_LIBC_VERSION char buf[64]; uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf)); if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) { char *end; int minor = internal_simple_strtoll(buf + 8, &end, 10); if (end != buf + 8 && (*end == '\0' || *end == '.')) { /* sizeof(struct thread) values from various glibc versions. */ if (SANITIZER_X32) val = 1728; // Assume only one particular version for x32. else if (minor <= 3) val = FIRST_32_SECOND_64(1104, 1696); else if (minor == 4) val = FIRST_32_SECOND_64(1120, 1728); else if (minor == 5) val = FIRST_32_SECOND_64(1136, 1728); else if (minor <= 9) val = FIRST_32_SECOND_64(1136, 1712); else if (minor == 10) val = FIRST_32_SECOND_64(1168, 1776); else if (minor <= 12) val = FIRST_32_SECOND_64(1168, 2288); + else if (minor == 13) + val = FIRST_32_SECOND_64(1168, 2304); else val = FIRST_32_SECOND_64(1216, 2304); } if (val) atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); return val; } #endif return 0; } // The offset at which pointer to self is located in the thread descriptor. const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16); uptr ThreadSelfOffset() { return kThreadSelfOffset; } uptr ThreadSelf() { uptr descr_addr; # if defined(__i386__) asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); # elif defined(__x86_64__) asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); # else # error "unsupported CPU arch" # endif return descr_addr; } #endif // (defined(__x86_64__) || defined(__i386__)) && SANITIZER_LINUX #if SANITIZER_FREEBSD static void **ThreadSelfSegbase() { void **segbase = 0; # if defined(__i386__) // sysarch(I386_GET_GSBASE, segbase); __asm __volatile("mov %%gs:0, %0" : "=r" (segbase)); # elif defined(__x86_64__) // sysarch(AMD64_GET_FSBASE, segbase); __asm __volatile("movq %%fs:0, %0" : "=r" (segbase)); # else # error "unsupported CPU arch for FreeBSD platform" # endif return segbase; } uptr ThreadSelf() { return (uptr)ThreadSelfSegbase()[2]; } #endif // SANITIZER_FREEBSD +#if !SANITIZER_GO static void GetTls(uptr *addr, uptr *size) { #if SANITIZER_LINUX # if defined(__x86_64__) || defined(__i386__) *addr = ThreadSelf(); *size = GetTlsSize(); *addr -= *size; *addr += ThreadDescriptorSize(); # else *addr = 0; *size = 0; # endif #elif SANITIZER_FREEBSD void** segbase = ThreadSelfSegbase(); *addr = 0; *size = 0; if (segbase != 0) { // tcbalign = 16 // tls_size = round(tls_static_space, tcbalign); // dtv = segbase[1]; // dtv[2] = segbase - tls_static_space; void **dtv = (void**) segbase[1]; *addr = (uptr) dtv[2]; *size = (*addr == 0) ? 0 : ((uptr) segbase[0] - (uptr) dtv[2]); } #else # error "Unknown OS" #endif } +#endif uptr GetTlsSize() { #if SANITIZER_FREEBSD uptr addr, size; GetTls(&addr, &size); return size; #else return g_tls_size; #endif } void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { +#if SANITIZER_GO + // Stub implementation for Go. + *stk_addr = *stk_size = *tls_addr = *tls_size = 0; +#else GetTls(tls_addr, tls_size); uptr stack_top, stack_bottom; GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); *stk_addr = stack_bottom; *stk_size = stack_top - stack_bottom; if (!main) { // If stack and tls intersect, make them non-intersecting. if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) { CHECK_GT(*tls_addr + *tls_size, *stk_addr); CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size); *stk_size -= *tls_size; *tls_addr = *stk_addr + *stk_size; } } +#endif } void AdjustStackSize(void *attr_) { pthread_attr_t *attr = (pthread_attr_t *)attr_; uptr stackaddr = 0; size_t stacksize = 0; my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize); // GLibC will return (0 - stacksize) as the stack address in the case when // stacksize is set, but stackaddr is not. bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0); // We place a lot of tool data into TLS, account for that. const uptr minstacksize = GetTlsSize() + 128*1024; if (stacksize < minstacksize) { if (!stack_set) { if (stacksize != 0) { VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize, minstacksize); pthread_attr_setstacksize(attr, minstacksize); } } else { Printf("Sanitizer: pre-allocated stack size is insufficient: " "%zu < %zu\n", stacksize, minstacksize); Printf("Sanitizer: pthread_create is likely to fail.\n"); } } } #if SANITIZER_ANDROID uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter) { MemoryMappingLayout memory_mapping(false); return memory_mapping.DumpListOfModules(modules, max_modules, filter); } #else // SANITIZER_ANDROID # if !SANITIZER_FREEBSD typedef ElfW(Phdr) Elf_Phdr; # elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2 # define Elf_Phdr XElf32_Phdr # define dl_phdr_info xdl_phdr_info # define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b)) # endif struct DlIteratePhdrData { LoadedModule *modules; uptr current_n; bool first; uptr max_n; string_predicate_t filter; }; static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { DlIteratePhdrData *data = (DlIteratePhdrData*)arg; if (data->current_n == data->max_n) return 0; InternalScopedString module_name(kMaxPathLength); if (data->first) { data->first = false; // First module is the binary itself. ReadBinaryName(module_name.data(), module_name.size()); } else if (info->dlpi_name) { module_name.append("%s", info->dlpi_name); } if (module_name[0] == '\0') return 0; if (data->filter && !data->filter(module_name.data())) return 0; void *mem = &data->modules[data->current_n]; LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(), info->dlpi_addr); data->current_n++; for (int i = 0; i < info->dlpi_phnum; i++) { const Elf_Phdr *phdr = &info->dlpi_phdr[i]; if (phdr->p_type == PT_LOAD) { uptr cur_beg = info->dlpi_addr + phdr->p_vaddr; uptr cur_end = cur_beg + phdr->p_memsz; bool executable = phdr->p_flags & PF_X; cur_module->addAddressRange(cur_beg, cur_end, executable); } } return 0; } uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter) { CHECK(modules); DlIteratePhdrData data = {modules, 0, true, max_modules, filter}; dl_iterate_phdr(dl_iterate_phdr_cb, &data); return data.current_n; } #endif // SANITIZER_ANDROID void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { // Some kinds of sandboxes may forbid filesystem access, so we won't be able // to read the file mappings from /proc/self/maps. Luckily, neither the // process will be able to load additional libraries, so it's fine to use the // cached mappings. MemoryMappingLayout::CacheMemoryMappings(); // Same for /proc/self/exe in the symbolizer. #if !SANITIZER_GO Symbolizer::GetOrInit()->PrepareForSandboxing(); CovPrepareForSandboxing(args); #endif +} + +// getrusage does not give us the current RSS, only the max RSS. +// Still, this is better than nothing if /proc/self/statm is not available +// for some reason, e.g. due to a sandbox. +static uptr GetRSSFromGetrusage() { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage)) // Failed, probably due to a sandbox. + return 0; + return usage.ru_maxrss << 10; // ru_maxrss is in Kb. +} + +uptr GetRSS() { + if (!common_flags()->can_use_proc_maps_statm) + return GetRSSFromGetrusage(); + uptr fd = OpenFile("/proc/self/statm", false); + if ((sptr)fd < 0) + return GetRSSFromGetrusage(); + char buf[64]; + uptr len = internal_read(fd, buf, sizeof(buf) - 1); + internal_close(fd); + if ((sptr)len <= 0) + return 0; + buf[len] = 0; + // The format of the file is: + // 1084 89 69 11 0 79 0 + // We need the second number which is RSS in pages. + char *pos = buf; + // Skip the first number. + while (*pos >= '0' && *pos <= '9') + pos++; + // Skip whitespaces. + while (!(*pos >= '0' && *pos <= '9') && *pos != 0) + pos++; + // Read the number. + uptr rss = 0; + while (*pos >= '0' && *pos <= '9') + rss = rss * 10 + *pos++ - '0'; + return rss * GetPageSizeCached(); } } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_list.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_list.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_list.h (revision 279191) @@ -1,141 +1,145 @@ //===-- sanitizer_list.h ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains implementation of a list class to be used by // ThreadSanitizer, etc run-times. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_LIST_H #define SANITIZER_LIST_H #include "sanitizer_internal_defs.h" namespace __sanitizer { // Intrusive singly-linked list with size(), push_back(), push_front() // pop_front(), append_front() and append_back(). // This class should be a POD (so that it can be put into TLS) // and an object with all zero fields should represent a valid empty list. // This class does not have a CTOR, so clear() should be called on all // non-zero-initialized objects before using. template struct IntrusiveList { friend class Iterator; void clear() { first_ = last_ = 0; size_ = 0; } bool empty() const { return size_ == 0; } uptr size() const { return size_; } void push_back(Item *x) { if (empty()) { x->next = 0; first_ = last_ = x; size_ = 1; } else { x->next = 0; last_->next = x; last_ = x; size_++; } } void push_front(Item *x) { if (empty()) { x->next = 0; first_ = last_ = x; size_ = 1; } else { x->next = first_; first_ = x; size_++; } } void pop_front() { CHECK(!empty()); first_ = first_->next; if (first_ == 0) last_ = 0; size_--; } Item *front() { return first_; } Item *back() { return last_; } void append_front(IntrusiveList *l) { CHECK_NE(this, l); if (l->empty()) return; if (empty()) { *this = *l; } else if (!l->empty()) { l->last_->next = first_; first_ = l->first_; size_ += l->size(); } l->clear(); } void append_back(IntrusiveList *l) { CHECK_NE(this, l); if (l->empty()) return; if (empty()) { *this = *l; } else { last_->next = l->first_; last_ = l->last_; size_ += l->size(); } l->clear(); } void CheckConsistency() { if (size_ == 0) { CHECK_EQ(first_, 0); CHECK_EQ(last_, 0); } else { uptr count = 0; for (Item *i = first_; ; i = i->next) { count++; if (i == last_) break; } CHECK_EQ(size(), count); CHECK_EQ(last_->next, 0); } } - class Iterator { + template + class IteratorBase { public: - explicit Iterator(IntrusiveList *list) + explicit IteratorBase(ListTy *list) : list_(list), current_(list->first_) { } - Item *next() { - Item *ret = current_; + ItemTy *next() { + ItemTy *ret = current_; if (current_) current_ = current_->next; return ret; } bool hasNext() const { return current_ != 0; } private: - IntrusiveList *list_; - Item *current_; + ListTy *list_; + ItemTy *current_; }; + + typedef IteratorBase, Item> Iterator; + typedef IteratorBase, const Item> ConstIterator; // private, don't use directly. uptr size_; Item *first_; Item *last_; }; } // namespace __sanitizer #endif // SANITIZER_LIST_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc (revision 279191) @@ -1,326 +1,333 @@ //===-- sanitizer_mac.cc --------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between various sanitizers' runtime libraries and // implements OSX-specific functions. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_MAC // Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so // the clients will most certainly use 64-bit ones as well. #ifndef _DARWIN_USE_64_BIT_INODE #define _DARWIN_USE_64_BIT_INODE 1 #endif #include #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" #include "sanitizer_mac.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include // for _NSGetEnviron #include #include #include #include #include #include #include #include #include #include #include #include namespace __sanitizer { #include "sanitizer_syscall_generic.inc" // ---------------------- sanitizer_libc.h uptr internal_mmap(void *addr, size_t length, int prot, int flags, int fd, u64 offset) { return (uptr)mmap(addr, length, prot, flags, fd, offset); } uptr internal_munmap(void *addr, uptr length) { return munmap(addr, length); } uptr internal_close(fd_t fd) { return close(fd); } uptr internal_open(const char *filename, int flags) { return open(filename, flags); } uptr internal_open(const char *filename, int flags, u32 mode) { return open(filename, flags, mode); } uptr OpenFile(const char *filename, bool write) { return internal_open(filename, write ? O_WRONLY | O_CREAT : O_RDONLY, 0660); } uptr internal_read(fd_t fd, void *buf, uptr count) { return read(fd, buf, count); } uptr internal_write(fd_t fd, const void *buf, uptr count) { return write(fd, buf, count); } uptr internal_stat(const char *path, void *buf) { return stat(path, (struct stat *)buf); } uptr internal_lstat(const char *path, void *buf) { return lstat(path, (struct stat *)buf); } uptr internal_fstat(fd_t fd, void *buf) { return fstat(fd, (struct stat *)buf); } uptr internal_filesize(fd_t fd) { struct stat st; if (internal_fstat(fd, &st)) return -1; return (uptr)st.st_size; } uptr internal_dup2(int oldfd, int newfd) { return dup2(oldfd, newfd); } uptr internal_readlink(const char *path, char *buf, uptr bufsize) { return readlink(path, buf, bufsize); } +uptr internal_unlink(const char *path) { + return unlink(path); +} + uptr internal_sched_yield() { return sched_yield(); } void internal__exit(int exitcode) { _exit(exitcode); } uptr internal_getpid() { return getpid(); } int internal_sigaction(int signum, const void *act, void *oldact) { return sigaction(signum, (struct sigaction *)act, (struct sigaction *)oldact); } int internal_fork() { // TODO(glider): this may call user's pthread_atfork() handlers which is bad. return fork(); } uptr internal_rename(const char *oldpath, const char *newpath) { return rename(oldpath, newpath); } uptr internal_ftruncate(fd_t fd, uptr size) { return ftruncate(fd, size); } // ----------------- sanitizer_common.h bool FileExists(const char *filename) { struct stat st; if (stat(filename, &st)) return false; // Sanity check: filename is a regular file. return S_ISREG(st.st_mode); } uptr GetTid() { return reinterpret_cast(pthread_self()); } void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom) { CHECK(stack_top); CHECK(stack_bottom); uptr stacksize = pthread_get_stacksize_np(pthread_self()); // pthread_get_stacksize_np() returns an incorrect stack size for the main // thread on Mavericks. See // https://code.google.com/p/address-sanitizer/issues/detail?id=261 if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization && stacksize == (1 << 19)) { struct rlimit rl; CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); // Most often rl.rlim_cur will be the desired 8M. if (rl.rlim_cur < kMaxThreadStackSize) { stacksize = rl.rlim_cur; } else { stacksize = kMaxThreadStackSize; } } void *stackaddr = pthread_get_stackaddr_np(pthread_self()); *stack_top = (uptr)stackaddr; *stack_bottom = *stack_top - stacksize; } const char *GetEnv(const char *name) { char ***env_ptr = _NSGetEnviron(); if (!env_ptr) { Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is " "called after libSystem_initializer().\n"); CHECK(env_ptr); } char **environ = *env_ptr; CHECK(environ); uptr name_len = internal_strlen(name); while (*environ != 0) { uptr len = internal_strlen(*environ); if (len > name_len) { const char *p = *environ; if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { // Match. return *environ + name_len + 1; // String starting after =. } } environ++; } return 0; } void ReExec() { UNIMPLEMENTED(); } void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { (void)args; // Nothing here for now. } uptr GetPageSize() { return sysconf(_SC_PAGESIZE); } -BlockingMutex::BlockingMutex(LinkerInitialized) { - // We assume that OS_SPINLOCK_INIT is zero -} - BlockingMutex::BlockingMutex() { internal_memset(this, 0, sizeof(*this)); } void BlockingMutex::Lock() { CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_)); CHECK_EQ(OS_SPINLOCK_INIT, 0); CHECK_NE(owner_, (uptr)pthread_self()); OSSpinLockLock((OSSpinLock*)&opaque_storage_); CHECK(!owner_); owner_ = (uptr)pthread_self(); } void BlockingMutex::Unlock() { CHECK(owner_ == (uptr)pthread_self()); owner_ = 0; OSSpinLockUnlock((OSSpinLock*)&opaque_storage_); } void BlockingMutex::CheckLocked() { CHECK_EQ((uptr)pthread_self(), owner_); } u64 NanoTime() { return 0; } uptr GetTlsSize() { return 0; } void InitTlsSize() { } void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { #ifndef SANITIZER_GO uptr stack_top, stack_bottom; GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); *stk_addr = stack_bottom; *stk_size = stack_top - stack_bottom; *tls_addr = 0; *tls_size = 0; #else *stk_addr = 0; *stk_size = 0; *tls_addr = 0; *tls_size = 0; #endif } uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter) { MemoryMappingLayout memory_mapping(false); return memory_mapping.DumpListOfModules(modules, max_modules, filter); } bool IsDeadlySignal(int signum) { return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; } MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED; MacosVersion GetMacosVersionInternal() { int mib[2] = { CTL_KERN, KERN_OSRELEASE }; char version[100]; uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]); for (uptr i = 0; i < maxlen; i++) version[i] = '\0'; // Get the version length. CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1); CHECK_LT(len, maxlen); CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1); switch (version[0]) { case '9': return MACOS_VERSION_LEOPARD; case '1': { switch (version[1]) { case '0': return MACOS_VERSION_SNOW_LEOPARD; case '1': return MACOS_VERSION_LION; case '2': return MACOS_VERSION_MOUNTAIN_LION; case '3': return MACOS_VERSION_MAVERICKS; case '4': return MACOS_VERSION_YOSEMITE; - default: return MACOS_VERSION_UNKNOWN; + default: + if (IsDigit(version[1])) + return MACOS_VERSION_UNKNOWN_NEWER; + else + return MACOS_VERSION_UNKNOWN; } } default: return MACOS_VERSION_UNKNOWN; } } MacosVersion GetMacosVersion() { atomic_uint32_t *cache = reinterpret_cast(&cached_macos_version); MacosVersion result = static_cast(atomic_load(cache, memory_order_acquire)); if (result == MACOS_VERSION_UNINITIALIZED) { result = GetMacosVersionInternal(); atomic_store(cache, result, memory_order_release); } return result; } uptr GetRSS() { return 0; } + +void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } +void internal_join_thread(void *th) { } } // namespace __sanitizer #endif // SANITIZER_MAC Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mac.h (revision 279191) @@ -1,37 +1,38 @@ //===-- sanitizer_mac.h -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between various sanitizers' runtime libraries and // provides definitions for OSX-specific functions. //===----------------------------------------------------------------------===// #ifndef SANITIZER_MAC_H #define SANITIZER_MAC_H #include "sanitizer_platform.h" #if SANITIZER_MAC namespace __sanitizer { enum MacosVersion { MACOS_VERSION_UNINITIALIZED = 0, MACOS_VERSION_UNKNOWN, MACOS_VERSION_LEOPARD, MACOS_VERSION_SNOW_LEOPARD, MACOS_VERSION_LION, MACOS_VERSION_MOUNTAIN_LION, MACOS_VERSION_MAVERICKS, MACOS_VERSION_YOSEMITE, + MACOS_VERSION_UNKNOWN_NEWER }; MacosVersion GetMacosVersion(); } // namespace __sanitizer #endif // SANITIZER_MAC #endif // SANITIZER_MAC_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h (revision 279191) @@ -1,213 +1,219 @@ //===-- sanitizer_mutex.h ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer/AddressSanitizer runtime. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_MUTEX_H #define SANITIZER_MUTEX_H #include "sanitizer_atomic.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" namespace __sanitizer { class StaticSpinMutex { public: void Init() { atomic_store(&state_, 0, memory_order_relaxed); } void Lock() { if (TryLock()) return; LockSlow(); } bool TryLock() { return atomic_exchange(&state_, 1, memory_order_acquire) == 0; } void Unlock() { atomic_store(&state_, 0, memory_order_release); } void CheckLocked() { CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1); } private: atomic_uint8_t state_; void NOINLINE LockSlow() { for (int i = 0;; i++) { if (i < 10) proc_yield(10); else internal_sched_yield(); if (atomic_load(&state_, memory_order_relaxed) == 0 && atomic_exchange(&state_, 1, memory_order_acquire) == 0) return; } } }; class SpinMutex : public StaticSpinMutex { public: SpinMutex() { Init(); } private: SpinMutex(const SpinMutex&); void operator=(const SpinMutex&); }; class BlockingMutex { public: +#if SANITIZER_WINDOWS + // Windows does not currently support LinkerInitialized explicit BlockingMutex(LinkerInitialized); +#else + explicit constexpr BlockingMutex(LinkerInitialized) + : opaque_storage_ {0, }, owner_(0) {} +#endif BlockingMutex(); void Lock(); void Unlock(); void CheckLocked(); private: uptr opaque_storage_[10]; uptr owner_; // for debugging }; // Reader-writer spin mutex. class RWMutex { public: RWMutex() { atomic_store(&state_, kUnlocked, memory_order_relaxed); } ~RWMutex() { CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked); } void Lock() { u32 cmp = kUnlocked; if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock, memory_order_acquire)) return; LockSlow(); } void Unlock() { u32 prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release); DCHECK_NE(prev & kWriteLock, 0); (void)prev; } void ReadLock() { u32 prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire); if ((prev & kWriteLock) == 0) return; ReadLockSlow(); } void ReadUnlock() { u32 prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release); DCHECK_EQ(prev & kWriteLock, 0); DCHECK_GT(prev & ~kWriteLock, 0); (void)prev; } void CheckLocked() { CHECK_NE(atomic_load(&state_, memory_order_relaxed), kUnlocked); } private: atomic_uint32_t state_; enum { kUnlocked = 0, kWriteLock = 1, kReadLock = 2 }; void NOINLINE LockSlow() { for (int i = 0;; i++) { if (i < 10) proc_yield(10); else internal_sched_yield(); u32 cmp = atomic_load(&state_, memory_order_relaxed); if (cmp == kUnlocked && atomic_compare_exchange_weak(&state_, &cmp, kWriteLock, memory_order_acquire)) return; } } void NOINLINE ReadLockSlow() { for (int i = 0;; i++) { if (i < 10) proc_yield(10); else internal_sched_yield(); u32 prev = atomic_load(&state_, memory_order_acquire); if ((prev & kWriteLock) == 0) return; } } RWMutex(const RWMutex&); void operator = (const RWMutex&); }; template class GenericScopedLock { public: explicit GenericScopedLock(MutexType *mu) : mu_(mu) { mu_->Lock(); } ~GenericScopedLock() { mu_->Unlock(); } private: MutexType *mu_; GenericScopedLock(const GenericScopedLock&); void operator=(const GenericScopedLock&); }; template class GenericScopedReadLock { public: explicit GenericScopedReadLock(MutexType *mu) : mu_(mu) { mu_->ReadLock(); } ~GenericScopedReadLock() { mu_->ReadUnlock(); } private: MutexType *mu_; GenericScopedReadLock(const GenericScopedReadLock&); void operator=(const GenericScopedReadLock&); }; typedef GenericScopedLock SpinMutexLock; typedef GenericScopedLock BlockingMutexLock; typedef GenericScopedLock RWMutexLock; typedef GenericScopedReadLock RWMutexReadLock; } // namespace __sanitizer #endif // SANITIZER_MUTEX_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h (revision 279191) @@ -1,240 +1,247 @@ //===-- sanitizer_platform_interceptors.h -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines macro telling whether sanitizer tools can/should intercept // given library functions on a given platform. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H #define SANITIZER_PLATFORM_INTERCEPTORS_H #include "sanitizer_internal_defs.h" #if !SANITIZER_WINDOWS # define SI_NOT_WINDOWS 1 # include "sanitizer_platform_limits_posix.h" #else # define SI_NOT_WINDOWS 0 #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID # define SI_LINUX_NOT_ANDROID 1 #else # define SI_LINUX_NOT_ANDROID 0 #endif #if SANITIZER_FREEBSD # define SI_FREEBSD 1 #else # define SI_FREEBSD 0 #endif #if SANITIZER_LINUX # define SI_LINUX 1 #else # define SI_LINUX 0 #endif #if SANITIZER_MAC # define SI_MAC 1 #else # define SI_MAC 0 #endif #if SANITIZER_IOS # define SI_IOS 1 #else # define SI_IOS 0 #endif #define SANITIZER_INTERCEPT_STRCMP 1 #define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MEMCHR 1 -#define SANITIZER_INTERCEPT_MEMRCHR SI_LINUX +#define SANITIZER_INTERCEPT_MEMRCHR SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PREAD SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_WRITE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PWRITE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_READV SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_WRITEV SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_PREADV SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PREADV SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PRCTL SI_LINUX #define SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX_NOT_ANDROID #ifndef SANITIZER_INTERCEPT_PRINTF # define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PRINTF_L SI_FREEBSD # define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID #endif #define SANITIZER_INTERCEPT_FREXP 1 #define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \ - SI_MAC || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_GETPWENT SI_MAC || SI_LINUX_NOT_ANDROID + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETPWENT \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_GETPWENT_R SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETPWENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SETPWENT SI_MAC || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_LINUX +#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GLOB SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_WAIT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_INET SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETADDRINFO SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETNAMEINFO SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_LINUX -#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_GETHOSTENT_R SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_FREEBSD || SI_LINUX +#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_FREEBSD || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_FREEBSD || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETHOSTENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_IOCTL SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_INET_ATON SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SYSINFO SI_LINUX #define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \ (defined(__i386) || defined (__x86_64)) // NOLINT #define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_WCSNRTOMBS \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_CONFSTR SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_CONFSTR \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_SCHED_GETPARAM SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SCANDIR SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_WORDEXP (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_WORDEXP \ + SI_FREEBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGSETOPS \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX #define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_STATFS SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_STATFS SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATFS64 \ (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_STATVFS SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ETHER_NTOA_ATON SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_ETHER_HOST SI_MAC || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_ETHER_R SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_ETHER_HOST \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_ETHER_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SHMCTL \ ((SI_FREEBSD || SI_LINUX_NOT_ANDROID) && SANITIZER_WORDSIZE == 64) #define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \ - SI_MAC || SI_LINUX_NOT_ANDROID + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL \ SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING \ SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SINCOS SI_LINUX #define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX +#define SANITIZER_INTERCEPT_LGAMMA_R SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_RAND_R SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TIMES SI_NOT_WINDOWS // FIXME: getline seems to be available on OSX 10.7 #define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD #define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \ SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TLS_GET_ADDR \ SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_LISTXATTR SI_LINUX #define SANITIZER_INTERCEPT_GETXATTR SI_LINUX #define SANITIZER_INTERCEPT_GETRESID SI_LINUX #define SANITIZER_INTERCEPT_GETIFADDRS SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_IF_INDEXTONAME SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_CAPGET SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_AEABI_MEM SI_LINUX && defined(__arm__) #define SANITIZER_INTERCEPT___BZERO SI_MAC #define SANITIZER_INTERCEPT_FTIME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TSEARCH SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_FOPEN SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FOPEN64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_OPEN_MEMSTREAM SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE \ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_GETPASS SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cc (revision 279191) @@ -1,1188 +1,1194 @@ //===-- sanitizer_platform_limits_posix.cc --------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of Sanitizer common code. // // Sizes and layouts of platform-specific POSIX data structures. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC // Tests in this file assume that off_t-dependent data structures match the // libc ABI. For example, struct dirent here is what readdir() function (as // exported from libc) returns, and not the user-facing "dirent", which // depends on _FILE_OFFSET_BITS setting. // To get this "true" dirent definition, we undefine _FILE_OFFSET_BITS below. #ifdef _FILE_OFFSET_BITS #undef _FILE_OFFSET_BITS #endif #if SANITIZER_FREEBSD #define _WANT_RTENTRY #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !SANITIZER_ANDROID #include #include #endif #if SANITIZER_LINUX #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #if SANITIZER_FREEBSD # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include #define _KERNEL // to declare 'shminfo' structure # include #undef _KERNEL #undef INLINE // to avoid clashes with sanitizers' definitions #endif #if SANITIZER_FREEBSD || SANITIZER_IOS #undef IOC_DIRMASK #endif #if SANITIZER_LINUX || SANITIZER_FREEBSD # include # include #endif #if !SANITIZER_ANDROID #include #include #include #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if SANITIZER_ANDROID #include #include #include #include #endif #if SANITIZER_LINUX #include #include #include #include #endif // SANITIZER_LINUX #if SANITIZER_MAC #include #include #include #endif // Include these after system headers to avoid name clashes and ambiguities. #include "sanitizer_internal_defs.h" #include "sanitizer_platform_limits_posix.h" namespace __sanitizer { unsigned struct_utsname_sz = sizeof(struct utsname); unsigned struct_stat_sz = sizeof(struct stat); #if !SANITIZER_IOS && !SANITIZER_FREEBSD unsigned struct_stat64_sz = sizeof(struct stat64); #endif // !SANITIZER_IOS && !SANITIZER_FREEBSD unsigned struct_rusage_sz = sizeof(struct rusage); unsigned struct_tm_sz = sizeof(struct tm); unsigned struct_passwd_sz = sizeof(struct passwd); unsigned struct_group_sz = sizeof(struct group); unsigned siginfo_t_sz = sizeof(siginfo_t); unsigned struct_sigaction_sz = sizeof(struct sigaction); unsigned struct_itimerval_sz = sizeof(struct itimerval); unsigned pthread_t_sz = sizeof(pthread_t); unsigned pthread_cond_t_sz = sizeof(pthread_cond_t); unsigned pid_t_sz = sizeof(pid_t); unsigned timeval_sz = sizeof(timeval); unsigned uid_t_sz = sizeof(uid_t); unsigned gid_t_sz = sizeof(gid_t); unsigned mbstate_t_sz = sizeof(mbstate_t); unsigned sigset_t_sz = sizeof(sigset_t); unsigned struct_timezone_sz = sizeof(struct timezone); unsigned struct_tms_sz = sizeof(struct tms); unsigned struct_sigevent_sz = sizeof(struct sigevent); unsigned struct_sched_param_sz = sizeof(struct sched_param); #if SANITIZER_MAC && !SANITIZER_IOS unsigned struct_statfs64_sz = sizeof(struct statfs64); #endif // SANITIZER_MAC && !SANITIZER_IOS #if !SANITIZER_ANDROID unsigned struct_statfs_sz = sizeof(struct statfs); unsigned struct_sockaddr_sz = sizeof(struct sockaddr); unsigned ucontext_t_sz = sizeof(ucontext_t); #endif // !SANITIZER_ANDROID #if SANITIZER_LINUX unsigned struct_epoll_event_sz = sizeof(struct epoll_event); unsigned struct_sysinfo_sz = sizeof(struct sysinfo); unsigned __user_cap_header_struct_sz = sizeof(struct __user_cap_header_struct); unsigned __user_cap_data_struct_sz = sizeof(struct __user_cap_data_struct); unsigned struct_new_utsname_sz = sizeof(struct new_utsname); unsigned struct_old_utsname_sz = sizeof(struct old_utsname); unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname); #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD unsigned struct_rlimit_sz = sizeof(struct rlimit); unsigned struct_timespec_sz = sizeof(struct timespec); unsigned struct_utimbuf_sz = sizeof(struct utimbuf); unsigned struct_itimerspec_sz = sizeof(struct itimerspec); #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned struct_ustat_sz = sizeof(struct ustat); unsigned struct_rlimit64_sz = sizeof(struct rlimit64); unsigned struct_statvfs64_sz = sizeof(struct statvfs64); #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned struct_timex_sz = sizeof(struct timex); unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds); unsigned struct_mq_attr_sz = sizeof(struct mq_attr); unsigned struct_statvfs_sz = sizeof(struct statvfs); #endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID uptr sig_ign = (uptr)SIG_IGN; uptr sig_dfl = (uptr)SIG_DFL; uptr sa_siginfo = (uptr)SA_SIGINFO; #if SANITIZER_LINUX int e_tabsz = (int)E_TABSZ; #endif #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned struct_shminfo_sz = sizeof(struct shminfo); unsigned struct_shm_info_sz = sizeof(struct shm_info); int shmctl_ipc_stat = (int)IPC_STAT; int shmctl_ipc_info = (int)IPC_INFO; int shmctl_shm_info = (int)SHM_INFO; int shmctl_shm_stat = (int)SHM_STAT; #endif int map_fixed = MAP_FIXED; int af_inet = (int)AF_INET; int af_inet6 = (int)AF_INET6; uptr __sanitizer_in_addr_sz(int af) { if (af == AF_INET) return sizeof(struct in_addr); else if (af == AF_INET6) return sizeof(struct in6_addr); else return 0; } #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID int glob_nomatch = GLOB_NOMATCH; int glob_altdirfunc = GLOB_ALTDIRFUNC; #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ (defined(__i386) || defined(__x86_64)) unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct); unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); #ifdef __x86_64 unsigned struct_user_fpxregs_struct_sz = 0; #else unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); #endif int ptrace_peektext = PTRACE_PEEKTEXT; int ptrace_peekdata = PTRACE_PEEKDATA; int ptrace_peekuser = PTRACE_PEEKUSER; int ptrace_getregs = PTRACE_GETREGS; int ptrace_setregs = PTRACE_SETREGS; int ptrace_getfpregs = PTRACE_GETFPREGS; int ptrace_setfpregs = PTRACE_SETFPREGS; int ptrace_getfpxregs = PTRACE_GETFPXREGS; int ptrace_setfpxregs = PTRACE_SETFPXREGS; int ptrace_geteventmsg = PTRACE_GETEVENTMSG; #if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \ (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO)) int ptrace_getsiginfo = PTRACE_GETSIGINFO; int ptrace_setsiginfo = PTRACE_SETSIGINFO; #else int ptrace_getsiginfo = -1; int ptrace_setsiginfo = -1; #endif // PTRACE_GETSIGINFO/PTRACE_SETSIGINFO #if defined(PTRACE_GETREGSET) && defined(PTRACE_SETREGSET) int ptrace_getregset = PTRACE_GETREGSET; int ptrace_setregset = PTRACE_SETREGSET; #else int ptrace_getregset = -1; int ptrace_setregset = -1; #endif // PTRACE_GETREGSET/PTRACE_SETREGSET #endif unsigned path_max = PATH_MAX; // ioctl arguments unsigned struct_arpreq_sz = sizeof(struct arpreq); unsigned struct_ifreq_sz = sizeof(struct ifreq); unsigned struct_termios_sz = sizeof(struct termios); unsigned struct_winsize_sz = sizeof(struct winsize); #if SANITIZER_LINUX unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf); unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession); unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio); unsigned struct_cdrom_subchnl_sz = sizeof(struct cdrom_subchnl); unsigned struct_cdrom_ti_sz = sizeof(struct cdrom_ti); unsigned struct_cdrom_tocentry_sz = sizeof(struct cdrom_tocentry); unsigned struct_cdrom_tochdr_sz = sizeof(struct cdrom_tochdr); unsigned struct_cdrom_volctrl_sz = sizeof(struct cdrom_volctrl); unsigned struct_ff_effect_sz = sizeof(struct ff_effect); unsigned struct_floppy_drive_params_sz = sizeof(struct floppy_drive_params); unsigned struct_floppy_drive_struct_sz = sizeof(struct floppy_drive_struct); unsigned struct_floppy_fdc_state_sz = sizeof(struct floppy_fdc_state); unsigned struct_floppy_max_errors_sz = sizeof(struct floppy_max_errors); unsigned struct_floppy_raw_cmd_sz = sizeof(struct floppy_raw_cmd); unsigned struct_floppy_struct_sz = sizeof(struct floppy_struct); unsigned struct_floppy_write_errors_sz = sizeof(struct floppy_write_errors); unsigned struct_format_descr_sz = sizeof(struct format_descr); unsigned struct_hd_driveid_sz = sizeof(struct hd_driveid); unsigned struct_hd_geometry_sz = sizeof(struct hd_geometry); unsigned struct_input_absinfo_sz = sizeof(struct input_absinfo); unsigned struct_input_id_sz = sizeof(struct input_id); unsigned struct_mtpos_sz = sizeof(struct mtpos); unsigned struct_termio_sz = sizeof(struct termio); unsigned struct_vt_consize_sz = sizeof(struct vt_consize); unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes); unsigned struct_vt_stat_sz = sizeof(struct vt_stat); #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD #if SOUND_VERSION >= 0x040000 unsigned struct_copr_buffer_sz = 0; unsigned struct_copr_debug_buf_sz = 0; unsigned struct_copr_msg_sz = 0; #else unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer); unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf); unsigned struct_copr_msg_sz = sizeof(struct copr_msg); #endif unsigned struct_midi_info_sz = sizeof(struct midi_info); unsigned struct_mtget_sz = sizeof(struct mtget); unsigned struct_mtop_sz = sizeof(struct mtop); unsigned struct_rtentry_sz = sizeof(struct rtentry); unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument); unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec); unsigned struct_synth_info_sz = sizeof(struct synth_info); unsigned struct_vt_mode_sz = sizeof(struct vt_mode); #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct); unsigned struct_cyclades_monitor_sz = sizeof(struct cyclades_monitor); #if EV_VERSION > (0x010000) unsigned struct_input_keymap_entry_sz = sizeof(struct input_keymap_entry); #else unsigned struct_input_keymap_entry_sz = 0; #endif unsigned struct_ipx_config_data_sz = sizeof(struct ipx_config_data); unsigned struct_kbdiacrs_sz = sizeof(struct kbdiacrs); unsigned struct_kbentry_sz = sizeof(struct kbentry); unsigned struct_kbkeycode_sz = sizeof(struct kbkeycode); unsigned struct_kbsentry_sz = sizeof(struct kbsentry); unsigned struct_mtconfiginfo_sz = sizeof(struct mtconfiginfo); unsigned struct_nr_parms_struct_sz = sizeof(struct nr_parms_struct); unsigned struct_scc_modem_sz = sizeof(struct scc_modem); unsigned struct_scc_stat_sz = sizeof(struct scc_stat); unsigned struct_serial_multiport_struct_sz = sizeof(struct serial_multiport_struct); unsigned struct_serial_struct_sz = sizeof(struct serial_struct); unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25); unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc); unsigned struct_unimapinit_sz = sizeof(struct unimapinit); #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info); unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats); #endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID #if !SANITIZER_ANDROID && !SANITIZER_MAC unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req); unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req); #endif const unsigned IOCTL_NOT_PRESENT = 0; unsigned IOCTL_FIOASYNC = FIOASYNC; unsigned IOCTL_FIOCLEX = FIOCLEX; unsigned IOCTL_FIOGETOWN = FIOGETOWN; unsigned IOCTL_FIONBIO = FIONBIO; unsigned IOCTL_FIONCLEX = FIONCLEX; unsigned IOCTL_FIOSETOWN = FIOSETOWN; unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI; unsigned IOCTL_SIOCATMARK = SIOCATMARK; unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI; unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR; unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR; unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF; unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR; unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS; unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC; unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU; unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK; unsigned IOCTL_SIOCGPGRP = SIOCGPGRP; unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR; unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR; unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR; unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS; unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC; unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU; unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK; unsigned IOCTL_SIOCSPGRP = SIOCSPGRP; unsigned IOCTL_TIOCCONS = TIOCCONS; unsigned IOCTL_TIOCEXCL = TIOCEXCL; unsigned IOCTL_TIOCGETD = TIOCGETD; unsigned IOCTL_TIOCGPGRP = TIOCGPGRP; unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ; unsigned IOCTL_TIOCMBIC = TIOCMBIC; unsigned IOCTL_TIOCMBIS = TIOCMBIS; unsigned IOCTL_TIOCMGET = TIOCMGET; unsigned IOCTL_TIOCMSET = TIOCMSET; unsigned IOCTL_TIOCNOTTY = TIOCNOTTY; unsigned IOCTL_TIOCNXCL = TIOCNXCL; unsigned IOCTL_TIOCOUTQ = TIOCOUTQ; unsigned IOCTL_TIOCPKT = TIOCPKT; unsigned IOCTL_TIOCSCTTY = TIOCSCTTY; unsigned IOCTL_TIOCSETD = TIOCSETD; unsigned IOCTL_TIOCSPGRP = TIOCSPGRP; unsigned IOCTL_TIOCSTI = TIOCSTI; unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ; #if ((SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID) unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT; unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT; #endif #if SANITIZER_LINUX unsigned IOCTL_EVIOCGABS = EVIOCGABS(0); unsigned IOCTL_EVIOCGBIT = EVIOCGBIT(0, 0); unsigned IOCTL_EVIOCGEFFECTS = EVIOCGEFFECTS; unsigned IOCTL_EVIOCGID = EVIOCGID; unsigned IOCTL_EVIOCGKEY = EVIOCGKEY(0); unsigned IOCTL_EVIOCGKEYCODE = EVIOCGKEYCODE; unsigned IOCTL_EVIOCGLED = EVIOCGLED(0); unsigned IOCTL_EVIOCGNAME = EVIOCGNAME(0); unsigned IOCTL_EVIOCGPHYS = EVIOCGPHYS(0); unsigned IOCTL_EVIOCGRAB = EVIOCGRAB; unsigned IOCTL_EVIOCGREP = EVIOCGREP; unsigned IOCTL_EVIOCGSND = EVIOCGSND(0); unsigned IOCTL_EVIOCGSW = EVIOCGSW(0); unsigned IOCTL_EVIOCGUNIQ = EVIOCGUNIQ(0); unsigned IOCTL_EVIOCGVERSION = EVIOCGVERSION; unsigned IOCTL_EVIOCRMFF = EVIOCRMFF; unsigned IOCTL_EVIOCSABS = EVIOCSABS(0); unsigned IOCTL_EVIOCSFF = EVIOCSFF; unsigned IOCTL_EVIOCSKEYCODE = EVIOCSKEYCODE; unsigned IOCTL_EVIOCSREP = EVIOCSREP; unsigned IOCTL_BLKFLSBUF = BLKFLSBUF; unsigned IOCTL_BLKGETSIZE = BLKGETSIZE; unsigned IOCTL_BLKRAGET = BLKRAGET; unsigned IOCTL_BLKRASET = BLKRASET; unsigned IOCTL_BLKROGET = BLKROGET; unsigned IOCTL_BLKROSET = BLKROSET; unsigned IOCTL_BLKRRPART = BLKRRPART; unsigned IOCTL_CDROMAUDIOBUFSIZ = CDROMAUDIOBUFSIZ; unsigned IOCTL_CDROMEJECT = CDROMEJECT; unsigned IOCTL_CDROMEJECT_SW = CDROMEJECT_SW; unsigned IOCTL_CDROMMULTISESSION = CDROMMULTISESSION; unsigned IOCTL_CDROMPAUSE = CDROMPAUSE; unsigned IOCTL_CDROMPLAYMSF = CDROMPLAYMSF; unsigned IOCTL_CDROMPLAYTRKIND = CDROMPLAYTRKIND; unsigned IOCTL_CDROMREADAUDIO = CDROMREADAUDIO; unsigned IOCTL_CDROMREADCOOKED = CDROMREADCOOKED; unsigned IOCTL_CDROMREADMODE1 = CDROMREADMODE1; unsigned IOCTL_CDROMREADMODE2 = CDROMREADMODE2; unsigned IOCTL_CDROMREADRAW = CDROMREADRAW; unsigned IOCTL_CDROMREADTOCENTRY = CDROMREADTOCENTRY; unsigned IOCTL_CDROMREADTOCHDR = CDROMREADTOCHDR; unsigned IOCTL_CDROMRESET = CDROMRESET; unsigned IOCTL_CDROMRESUME = CDROMRESUME; unsigned IOCTL_CDROMSEEK = CDROMSEEK; unsigned IOCTL_CDROMSTART = CDROMSTART; unsigned IOCTL_CDROMSTOP = CDROMSTOP; unsigned IOCTL_CDROMSUBCHNL = CDROMSUBCHNL; unsigned IOCTL_CDROMVOLCTRL = CDROMVOLCTRL; unsigned IOCTL_CDROMVOLREAD = CDROMVOLREAD; unsigned IOCTL_CDROM_GET_UPC = CDROM_GET_UPC; unsigned IOCTL_FDCLRPRM = FDCLRPRM; unsigned IOCTL_FDDEFPRM = FDDEFPRM; unsigned IOCTL_FDFLUSH = FDFLUSH; unsigned IOCTL_FDFMTBEG = FDFMTBEG; unsigned IOCTL_FDFMTEND = FDFMTEND; unsigned IOCTL_FDFMTTRK = FDFMTTRK; unsigned IOCTL_FDGETDRVPRM = FDGETDRVPRM; unsigned IOCTL_FDGETDRVSTAT = FDGETDRVSTAT; unsigned IOCTL_FDGETDRVTYP = FDGETDRVTYP; unsigned IOCTL_FDGETFDCSTAT = FDGETFDCSTAT; unsigned IOCTL_FDGETMAXERRS = FDGETMAXERRS; unsigned IOCTL_FDGETPRM = FDGETPRM; unsigned IOCTL_FDMSGOFF = FDMSGOFF; unsigned IOCTL_FDMSGON = FDMSGON; unsigned IOCTL_FDPOLLDRVSTAT = FDPOLLDRVSTAT; unsigned IOCTL_FDRAWCMD = FDRAWCMD; unsigned IOCTL_FDRESET = FDRESET; unsigned IOCTL_FDSETDRVPRM = FDSETDRVPRM; unsigned IOCTL_FDSETEMSGTRESH = FDSETEMSGTRESH; unsigned IOCTL_FDSETMAXERRS = FDSETMAXERRS; unsigned IOCTL_FDSETPRM = FDSETPRM; unsigned IOCTL_FDTWADDLE = FDTWADDLE; unsigned IOCTL_FDWERRORCLR = FDWERRORCLR; unsigned IOCTL_FDWERRORGET = FDWERRORGET; unsigned IOCTL_HDIO_DRIVE_CMD = HDIO_DRIVE_CMD; unsigned IOCTL_HDIO_GETGEO = HDIO_GETGEO; unsigned IOCTL_HDIO_GET_32BIT = HDIO_GET_32BIT; unsigned IOCTL_HDIO_GET_DMA = HDIO_GET_DMA; unsigned IOCTL_HDIO_GET_IDENTITY = HDIO_GET_IDENTITY; unsigned IOCTL_HDIO_GET_KEEPSETTINGS = HDIO_GET_KEEPSETTINGS; unsigned IOCTL_HDIO_GET_MULTCOUNT = HDIO_GET_MULTCOUNT; unsigned IOCTL_HDIO_GET_NOWERR = HDIO_GET_NOWERR; unsigned IOCTL_HDIO_GET_UNMASKINTR = HDIO_GET_UNMASKINTR; unsigned IOCTL_HDIO_SET_32BIT = HDIO_SET_32BIT; unsigned IOCTL_HDIO_SET_DMA = HDIO_SET_DMA; unsigned IOCTL_HDIO_SET_KEEPSETTINGS = HDIO_SET_KEEPSETTINGS; unsigned IOCTL_HDIO_SET_MULTCOUNT = HDIO_SET_MULTCOUNT; unsigned IOCTL_HDIO_SET_NOWERR = HDIO_SET_NOWERR; unsigned IOCTL_HDIO_SET_UNMASKINTR = HDIO_SET_UNMASKINTR; unsigned IOCTL_MTIOCPOS = MTIOCPOS; unsigned IOCTL_PPPIOCGASYNCMAP = PPPIOCGASYNCMAP; unsigned IOCTL_PPPIOCGDEBUG = PPPIOCGDEBUG; unsigned IOCTL_PPPIOCGFLAGS = PPPIOCGFLAGS; unsigned IOCTL_PPPIOCGUNIT = PPPIOCGUNIT; unsigned IOCTL_PPPIOCGXASYNCMAP = PPPIOCGXASYNCMAP; unsigned IOCTL_PPPIOCSASYNCMAP = PPPIOCSASYNCMAP; unsigned IOCTL_PPPIOCSDEBUG = PPPIOCSDEBUG; unsigned IOCTL_PPPIOCSFLAGS = PPPIOCSFLAGS; unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID; unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU; unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP; unsigned IOCTL_SIOCADDRT = SIOCADDRT; unsigned IOCTL_SIOCDARP = SIOCDARP; unsigned IOCTL_SIOCDELRT = SIOCDELRT; unsigned IOCTL_SIOCDRARP = SIOCDRARP; unsigned IOCTL_SIOCGARP = SIOCGARP; unsigned IOCTL_SIOCGIFENCAP = SIOCGIFENCAP; unsigned IOCTL_SIOCGIFHWADDR = SIOCGIFHWADDR; unsigned IOCTL_SIOCGIFMAP = SIOCGIFMAP; unsigned IOCTL_SIOCGIFMEM = SIOCGIFMEM; unsigned IOCTL_SIOCGIFNAME = SIOCGIFNAME; unsigned IOCTL_SIOCGIFSLAVE = SIOCGIFSLAVE; unsigned IOCTL_SIOCGRARP = SIOCGRARP; unsigned IOCTL_SIOCGSTAMP = SIOCGSTAMP; unsigned IOCTL_SIOCSARP = SIOCSARP; unsigned IOCTL_SIOCSIFENCAP = SIOCSIFENCAP; unsigned IOCTL_SIOCSIFHWADDR = SIOCSIFHWADDR; unsigned IOCTL_SIOCSIFLINK = SIOCSIFLINK; unsigned IOCTL_SIOCSIFMAP = SIOCSIFMAP; unsigned IOCTL_SIOCSIFMEM = SIOCSIFMEM; unsigned IOCTL_SIOCSIFSLAVE = SIOCSIFSLAVE; unsigned IOCTL_SIOCSRARP = SIOCSRARP; # if SOUND_VERSION >= 0x040000 unsigned IOCTL_SNDCTL_COPR_HALT = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_LOAD = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_RCODE = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_RCVMSG = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_RDATA = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_RESET = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_RUN = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_SENDMSG = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_WCODE = IOCTL_NOT_PRESENT; unsigned IOCTL_SNDCTL_COPR_WDATA = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_READ_BITS = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_READ_CHANNELS = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_READ_FILTER = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_READ_RATE = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = IOCTL_NOT_PRESENT; unsigned IOCTL_SOUND_PCM_WRITE_FILTER = IOCTL_NOT_PRESENT; # else // SOUND_VERSION unsigned IOCTL_SNDCTL_COPR_HALT = SNDCTL_COPR_HALT; unsigned IOCTL_SNDCTL_COPR_LOAD = SNDCTL_COPR_LOAD; unsigned IOCTL_SNDCTL_COPR_RCODE = SNDCTL_COPR_RCODE; unsigned IOCTL_SNDCTL_COPR_RCVMSG = SNDCTL_COPR_RCVMSG; unsigned IOCTL_SNDCTL_COPR_RDATA = SNDCTL_COPR_RDATA; unsigned IOCTL_SNDCTL_COPR_RESET = SNDCTL_COPR_RESET; unsigned IOCTL_SNDCTL_COPR_RUN = SNDCTL_COPR_RUN; unsigned IOCTL_SNDCTL_COPR_SENDMSG = SNDCTL_COPR_SENDMSG; unsigned IOCTL_SNDCTL_COPR_WCODE = SNDCTL_COPR_WCODE; unsigned IOCTL_SNDCTL_COPR_WDATA = SNDCTL_COPR_WDATA; unsigned IOCTL_SOUND_PCM_READ_BITS = SOUND_PCM_READ_BITS; unsigned IOCTL_SOUND_PCM_READ_CHANNELS = SOUND_PCM_READ_CHANNELS; unsigned IOCTL_SOUND_PCM_READ_FILTER = SOUND_PCM_READ_FILTER; unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE; unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = SOUND_PCM_WRITE_CHANNELS; unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER; #endif // SOUND_VERSION unsigned IOCTL_TCFLSH = TCFLSH; unsigned IOCTL_TCGETA = TCGETA; unsigned IOCTL_TCGETS = TCGETS; unsigned IOCTL_TCSBRK = TCSBRK; unsigned IOCTL_TCSBRKP = TCSBRKP; unsigned IOCTL_TCSETA = TCSETA; unsigned IOCTL_TCSETAF = TCSETAF; unsigned IOCTL_TCSETAW = TCSETAW; unsigned IOCTL_TCSETS = TCSETS; unsigned IOCTL_TCSETSF = TCSETSF; unsigned IOCTL_TCSETSW = TCSETSW; unsigned IOCTL_TCXONC = TCXONC; unsigned IOCTL_TIOCGLCKTRMIOS = TIOCGLCKTRMIOS; unsigned IOCTL_TIOCGSOFTCAR = TIOCGSOFTCAR; unsigned IOCTL_TIOCINQ = TIOCINQ; unsigned IOCTL_TIOCLINUX = TIOCLINUX; unsigned IOCTL_TIOCSERCONFIG = TIOCSERCONFIG; unsigned IOCTL_TIOCSERGETLSR = TIOCSERGETLSR; unsigned IOCTL_TIOCSERGWILD = TIOCSERGWILD; unsigned IOCTL_TIOCSERSWILD = TIOCSERSWILD; unsigned IOCTL_TIOCSLCKTRMIOS = TIOCSLCKTRMIOS; unsigned IOCTL_TIOCSSOFTCAR = TIOCSSOFTCAR; unsigned IOCTL_VT_DISALLOCATE = VT_DISALLOCATE; unsigned IOCTL_VT_GETSTATE = VT_GETSTATE; unsigned IOCTL_VT_RESIZE = VT_RESIZE; unsigned IOCTL_VT_RESIZEX = VT_RESIZEX; unsigned IOCTL_VT_SENDSIG = VT_SENDSIG; #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD unsigned IOCTL_MTIOCGET = MTIOCGET; unsigned IOCTL_MTIOCTOP = MTIOCTOP; unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE; unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS; unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK; unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST; unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET; unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT; unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT; unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED; unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO; unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE; unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC; unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE; unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR; unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO; unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME; unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE; unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT; unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT; unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS; unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS; unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND; unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC; unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE; unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET; unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES; unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC; unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI; unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD; unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO; unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL; unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE; unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME; unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT; unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE; unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START; unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP; unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO; unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE; unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM; unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS; unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS; unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD; unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK; unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE; unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN; unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX; unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE; unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1; unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2; unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3; unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD; unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC; unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE; unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN; unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM; unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV; unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK; unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC; unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER; unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS; unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH; unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE; unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME; unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM; unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS; unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD; unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE; unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN; unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX; unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE; unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1; unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2; unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3; unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD; unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC; unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE; unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN; unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM; unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV; unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC; unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER; unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH; unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE; unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME; unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE; unsigned IOCTL_VT_GETMODE = VT_GETMODE; unsigned IOCTL_VT_OPENQRY = VT_OPENQRY; unsigned IOCTL_VT_RELDISP = VT_RELDISP; unsigned IOCTL_VT_SETMODE = VT_SETMODE; unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE; #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH; unsigned IOCTL_CYGETDEFTIMEOUT = CYGETDEFTIMEOUT; unsigned IOCTL_CYGETMON = CYGETMON; unsigned IOCTL_CYGETTHRESH = CYGETTHRESH; unsigned IOCTL_CYGETTIMEOUT = CYGETTIMEOUT; unsigned IOCTL_CYSETDEFTHRESH = CYSETDEFTHRESH; unsigned IOCTL_CYSETDEFTIMEOUT = CYSETDEFTIMEOUT; unsigned IOCTL_CYSETTHRESH = CYSETTHRESH; unsigned IOCTL_CYSETTIMEOUT = CYSETTIMEOUT; unsigned IOCTL_EQL_EMANCIPATE = EQL_EMANCIPATE; unsigned IOCTL_EQL_ENSLAVE = EQL_ENSLAVE; unsigned IOCTL_EQL_GETMASTRCFG = EQL_GETMASTRCFG; unsigned IOCTL_EQL_GETSLAVECFG = EQL_GETSLAVECFG; unsigned IOCTL_EQL_SETMASTRCFG = EQL_SETMASTRCFG; unsigned IOCTL_EQL_SETSLAVECFG = EQL_SETSLAVECFG; #if EV_VERSION > (0x010000) unsigned IOCTL_EVIOCGKEYCODE_V2 = EVIOCGKEYCODE_V2; unsigned IOCTL_EVIOCGPROP = EVIOCGPROP(0); unsigned IOCTL_EVIOCSKEYCODE_V2 = EVIOCSKEYCODE_V2; #else unsigned IOCTL_EVIOCGKEYCODE_V2 = IOCTL_NOT_PRESENT; unsigned IOCTL_EVIOCGPROP = IOCTL_NOT_PRESENT; unsigned IOCTL_EVIOCSKEYCODE_V2 = IOCTL_NOT_PRESENT; #endif unsigned IOCTL_FS_IOC_GETFLAGS = FS_IOC_GETFLAGS; unsigned IOCTL_FS_IOC_GETVERSION = FS_IOC_GETVERSION; unsigned IOCTL_FS_IOC_SETFLAGS = FS_IOC_SETFLAGS; unsigned IOCTL_FS_IOC_SETVERSION = FS_IOC_SETVERSION; unsigned IOCTL_GIO_CMAP = GIO_CMAP; unsigned IOCTL_GIO_FONT = GIO_FONT; unsigned IOCTL_GIO_UNIMAP = GIO_UNIMAP; unsigned IOCTL_GIO_UNISCRNMAP = GIO_UNISCRNMAP; unsigned IOCTL_KDADDIO = KDADDIO; unsigned IOCTL_KDDELIO = KDDELIO; unsigned IOCTL_KDGETKEYCODE = KDGETKEYCODE; unsigned IOCTL_KDGKBDIACR = KDGKBDIACR; unsigned IOCTL_KDGKBENT = KDGKBENT; unsigned IOCTL_KDGKBLED = KDGKBLED; unsigned IOCTL_KDGKBMETA = KDGKBMETA; unsigned IOCTL_KDGKBSENT = KDGKBSENT; unsigned IOCTL_KDMAPDISP = KDMAPDISP; unsigned IOCTL_KDSETKEYCODE = KDSETKEYCODE; unsigned IOCTL_KDSIGACCEPT = KDSIGACCEPT; unsigned IOCTL_KDSKBDIACR = KDSKBDIACR; unsigned IOCTL_KDSKBENT = KDSKBENT; unsigned IOCTL_KDSKBLED = KDSKBLED; unsigned IOCTL_KDSKBMETA = KDSKBMETA; unsigned IOCTL_KDSKBSENT = KDSKBSENT; unsigned IOCTL_KDUNMAPDISP = KDUNMAPDISP; unsigned IOCTL_LPABORT = LPABORT; unsigned IOCTL_LPABORTOPEN = LPABORTOPEN; unsigned IOCTL_LPCAREFUL = LPCAREFUL; unsigned IOCTL_LPCHAR = LPCHAR; unsigned IOCTL_LPGETIRQ = LPGETIRQ; unsigned IOCTL_LPGETSTATUS = LPGETSTATUS; unsigned IOCTL_LPRESET = LPRESET; unsigned IOCTL_LPSETIRQ = LPSETIRQ; unsigned IOCTL_LPTIME = LPTIME; unsigned IOCTL_LPWAIT = LPWAIT; unsigned IOCTL_MTIOCGETCONFIG = MTIOCGETCONFIG; unsigned IOCTL_MTIOCSETCONFIG = MTIOCSETCONFIG; unsigned IOCTL_PIO_CMAP = PIO_CMAP; unsigned IOCTL_PIO_FONT = PIO_FONT; unsigned IOCTL_PIO_UNIMAP = PIO_UNIMAP; unsigned IOCTL_PIO_UNIMAPCLR = PIO_UNIMAPCLR; unsigned IOCTL_PIO_UNISCRNMAP = PIO_UNISCRNMAP; unsigned IOCTL_SCSI_IOCTL_GET_IDLUN = SCSI_IOCTL_GET_IDLUN; unsigned IOCTL_SCSI_IOCTL_PROBE_HOST = SCSI_IOCTL_PROBE_HOST; unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE = SCSI_IOCTL_TAGGED_DISABLE; unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE = SCSI_IOCTL_TAGGED_ENABLE; unsigned IOCTL_SIOCAIPXITFCRT = SIOCAIPXITFCRT; unsigned IOCTL_SIOCAIPXPRISLT = SIOCAIPXPRISLT; unsigned IOCTL_SIOCAX25ADDUID = SIOCAX25ADDUID; unsigned IOCTL_SIOCAX25DELUID = SIOCAX25DELUID; unsigned IOCTL_SIOCAX25GETPARMS = SIOCAX25GETPARMS; unsigned IOCTL_SIOCAX25GETUID = SIOCAX25GETUID; unsigned IOCTL_SIOCAX25NOUID = SIOCAX25NOUID; unsigned IOCTL_SIOCAX25SETPARMS = SIOCAX25SETPARMS; unsigned IOCTL_SIOCDEVPLIP = SIOCDEVPLIP; unsigned IOCTL_SIOCIPXCFGDATA = SIOCIPXCFGDATA; unsigned IOCTL_SIOCNRDECOBS = SIOCNRDECOBS; unsigned IOCTL_SIOCNRGETPARMS = SIOCNRGETPARMS; unsigned IOCTL_SIOCNRRTCTL = SIOCNRRTCTL; unsigned IOCTL_SIOCNRSETPARMS = SIOCNRSETPARMS; unsigned IOCTL_TIOCGSERIAL = TIOCGSERIAL; unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI; unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI; unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL; #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP; unsigned IOCTL_KDDISABIO = KDDISABIO; unsigned IOCTL_KDENABIO = KDENABIO; unsigned IOCTL_KDGETLED = KDGETLED; unsigned IOCTL_KDGETMODE = KDGETMODE; unsigned IOCTL_KDGKBMODE = KDGKBMODE; unsigned IOCTL_KDGKBTYPE = KDGKBTYPE; unsigned IOCTL_KDMKTONE = KDMKTONE; unsigned IOCTL_KDSETLED = KDSETLED; unsigned IOCTL_KDSETMODE = KDSETMODE; unsigned IOCTL_KDSKBMODE = KDSKBMODE; unsigned IOCTL_KIOCSOUND = KIOCSOUND; unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP; unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE; unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE; #endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID const int errno_EINVAL = EINVAL; // EOWNERDEAD is not present in some older platforms. #if defined(EOWNERDEAD) const int errno_EOWNERDEAD = EOWNERDEAD; #else const int errno_EOWNERDEAD = -1; #endif const int si_SEGV_MAPERR = SEGV_MAPERR; const int si_SEGV_ACCERR = SEGV_ACCERR; } // namespace __sanitizer COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t)); COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned)); CHECK_TYPE_SIZE(pthread_key_t); #if SANITIZER_LINUX // FIXME: We define those on Linux and Mac, but only check on Linux. COMPILER_CHECK(IOC_NRBITS == _IOC_NRBITS); COMPILER_CHECK(IOC_TYPEBITS == _IOC_TYPEBITS); COMPILER_CHECK(IOC_SIZEBITS == _IOC_SIZEBITS); COMPILER_CHECK(IOC_DIRBITS == _IOC_DIRBITS); COMPILER_CHECK(IOC_NRMASK == _IOC_NRMASK); COMPILER_CHECK(IOC_TYPEMASK == _IOC_TYPEMASK); COMPILER_CHECK(IOC_SIZEMASK == _IOC_SIZEMASK); COMPILER_CHECK(IOC_DIRMASK == _IOC_DIRMASK); COMPILER_CHECK(IOC_NRSHIFT == _IOC_NRSHIFT); COMPILER_CHECK(IOC_TYPESHIFT == _IOC_TYPESHIFT); COMPILER_CHECK(IOC_SIZESHIFT == _IOC_SIZESHIFT); COMPILER_CHECK(IOC_DIRSHIFT == _IOC_DIRSHIFT); COMPILER_CHECK(IOC_NONE == _IOC_NONE); COMPILER_CHECK(IOC_WRITE == _IOC_WRITE); COMPILER_CHECK(IOC_READ == _IOC_READ); COMPILER_CHECK(EVIOC_ABS_MAX == ABS_MAX); COMPILER_CHECK(EVIOC_EV_MAX == EV_MAX); COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678)); COMPILER_CHECK(IOC_DIR(0x12345678) == _IOC_DIR(0x12345678)); COMPILER_CHECK(IOC_NR(0x12345678) == _IOC_NR(0x12345678)); COMPILER_CHECK(IOC_TYPE(0x12345678) == _IOC_TYPE(0x12345678)); #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD // There are more undocumented fields in dl_phdr_info that we are not interested // in. COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info)); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum); #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID CHECK_TYPE_SIZE(glob_t); CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc); CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv); CHECK_SIZE_AND_OFFSET(glob_t, gl_offs); CHECK_SIZE_AND_OFFSET(glob_t, gl_flags); CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir); CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir); CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir); CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat); CHECK_SIZE_AND_OFFSET(glob_t, gl_stat); #endif CHECK_TYPE_SIZE(addrinfo); CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags); CHECK_SIZE_AND_OFFSET(addrinfo, ai_family); CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype); CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol); CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol); CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen); CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname); CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr); CHECK_TYPE_SIZE(hostent); CHECK_SIZE_AND_OFFSET(hostent, h_name); CHECK_SIZE_AND_OFFSET(hostent, h_aliases); CHECK_SIZE_AND_OFFSET(hostent, h_addrtype); CHECK_SIZE_AND_OFFSET(hostent, h_length); CHECK_SIZE_AND_OFFSET(hostent, h_addr_list); CHECK_TYPE_SIZE(iovec); CHECK_SIZE_AND_OFFSET(iovec, iov_base); CHECK_SIZE_AND_OFFSET(iovec, iov_len); CHECK_TYPE_SIZE(msghdr); CHECK_SIZE_AND_OFFSET(msghdr, msg_name); CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen); CHECK_SIZE_AND_OFFSET(msghdr, msg_iov); CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen); CHECK_SIZE_AND_OFFSET(msghdr, msg_control); CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen); CHECK_SIZE_AND_OFFSET(msghdr, msg_flags); CHECK_TYPE_SIZE(cmsghdr); CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len); CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level); CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type); COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent)); CHECK_SIZE_AND_OFFSET(dirent, d_ino); #if SANITIZER_MAC CHECK_SIZE_AND_OFFSET(dirent, d_seekoff); #elif SANITIZER_FREEBSD // There is no 'd_off' field on FreeBSD. #else CHECK_SIZE_AND_OFFSET(dirent, d_off); #endif CHECK_SIZE_AND_OFFSET(dirent, d_reclen); #if SANITIZER_LINUX && !SANITIZER_ANDROID COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64)); CHECK_SIZE_AND_OFFSET(dirent64, d_ino); CHECK_SIZE_AND_OFFSET(dirent64, d_off); CHECK_SIZE_AND_OFFSET(dirent64, d_reclen); #endif CHECK_TYPE_SIZE(ifconf); CHECK_SIZE_AND_OFFSET(ifconf, ifc_len); CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu); CHECK_TYPE_SIZE(pollfd); CHECK_SIZE_AND_OFFSET(pollfd, fd); CHECK_SIZE_AND_OFFSET(pollfd, events); CHECK_SIZE_AND_OFFSET(pollfd, revents); CHECK_TYPE_SIZE(nfds_t); CHECK_TYPE_SIZE(sigset_t); COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction)); // Can't write checks for sa_handler and sa_sigaction due to them being // preprocessor macros. CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask); CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags); #if SANITIZER_LINUX CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer); #endif #if SANITIZER_LINUX CHECK_TYPE_SIZE(__sysctl_args); CHECK_SIZE_AND_OFFSET(__sysctl_args, name); CHECK_SIZE_AND_OFFSET(__sysctl_args, nlen); CHECK_SIZE_AND_OFFSET(__sysctl_args, oldval); CHECK_SIZE_AND_OFFSET(__sysctl_args, oldlenp); CHECK_SIZE_AND_OFFSET(__sysctl_args, newval); CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen); CHECK_TYPE_SIZE(__kernel_uid_t); CHECK_TYPE_SIZE(__kernel_gid_t); #if !defined(__aarch64__) CHECK_TYPE_SIZE(__kernel_old_uid_t); CHECK_TYPE_SIZE(__kernel_old_gid_t); #endif CHECK_TYPE_SIZE(__kernel_off_t); CHECK_TYPE_SIZE(__kernel_loff_t); CHECK_TYPE_SIZE(__kernel_fd_set); #endif #if !SANITIZER_ANDROID CHECK_TYPE_SIZE(wordexp_t); CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc); CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv); CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs); #endif CHECK_TYPE_SIZE(tm); CHECK_SIZE_AND_OFFSET(tm, tm_sec); CHECK_SIZE_AND_OFFSET(tm, tm_min); CHECK_SIZE_AND_OFFSET(tm, tm_hour); CHECK_SIZE_AND_OFFSET(tm, tm_mday); CHECK_SIZE_AND_OFFSET(tm, tm_mon); CHECK_SIZE_AND_OFFSET(tm, tm_year); CHECK_SIZE_AND_OFFSET(tm, tm_wday); CHECK_SIZE_AND_OFFSET(tm, tm_yday); CHECK_SIZE_AND_OFFSET(tm, tm_isdst); CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff); CHECK_SIZE_AND_OFFSET(tm, tm_zone); #if SANITIZER_LINUX CHECK_TYPE_SIZE(mntent); CHECK_SIZE_AND_OFFSET(mntent, mnt_fsname); CHECK_SIZE_AND_OFFSET(mntent, mnt_dir); CHECK_SIZE_AND_OFFSET(mntent, mnt_type); CHECK_SIZE_AND_OFFSET(mntent, mnt_opts); CHECK_SIZE_AND_OFFSET(mntent, mnt_freq); CHECK_SIZE_AND_OFFSET(mntent, mnt_passno); #endif CHECK_TYPE_SIZE(ether_addr); #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID CHECK_TYPE_SIZE(ipc_perm); # if SANITIZER_FREEBSD CHECK_SIZE_AND_OFFSET(ipc_perm, key); CHECK_SIZE_AND_OFFSET(ipc_perm, seq); # else CHECK_SIZE_AND_OFFSET(ipc_perm, __key); CHECK_SIZE_AND_OFFSET(ipc_perm, __seq); # endif CHECK_SIZE_AND_OFFSET(ipc_perm, uid); CHECK_SIZE_AND_OFFSET(ipc_perm, gid); CHECK_SIZE_AND_OFFSET(ipc_perm, cuid); CHECK_SIZE_AND_OFFSET(ipc_perm, cgid); +#ifndef __GLIBC_PREREQ +#define __GLIBC_PREREQ(x, y) 0 +#endif +#if !defined(__aarch64__) || !SANITIZER_LINUX || __GLIBC_PREREQ (2, 21) +/* On aarch64 glibc 2.20 and earlier provided incorrect mode field. */ CHECK_SIZE_AND_OFFSET(ipc_perm, mode); +#endif CHECK_TYPE_SIZE(shmid_ds); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch); #endif CHECK_TYPE_SIZE(clock_t); #if SANITIZER_LINUX CHECK_TYPE_SIZE(clockid_t); #endif #if !SANITIZER_ANDROID CHECK_TYPE_SIZE(ifaddrs); CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next); CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name); CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr); CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask); #if SANITIZER_LINUX || SANITIZER_FREEBSD // Compare against the union, because we can't reach into the union in a // compliant way. #ifdef ifa_dstaddr #undef ifa_dstaddr #endif # if SANITIZER_FREEBSD CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr); # else COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)NULL)->ifa_dstaddr) == sizeof(((ifaddrs *)NULL)->ifa_ifu)); COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) == offsetof(ifaddrs, ifa_ifu)); # endif // SANITIZER_FREEBSD #else CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr); #endif // SANITIZER_LINUX CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data); #endif #if SANITIZER_LINUX COMPILER_CHECK(sizeof(__sanitizer_mallinfo) == sizeof(struct mallinfo)); #endif #if !SANITIZER_ANDROID CHECK_TYPE_SIZE(timeb); CHECK_SIZE_AND_OFFSET(timeb, time); CHECK_SIZE_AND_OFFSET(timeb, millitm); CHECK_SIZE_AND_OFFSET(timeb, timezone); CHECK_SIZE_AND_OFFSET(timeb, dstflag); #endif CHECK_TYPE_SIZE(passwd); CHECK_SIZE_AND_OFFSET(passwd, pw_name); CHECK_SIZE_AND_OFFSET(passwd, pw_passwd); CHECK_SIZE_AND_OFFSET(passwd, pw_uid); CHECK_SIZE_AND_OFFSET(passwd, pw_gid); CHECK_SIZE_AND_OFFSET(passwd, pw_dir); CHECK_SIZE_AND_OFFSET(passwd, pw_shell); #if !SANITIZER_ANDROID CHECK_SIZE_AND_OFFSET(passwd, pw_gecos); #endif #if SANITIZER_MAC CHECK_SIZE_AND_OFFSET(passwd, pw_change); CHECK_SIZE_AND_OFFSET(passwd, pw_expire); CHECK_SIZE_AND_OFFSET(passwd, pw_class); #endif CHECK_TYPE_SIZE(group); CHECK_SIZE_AND_OFFSET(group, gr_name); CHECK_SIZE_AND_OFFSET(group, gr_passwd); CHECK_SIZE_AND_OFFSET(group, gr_gid); CHECK_SIZE_AND_OFFSET(group, gr_mem); #if SANITIZER_LINUX && !SANITIZER_ANDROID CHECK_TYPE_SIZE(XDR); CHECK_SIZE_AND_OFFSET(XDR, x_op); CHECK_SIZE_AND_OFFSET(XDR, x_ops); CHECK_SIZE_AND_OFFSET(XDR, x_public); CHECK_SIZE_AND_OFFSET(XDR, x_private); CHECK_SIZE_AND_OFFSET(XDR, x_base); CHECK_SIZE_AND_OFFSET(XDR, x_handy); COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE); COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE); COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE); #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID COMPILER_CHECK(sizeof(__sanitizer_FILE) <= sizeof(FILE)); CHECK_SIZE_AND_OFFSET(FILE, _flags); CHECK_SIZE_AND_OFFSET(FILE, _IO_read_ptr); CHECK_SIZE_AND_OFFSET(FILE, _IO_read_end); CHECK_SIZE_AND_OFFSET(FILE, _IO_read_base); CHECK_SIZE_AND_OFFSET(FILE, _IO_write_ptr); CHECK_SIZE_AND_OFFSET(FILE, _IO_write_end); CHECK_SIZE_AND_OFFSET(FILE, _IO_write_base); CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_base); CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_end); CHECK_SIZE_AND_OFFSET(FILE, _IO_save_base); CHECK_SIZE_AND_OFFSET(FILE, _IO_backup_base); CHECK_SIZE_AND_OFFSET(FILE, _IO_save_end); CHECK_SIZE_AND_OFFSET(FILE, _markers); CHECK_SIZE_AND_OFFSET(FILE, _chain); CHECK_SIZE_AND_OFFSET(FILE, _fileno); #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID COMPILER_CHECK(sizeof(__sanitizer__obstack_chunk) <= sizeof(_obstack_chunk)); CHECK_SIZE_AND_OFFSET(_obstack_chunk, limit); CHECK_SIZE_AND_OFFSET(_obstack_chunk, prev); CHECK_TYPE_SIZE(obstack); CHECK_SIZE_AND_OFFSET(obstack, chunk_size); CHECK_SIZE_AND_OFFSET(obstack, chunk); CHECK_SIZE_AND_OFFSET(obstack, object_base); CHECK_SIZE_AND_OFFSET(obstack, next_free); #endif #endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.h (revision 279191) @@ -1,1303 +1,1312 @@ //===-- sanitizer_platform_limits_posix.h ---------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of Sanitizer common code. // // Sizes and layouts of platform-specific POSIX data structures. //===----------------------------------------------------------------------===// #ifndef SANITIZER_PLATFORM_LIMITS_POSIX_H #define SANITIZER_PLATFORM_LIMITS_POSIX_H #include "sanitizer_internal_defs.h" #include "sanitizer_platform.h" +#if SANITIZER_FREEBSD +// FreeBSD's dlopen() returns a pointer to an Obj_Entry structure that +// incroporates the map structure. +# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \ + ((link_map*)((handle) == nullptr ? nullptr : ((char*)(handle) + 544))) +#else +# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) ((link_map*)(handle)) +#endif // !SANITIZER_FREEBSD + namespace __sanitizer { extern unsigned struct_utsname_sz; extern unsigned struct_stat_sz; #if !SANITIZER_FREEBSD && !SANITIZER_IOS extern unsigned struct_stat64_sz; #endif extern unsigned struct_rusage_sz; extern unsigned siginfo_t_sz; extern unsigned struct_itimerval_sz; extern unsigned pthread_t_sz; extern unsigned pthread_cond_t_sz; extern unsigned pid_t_sz; extern unsigned timeval_sz; extern unsigned uid_t_sz; extern unsigned gid_t_sz; extern unsigned mbstate_t_sz; extern unsigned struct_timezone_sz; extern unsigned struct_tms_sz; extern unsigned struct_itimerspec_sz; extern unsigned struct_sigevent_sz; extern unsigned struct_sched_param_sz; extern unsigned struct_statfs64_sz; #if !SANITIZER_ANDROID extern unsigned struct_statfs_sz; extern unsigned struct_sockaddr_sz; extern unsigned ucontext_t_sz; #endif // !SANITIZER_ANDROID #if SANITIZER_LINUX #if defined(__x86_64__) const unsigned struct_kernel_stat_sz = 144; const unsigned struct_kernel_stat64_sz = 0; #elif defined(__i386__) const unsigned struct_kernel_stat_sz = 64; const unsigned struct_kernel_stat64_sz = 96; #elif defined(__arm__) const unsigned struct_kernel_stat_sz = 64; const unsigned struct_kernel_stat64_sz = 104; #elif defined(__aarch64__) const unsigned struct_kernel_stat_sz = 128; const unsigned struct_kernel_stat64_sz = 104; #elif defined(__powerpc__) && !defined(__powerpc64__) const unsigned struct_kernel_stat_sz = 72; const unsigned struct_kernel_stat64_sz = 104; #elif defined(__powerpc64__) const unsigned struct_kernel_stat_sz = 144; const unsigned struct_kernel_stat64_sz = 104; #elif defined(__mips__) #if SANITIZER_WORDSIZE == 64 const unsigned struct_kernel_stat_sz = 216; #else const unsigned struct_kernel_stat_sz = 144; #endif const unsigned struct_kernel_stat64_sz = 104; #endif struct __sanitizer_perf_event_attr { unsigned type; unsigned size; // More fields that vary with the kernel version. }; extern unsigned struct_epoll_event_sz; extern unsigned struct_sysinfo_sz; extern unsigned __user_cap_header_struct_sz; extern unsigned __user_cap_data_struct_sz; extern unsigned struct_new_utsname_sz; extern unsigned struct_old_utsname_sz; extern unsigned struct_oldold_utsname_sz; const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long); #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD #if defined(__powerpc64__) const unsigned struct___old_kernel_stat_sz = 0; #else const unsigned struct___old_kernel_stat_sz = 32; #endif extern unsigned struct_rlimit_sz; extern unsigned struct_utimbuf_sz; extern unsigned struct_timespec_sz; struct __sanitizer_iocb { u64 aio_data; u32 aio_key_or_aio_reserved1; // Simply crazy. u32 aio_reserved1_or_aio_key; // Luckily, we don't need these. u16 aio_lio_opcode; s16 aio_reqprio; u32 aio_fildes; u64 aio_buf; u64 aio_nbytes; s64 aio_offset; u64 aio_reserved2; u64 aio_reserved3; }; struct __sanitizer_io_event { u64 data; u64 obj; u64 res; u64 res2; }; const unsigned iocb_cmd_pread = 0; const unsigned iocb_cmd_pwrite = 1; const unsigned iocb_cmd_preadv = 7; const unsigned iocb_cmd_pwritev = 8; struct __sanitizer___sysctl_args { int *name; int nlen; void *oldval; uptr *oldlenp; void *newval; uptr newlen; unsigned long ___unused[4]; }; const unsigned old_sigset_t_sz = sizeof(unsigned long); #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_ANDROID struct __sanitizer_mallinfo { uptr v[10]; }; #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID struct __sanitizer_mallinfo { int v[10]; }; extern unsigned struct_ustat_sz; extern unsigned struct_rlimit64_sz; extern unsigned struct_statvfs64_sz; struct __sanitizer_ipc_perm { int __key; int uid; int gid; int cuid; int cgid; #ifdef __powerpc__ unsigned mode; unsigned __seq; u64 __unused1; u64 __unused2; -#elif defined(__mips__) +#elif defined(__mips__) || defined(__aarch64__) unsigned int mode; unsigned short __seq; unsigned short __pad1; unsigned long __unused1; unsigned long __unused2; #else unsigned short mode; unsigned short __pad1; unsigned short __seq; unsigned short __pad2; #if defined(__x86_64__) && !defined(_LP64) u64 __unused1; u64 __unused2; #else unsigned long __unused1; unsigned long __unused2; #endif #endif }; struct __sanitizer_shmid_ds { __sanitizer_ipc_perm shm_perm; #ifndef __powerpc__ uptr shm_segsz; #elif !defined(__powerpc64__) uptr __unused0; #endif #if defined(__x86_64__) && !defined(_LP64) u64 shm_atime; u64 shm_dtime; u64 shm_ctime; #else uptr shm_atime; #if !defined(_LP64) && !defined(__mips__) uptr __unused1; #endif uptr shm_dtime; #if !defined(_LP64) && !defined(__mips__) uptr __unused2; #endif uptr shm_ctime; #if !defined(_LP64) && !defined(__mips__) uptr __unused3; #endif #endif #ifdef __powerpc__ uptr shm_segsz; #endif int shm_cpid; int shm_lpid; #if defined(__x86_64__) && !defined(_LP64) u64 shm_nattch; u64 __unused4; u64 __unused5; #else uptr shm_nattch; uptr __unused4; uptr __unused5; #endif }; #elif SANITIZER_FREEBSD struct __sanitizer_ipc_perm { unsigned int cuid; unsigned int cgid; unsigned int uid; unsigned int gid; unsigned short mode; unsigned short seq; long key; }; struct __sanitizer_shmid_ds { __sanitizer_ipc_perm shm_perm; unsigned long shm_segsz; unsigned int shm_lpid; unsigned int shm_cpid; int shm_nattch; unsigned long shm_atime; unsigned long shm_dtime; unsigned long shm_ctime; }; #endif #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID extern unsigned struct_msqid_ds_sz; extern unsigned struct_mq_attr_sz; extern unsigned struct_timex_sz; extern unsigned struct_statvfs_sz; #endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID struct __sanitizer_iovec { void *iov_base; uptr iov_len; }; #if !SANITIZER_ANDROID struct __sanitizer_ifaddrs { struct __sanitizer_ifaddrs *ifa_next; char *ifa_name; unsigned int ifa_flags; void *ifa_addr; // (struct sockaddr *) void *ifa_netmask; // (struct sockaddr *) // This is a union on Linux. # ifdef ifa_dstaddr # undef ifa_dstaddr # endif void *ifa_dstaddr; // (struct sockaddr *) void *ifa_data; }; #endif // !SANITIZER_ANDROID #if SANITIZER_MAC typedef unsigned long __sanitizer_pthread_key_t; #else typedef unsigned __sanitizer_pthread_key_t; #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID struct __sanitizer_XDR { int x_op; void *x_ops; uptr x_public; uptr x_private; uptr x_base; unsigned x_handy; }; const int __sanitizer_XDR_ENCODE = 0; const int __sanitizer_XDR_DECODE = 1; const int __sanitizer_XDR_FREE = 2; #endif struct __sanitizer_passwd { char *pw_name; char *pw_passwd; int pw_uid; int pw_gid; #if SANITIZER_MAC || SANITIZER_FREEBSD long pw_change; char *pw_class; #endif #if !SANITIZER_ANDROID char *pw_gecos; #endif char *pw_dir; char *pw_shell; #if SANITIZER_MAC || SANITIZER_FREEBSD long pw_expire; #endif #if SANITIZER_FREEBSD int pw_fields; #endif }; struct __sanitizer_group { char *gr_name; char *gr_passwd; int gr_gid; char **gr_mem; }; #if defined(__x86_64__) && !defined(_LP64) typedef long long __sanitizer_time_t; #else typedef long __sanitizer_time_t; #endif struct __sanitizer_timeb { __sanitizer_time_t time; unsigned short millitm; short timezone; short dstflag; }; struct __sanitizer_ether_addr { u8 octet[6]; }; struct __sanitizer_tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; long int tm_gmtoff; const char *tm_zone; }; #if SANITIZER_LINUX struct __sanitizer_mntent { char *mnt_fsname; char *mnt_dir; char *mnt_type; char *mnt_opts; int mnt_freq; int mnt_passno; }; #endif #if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD struct __sanitizer_msghdr { void *msg_name; unsigned msg_namelen; struct __sanitizer_iovec *msg_iov; unsigned msg_iovlen; void *msg_control; unsigned msg_controllen; int msg_flags; }; struct __sanitizer_cmsghdr { unsigned cmsg_len; int cmsg_level; int cmsg_type; }; #else struct __sanitizer_msghdr { void *msg_name; unsigned msg_namelen; struct __sanitizer_iovec *msg_iov; uptr msg_iovlen; void *msg_control; uptr msg_controllen; int msg_flags; }; struct __sanitizer_cmsghdr { uptr cmsg_len; int cmsg_level; int cmsg_type; }; #endif #if SANITIZER_MAC struct __sanitizer_dirent { unsigned long long d_ino; unsigned long long d_seekoff; unsigned short d_reclen; // more fields that we don't care about }; #elif SANITIZER_FREEBSD struct __sanitizer_dirent { unsigned int d_fileno; unsigned short d_reclen; // more fields that we don't care about }; #elif SANITIZER_ANDROID || defined(__x86_64__) struct __sanitizer_dirent { unsigned long long d_ino; unsigned long long d_off; unsigned short d_reclen; // more fields that we don't care about }; #else struct __sanitizer_dirent { uptr d_ino; uptr d_off; unsigned short d_reclen; // more fields that we don't care about }; #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID struct __sanitizer_dirent64 { unsigned long long d_ino; unsigned long long d_off; unsigned short d_reclen; // more fields that we don't care about }; #endif // 'clock_t' is 32 bits wide on x64 FreeBSD #if SANITIZER_FREEBSD typedef int __sanitizer_clock_t; #elif defined(__x86_64__) && !defined(_LP64) typedef long long __sanitizer_clock_t; #else typedef long __sanitizer_clock_t; #endif #if SANITIZER_LINUX typedef int __sanitizer_clockid_t; #endif #if SANITIZER_LINUX || SANITIZER_FREEBSD #if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)\ || defined(__mips__) typedef unsigned __sanitizer___kernel_uid_t; typedef unsigned __sanitizer___kernel_gid_t; #else typedef unsigned short __sanitizer___kernel_uid_t; typedef unsigned short __sanitizer___kernel_gid_t; #endif #if defined(__x86_64__) && !defined(_LP64) typedef long long __sanitizer___kernel_off_t; #else typedef long __sanitizer___kernel_off_t; #endif #if defined(__powerpc__) || defined(__mips__) typedef unsigned int __sanitizer___kernel_old_uid_t; typedef unsigned int __sanitizer___kernel_old_gid_t; #else typedef unsigned short __sanitizer___kernel_old_uid_t; typedef unsigned short __sanitizer___kernel_old_gid_t; #endif typedef long long __sanitizer___kernel_loff_t; typedef struct { unsigned long fds_bits[1024 / (8 * sizeof(long))]; } __sanitizer___kernel_fd_set; #endif // This thing depends on the platform. We are only interested in the upper // limit. Verified with a compiler assert in .cc. const int pthread_attr_t_max_sz = 128; union __sanitizer_pthread_attr_t { char size[pthread_attr_t_max_sz]; // NOLINT void *align; }; #if SANITIZER_ANDROID typedef unsigned long __sanitizer_sigset_t; #elif SANITIZER_MAC typedef unsigned __sanitizer_sigset_t; #elif SANITIZER_LINUX struct __sanitizer_sigset_t { // The size is determined by looking at sizeof of real sigset_t on linux. uptr val[128 / sizeof(uptr)]; }; #elif SANITIZER_FREEBSD struct __sanitizer_sigset_t { // uint32_t * 4 unsigned int __bits[4]; }; #endif // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros. struct __sanitizer_sigaction { #if defined(__mips__) && !SANITIZER_FREEBSD unsigned int sa_flags; #endif union { void (*sigaction)(int sig, void *siginfo, void *uctx); void (*handler)(int sig); }; #if SANITIZER_FREEBSD int sa_flags; __sanitizer_sigset_t sa_mask; #else __sanitizer_sigset_t sa_mask; #ifndef __mips__ int sa_flags; #endif #endif #if SANITIZER_LINUX void (*sa_restorer)(); #endif #if defined(__mips__) && (SANITIZER_WORDSIZE == 32) int sa_resv[1]; #endif }; #if SANITIZER_FREEBSD typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t; #else struct __sanitizer_kernel_sigset_t { u8 sig[8]; }; #endif // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros. struct __sanitizer_kernel_sigaction_t { union { void (*handler)(int signo); void (*sigaction)(int signo, void *info, void *ctx); }; unsigned long sa_flags; void (*sa_restorer)(void); __sanitizer_kernel_sigset_t sa_mask; }; extern uptr sig_ign; extern uptr sig_dfl; extern uptr sa_siginfo; #if SANITIZER_LINUX extern int e_tabsz; #endif extern int af_inet; extern int af_inet6; uptr __sanitizer_in_addr_sz(int af); #if SANITIZER_LINUX || SANITIZER_FREEBSD struct __sanitizer_dl_phdr_info { uptr dlpi_addr; const char *dlpi_name; const void *dlpi_phdr; short dlpi_phnum; }; #endif struct __sanitizer_addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; #if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD unsigned ai_addrlen; char *ai_canonname; void *ai_addr; #else // LINUX unsigned ai_addrlen; void *ai_addr; char *ai_canonname; #endif struct __sanitizer_addrinfo *ai_next; }; struct __sanitizer_hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; }; struct __sanitizer_pollfd { int fd; short events; short revents; }; #if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD typedef unsigned __sanitizer_nfds_t; #else typedef unsigned long __sanitizer_nfds_t; #endif #if !SANITIZER_ANDROID # if SANITIZER_LINUX struct __sanitizer_glob_t { uptr gl_pathc; char **gl_pathv; uptr gl_offs; int gl_flags; void (*gl_closedir)(void *dirp); void *(*gl_readdir)(void *dirp); void *(*gl_opendir)(const char *); int (*gl_lstat)(const char *, void *); int (*gl_stat)(const char *, void *); }; # elif SANITIZER_FREEBSD struct __sanitizer_glob_t { uptr gl_pathc; uptr gl_matchc; uptr gl_offs; int gl_flags; char **gl_pathv; int (*gl_errfunc)(const char*, int); void (*gl_closedir)(void *dirp); struct dirent *(*gl_readdir)(void *dirp); void *(*gl_opendir)(const char*); int (*gl_lstat)(const char*, void* /* struct stat* */); int (*gl_stat)(const char*, void* /* struct stat* */); }; # endif // SANITIZER_FREEBSD # if SANITIZER_LINUX || SANITIZER_FREEBSD extern int glob_nomatch; extern int glob_altdirfunc; # endif #endif // !SANITIZER_ANDROID extern unsigned path_max; struct __sanitizer_wordexp_t { uptr we_wordc; char **we_wordv; uptr we_offs; #if SANITIZER_FREEBSD char *we_strings; uptr we_nbytes; #endif }; #if SANITIZER_LINUX && !SANITIZER_ANDROID struct __sanitizer_FILE { int _flags; char *_IO_read_ptr; char *_IO_read_end; char *_IO_read_base; char *_IO_write_base; char *_IO_write_ptr; char *_IO_write_end; char *_IO_buf_base; char *_IO_buf_end; char *_IO_save_base; char *_IO_backup_base; char *_IO_save_end; void *_markers; __sanitizer_FILE *_chain; int _fileno; }; # define SANITIZER_HAS_STRUCT_FILE 1 #else typedef void __sanitizer_FILE; # define SANITIZER_HAS_STRUCT_FILE 0 #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ (defined(__i386) || defined(__x86_64)) extern unsigned struct_user_regs_struct_sz; extern unsigned struct_user_fpregs_struct_sz; extern unsigned struct_user_fpxregs_struct_sz; extern int ptrace_peektext; extern int ptrace_peekdata; extern int ptrace_peekuser; extern int ptrace_getregs; extern int ptrace_setregs; extern int ptrace_getfpregs; extern int ptrace_setfpregs; extern int ptrace_getfpxregs; extern int ptrace_setfpxregs; extern int ptrace_getsiginfo; extern int ptrace_setsiginfo; extern int ptrace_getregset; extern int ptrace_setregset; extern int ptrace_geteventmsg; #endif #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID extern unsigned struct_shminfo_sz; extern unsigned struct_shm_info_sz; extern int shmctl_ipc_stat; extern int shmctl_ipc_info; extern int shmctl_shm_info; extern int shmctl_shm_stat; #endif extern int map_fixed; // ioctl arguments struct __sanitizer_ifconf { int ifc_len; union { void *ifcu_req; } ifc_ifcu; #if SANITIZER_MAC } __attribute__((packed)); #else }; #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID struct __sanitizer__obstack_chunk { char *limit; struct __sanitizer__obstack_chunk *prev; }; struct __sanitizer_obstack { long chunk_size; struct __sanitizer__obstack_chunk *chunk; char *object_base; char *next_free; uptr more_fields[7]; }; #endif #define IOC_NRBITS 8 #define IOC_TYPEBITS 8 #if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__) #define IOC_SIZEBITS 13 #define IOC_DIRBITS 3 #define IOC_NONE 1U #define IOC_WRITE 4U #define IOC_READ 2U #else #define IOC_SIZEBITS 14 #define IOC_DIRBITS 2 #define IOC_NONE 0U #define IOC_WRITE 1U #define IOC_READ 2U #endif #define IOC_NRMASK ((1 << IOC_NRBITS) - 1) #define IOC_TYPEMASK ((1 << IOC_TYPEBITS) - 1) #define IOC_SIZEMASK ((1 << IOC_SIZEBITS) - 1) #if defined(IOC_DIRMASK) #undef IOC_DIRMASK #endif #define IOC_DIRMASK ((1 << IOC_DIRBITS) - 1) #define IOC_NRSHIFT 0 #define IOC_TYPESHIFT (IOC_NRSHIFT + IOC_NRBITS) #define IOC_SIZESHIFT (IOC_TYPESHIFT + IOC_TYPEBITS) #define IOC_DIRSHIFT (IOC_SIZESHIFT + IOC_SIZEBITS) #define EVIOC_EV_MAX 0x1f #define EVIOC_ABS_MAX 0x3f #define IOC_DIR(nr) (((nr) >> IOC_DIRSHIFT) & IOC_DIRMASK) #define IOC_TYPE(nr) (((nr) >> IOC_TYPESHIFT) & IOC_TYPEMASK) #define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK) #define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK) extern unsigned struct_arpreq_sz; extern unsigned struct_ifreq_sz; extern unsigned struct_termios_sz; extern unsigned struct_winsize_sz; #if SANITIZER_LINUX extern unsigned struct_cdrom_msf_sz; extern unsigned struct_cdrom_multisession_sz; extern unsigned struct_cdrom_read_audio_sz; extern unsigned struct_cdrom_subchnl_sz; extern unsigned struct_cdrom_ti_sz; extern unsigned struct_cdrom_tocentry_sz; extern unsigned struct_cdrom_tochdr_sz; extern unsigned struct_cdrom_volctrl_sz; extern unsigned struct_ff_effect_sz; extern unsigned struct_floppy_drive_params_sz; extern unsigned struct_floppy_drive_struct_sz; extern unsigned struct_floppy_fdc_state_sz; extern unsigned struct_floppy_max_errors_sz; extern unsigned struct_floppy_raw_cmd_sz; extern unsigned struct_floppy_struct_sz; extern unsigned struct_floppy_write_errors_sz; extern unsigned struct_format_descr_sz; extern unsigned struct_hd_driveid_sz; extern unsigned struct_hd_geometry_sz; extern unsigned struct_input_absinfo_sz; extern unsigned struct_input_id_sz; extern unsigned struct_mtpos_sz; extern unsigned struct_termio_sz; extern unsigned struct_vt_consize_sz; extern unsigned struct_vt_sizes_sz; extern unsigned struct_vt_stat_sz; #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD extern unsigned struct_copr_buffer_sz; extern unsigned struct_copr_debug_buf_sz; extern unsigned struct_copr_msg_sz; extern unsigned struct_midi_info_sz; extern unsigned struct_mtget_sz; extern unsigned struct_mtop_sz; extern unsigned struct_rtentry_sz; extern unsigned struct_sbi_instrument_sz; extern unsigned struct_seq_event_rec_sz; extern unsigned struct_synth_info_sz; extern unsigned struct_vt_mode_sz; #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID extern unsigned struct_ax25_parms_struct_sz; extern unsigned struct_cyclades_monitor_sz; extern unsigned struct_input_keymap_entry_sz; extern unsigned struct_ipx_config_data_sz; extern unsigned struct_kbdiacrs_sz; extern unsigned struct_kbentry_sz; extern unsigned struct_kbkeycode_sz; extern unsigned struct_kbsentry_sz; extern unsigned struct_mtconfiginfo_sz; extern unsigned struct_nr_parms_struct_sz; extern unsigned struct_scc_modem_sz; extern unsigned struct_scc_stat_sz; extern unsigned struct_serial_multiport_struct_sz; extern unsigned struct_serial_struct_sz; extern unsigned struct_sockaddr_ax25_sz; extern unsigned struct_unimapdesc_sz; extern unsigned struct_unimapinit_sz; #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID extern unsigned struct_audio_buf_info_sz; extern unsigned struct_ppp_stats_sz; #endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID #if !SANITIZER_ANDROID && !SANITIZER_MAC extern unsigned struct_sioc_sg_req_sz; extern unsigned struct_sioc_vif_req_sz; #endif // ioctl request identifiers // A special value to mark ioctls that are not present on the target platform, // when it can not be determined without including any system headers. extern const unsigned IOCTL_NOT_PRESENT; extern unsigned IOCTL_FIOASYNC; extern unsigned IOCTL_FIOCLEX; extern unsigned IOCTL_FIOGETOWN; extern unsigned IOCTL_FIONBIO; extern unsigned IOCTL_FIONCLEX; extern unsigned IOCTL_FIOSETOWN; extern unsigned IOCTL_SIOCADDMULTI; extern unsigned IOCTL_SIOCATMARK; extern unsigned IOCTL_SIOCDELMULTI; extern unsigned IOCTL_SIOCGIFADDR; extern unsigned IOCTL_SIOCGIFBRDADDR; extern unsigned IOCTL_SIOCGIFCONF; extern unsigned IOCTL_SIOCGIFDSTADDR; extern unsigned IOCTL_SIOCGIFFLAGS; extern unsigned IOCTL_SIOCGIFMETRIC; extern unsigned IOCTL_SIOCGIFMTU; extern unsigned IOCTL_SIOCGIFNETMASK; extern unsigned IOCTL_SIOCGPGRP; extern unsigned IOCTL_SIOCSIFADDR; extern unsigned IOCTL_SIOCSIFBRDADDR; extern unsigned IOCTL_SIOCSIFDSTADDR; extern unsigned IOCTL_SIOCSIFFLAGS; extern unsigned IOCTL_SIOCSIFMETRIC; extern unsigned IOCTL_SIOCSIFMTU; extern unsigned IOCTL_SIOCSIFNETMASK; extern unsigned IOCTL_SIOCSPGRP; extern unsigned IOCTL_TIOCCONS; extern unsigned IOCTL_TIOCEXCL; extern unsigned IOCTL_TIOCGETD; extern unsigned IOCTL_TIOCGPGRP; extern unsigned IOCTL_TIOCGWINSZ; extern unsigned IOCTL_TIOCMBIC; extern unsigned IOCTL_TIOCMBIS; extern unsigned IOCTL_TIOCMGET; extern unsigned IOCTL_TIOCMSET; extern unsigned IOCTL_TIOCNOTTY; extern unsigned IOCTL_TIOCNXCL; extern unsigned IOCTL_TIOCOUTQ; extern unsigned IOCTL_TIOCPKT; extern unsigned IOCTL_TIOCSCTTY; extern unsigned IOCTL_TIOCSETD; extern unsigned IOCTL_TIOCSPGRP; extern unsigned IOCTL_TIOCSTI; extern unsigned IOCTL_TIOCSWINSZ; #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID extern unsigned IOCTL_SIOCGETSGCNT; extern unsigned IOCTL_SIOCGETVIFCNT; #endif #if SANITIZER_LINUX extern unsigned IOCTL_EVIOCGABS; extern unsigned IOCTL_EVIOCGBIT; extern unsigned IOCTL_EVIOCGEFFECTS; extern unsigned IOCTL_EVIOCGID; extern unsigned IOCTL_EVIOCGKEY; extern unsigned IOCTL_EVIOCGKEYCODE; extern unsigned IOCTL_EVIOCGLED; extern unsigned IOCTL_EVIOCGNAME; extern unsigned IOCTL_EVIOCGPHYS; extern unsigned IOCTL_EVIOCGRAB; extern unsigned IOCTL_EVIOCGREP; extern unsigned IOCTL_EVIOCGSND; extern unsigned IOCTL_EVIOCGSW; extern unsigned IOCTL_EVIOCGUNIQ; extern unsigned IOCTL_EVIOCGVERSION; extern unsigned IOCTL_EVIOCRMFF; extern unsigned IOCTL_EVIOCSABS; extern unsigned IOCTL_EVIOCSFF; extern unsigned IOCTL_EVIOCSKEYCODE; extern unsigned IOCTL_EVIOCSREP; extern unsigned IOCTL_BLKFLSBUF; extern unsigned IOCTL_BLKGETSIZE; extern unsigned IOCTL_BLKRAGET; extern unsigned IOCTL_BLKRASET; extern unsigned IOCTL_BLKROGET; extern unsigned IOCTL_BLKROSET; extern unsigned IOCTL_BLKRRPART; extern unsigned IOCTL_CDROMAUDIOBUFSIZ; extern unsigned IOCTL_CDROMEJECT; extern unsigned IOCTL_CDROMEJECT_SW; extern unsigned IOCTL_CDROMMULTISESSION; extern unsigned IOCTL_CDROMPAUSE; extern unsigned IOCTL_CDROMPLAYMSF; extern unsigned IOCTL_CDROMPLAYTRKIND; extern unsigned IOCTL_CDROMREADAUDIO; extern unsigned IOCTL_CDROMREADCOOKED; extern unsigned IOCTL_CDROMREADMODE1; extern unsigned IOCTL_CDROMREADMODE2; extern unsigned IOCTL_CDROMREADRAW; extern unsigned IOCTL_CDROMREADTOCENTRY; extern unsigned IOCTL_CDROMREADTOCHDR; extern unsigned IOCTL_CDROMRESET; extern unsigned IOCTL_CDROMRESUME; extern unsigned IOCTL_CDROMSEEK; extern unsigned IOCTL_CDROMSTART; extern unsigned IOCTL_CDROMSTOP; extern unsigned IOCTL_CDROMSUBCHNL; extern unsigned IOCTL_CDROMVOLCTRL; extern unsigned IOCTL_CDROMVOLREAD; extern unsigned IOCTL_CDROM_GET_UPC; extern unsigned IOCTL_FDCLRPRM; extern unsigned IOCTL_FDDEFPRM; extern unsigned IOCTL_FDFLUSH; extern unsigned IOCTL_FDFMTBEG; extern unsigned IOCTL_FDFMTEND; extern unsigned IOCTL_FDFMTTRK; extern unsigned IOCTL_FDGETDRVPRM; extern unsigned IOCTL_FDGETDRVSTAT; extern unsigned IOCTL_FDGETDRVTYP; extern unsigned IOCTL_FDGETFDCSTAT; extern unsigned IOCTL_FDGETMAXERRS; extern unsigned IOCTL_FDGETPRM; extern unsigned IOCTL_FDMSGOFF; extern unsigned IOCTL_FDMSGON; extern unsigned IOCTL_FDPOLLDRVSTAT; extern unsigned IOCTL_FDRAWCMD; extern unsigned IOCTL_FDRESET; extern unsigned IOCTL_FDSETDRVPRM; extern unsigned IOCTL_FDSETEMSGTRESH; extern unsigned IOCTL_FDSETMAXERRS; extern unsigned IOCTL_FDSETPRM; extern unsigned IOCTL_FDTWADDLE; extern unsigned IOCTL_FDWERRORCLR; extern unsigned IOCTL_FDWERRORGET; extern unsigned IOCTL_HDIO_DRIVE_CMD; extern unsigned IOCTL_HDIO_GETGEO; extern unsigned IOCTL_HDIO_GET_32BIT; extern unsigned IOCTL_HDIO_GET_DMA; extern unsigned IOCTL_HDIO_GET_IDENTITY; extern unsigned IOCTL_HDIO_GET_KEEPSETTINGS; extern unsigned IOCTL_HDIO_GET_MULTCOUNT; extern unsigned IOCTL_HDIO_GET_NOWERR; extern unsigned IOCTL_HDIO_GET_UNMASKINTR; extern unsigned IOCTL_HDIO_SET_32BIT; extern unsigned IOCTL_HDIO_SET_DMA; extern unsigned IOCTL_HDIO_SET_KEEPSETTINGS; extern unsigned IOCTL_HDIO_SET_MULTCOUNT; extern unsigned IOCTL_HDIO_SET_NOWERR; extern unsigned IOCTL_HDIO_SET_UNMASKINTR; extern unsigned IOCTL_MTIOCPOS; extern unsigned IOCTL_PPPIOCGASYNCMAP; extern unsigned IOCTL_PPPIOCGDEBUG; extern unsigned IOCTL_PPPIOCGFLAGS; extern unsigned IOCTL_PPPIOCGUNIT; extern unsigned IOCTL_PPPIOCGXASYNCMAP; extern unsigned IOCTL_PPPIOCSASYNCMAP; extern unsigned IOCTL_PPPIOCSDEBUG; extern unsigned IOCTL_PPPIOCSFLAGS; extern unsigned IOCTL_PPPIOCSMAXCID; extern unsigned IOCTL_PPPIOCSMRU; extern unsigned IOCTL_PPPIOCSXASYNCMAP; extern unsigned IOCTL_SIOCDARP; extern unsigned IOCTL_SIOCDRARP; extern unsigned IOCTL_SIOCGARP; extern unsigned IOCTL_SIOCGIFENCAP; extern unsigned IOCTL_SIOCGIFHWADDR; extern unsigned IOCTL_SIOCGIFMAP; extern unsigned IOCTL_SIOCGIFMEM; extern unsigned IOCTL_SIOCGIFNAME; extern unsigned IOCTL_SIOCGIFSLAVE; extern unsigned IOCTL_SIOCGRARP; extern unsigned IOCTL_SIOCGSTAMP; extern unsigned IOCTL_SIOCSARP; extern unsigned IOCTL_SIOCSIFENCAP; extern unsigned IOCTL_SIOCSIFHWADDR; extern unsigned IOCTL_SIOCSIFLINK; extern unsigned IOCTL_SIOCSIFMAP; extern unsigned IOCTL_SIOCSIFMEM; extern unsigned IOCTL_SIOCSIFSLAVE; extern unsigned IOCTL_SIOCSRARP; extern unsigned IOCTL_SNDCTL_COPR_HALT; extern unsigned IOCTL_SNDCTL_COPR_LOAD; extern unsigned IOCTL_SNDCTL_COPR_RCODE; extern unsigned IOCTL_SNDCTL_COPR_RCVMSG; extern unsigned IOCTL_SNDCTL_COPR_RDATA; extern unsigned IOCTL_SNDCTL_COPR_RESET; extern unsigned IOCTL_SNDCTL_COPR_RUN; extern unsigned IOCTL_SNDCTL_COPR_SENDMSG; extern unsigned IOCTL_SNDCTL_COPR_WCODE; extern unsigned IOCTL_SNDCTL_COPR_WDATA; extern unsigned IOCTL_TCFLSH; extern unsigned IOCTL_TCGETA; extern unsigned IOCTL_TCGETS; extern unsigned IOCTL_TCSBRK; extern unsigned IOCTL_TCSBRKP; extern unsigned IOCTL_TCSETA; extern unsigned IOCTL_TCSETAF; extern unsigned IOCTL_TCSETAW; extern unsigned IOCTL_TCSETS; extern unsigned IOCTL_TCSETSF; extern unsigned IOCTL_TCSETSW; extern unsigned IOCTL_TCXONC; extern unsigned IOCTL_TIOCGLCKTRMIOS; extern unsigned IOCTL_TIOCGSOFTCAR; extern unsigned IOCTL_TIOCINQ; extern unsigned IOCTL_TIOCLINUX; extern unsigned IOCTL_TIOCSERCONFIG; extern unsigned IOCTL_TIOCSERGETLSR; extern unsigned IOCTL_TIOCSERGWILD; extern unsigned IOCTL_TIOCSERSWILD; extern unsigned IOCTL_TIOCSLCKTRMIOS; extern unsigned IOCTL_TIOCSSOFTCAR; extern unsigned IOCTL_VT_DISALLOCATE; extern unsigned IOCTL_VT_GETSTATE; extern unsigned IOCTL_VT_RESIZE; extern unsigned IOCTL_VT_RESIZEX; extern unsigned IOCTL_VT_SENDSIG; #endif // SANITIZER_LINUX #if SANITIZER_LINUX || SANITIZER_FREEBSD extern unsigned IOCTL_MTIOCGET; extern unsigned IOCTL_MTIOCTOP; extern unsigned IOCTL_SIOCADDRT; extern unsigned IOCTL_SIOCDELRT; extern unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE; extern unsigned IOCTL_SNDCTL_DSP_GETFMTS; extern unsigned IOCTL_SNDCTL_DSP_NONBLOCK; extern unsigned IOCTL_SNDCTL_DSP_POST; extern unsigned IOCTL_SNDCTL_DSP_RESET; extern unsigned IOCTL_SNDCTL_DSP_SETFMT; extern unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT; extern unsigned IOCTL_SNDCTL_DSP_SPEED; extern unsigned IOCTL_SNDCTL_DSP_STEREO; extern unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE; extern unsigned IOCTL_SNDCTL_DSP_SYNC; extern unsigned IOCTL_SNDCTL_FM_4OP_ENABLE; extern unsigned IOCTL_SNDCTL_FM_LOAD_INSTR; extern unsigned IOCTL_SNDCTL_MIDI_INFO; extern unsigned IOCTL_SNDCTL_MIDI_PRETIME; extern unsigned IOCTL_SNDCTL_SEQ_CTRLRATE; extern unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT; extern unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT; extern unsigned IOCTL_SNDCTL_SEQ_NRMIDIS; extern unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS; extern unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND; extern unsigned IOCTL_SNDCTL_SEQ_PANIC; extern unsigned IOCTL_SNDCTL_SEQ_PERCMODE; extern unsigned IOCTL_SNDCTL_SEQ_RESET; extern unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES; extern unsigned IOCTL_SNDCTL_SEQ_SYNC; extern unsigned IOCTL_SNDCTL_SEQ_TESTMIDI; extern unsigned IOCTL_SNDCTL_SEQ_THRESHOLD; extern unsigned IOCTL_SNDCTL_SYNTH_INFO; extern unsigned IOCTL_SNDCTL_SYNTH_MEMAVL; extern unsigned IOCTL_SNDCTL_TMR_CONTINUE; extern unsigned IOCTL_SNDCTL_TMR_METRONOME; extern unsigned IOCTL_SNDCTL_TMR_SELECT; extern unsigned IOCTL_SNDCTL_TMR_SOURCE; extern unsigned IOCTL_SNDCTL_TMR_START; extern unsigned IOCTL_SNDCTL_TMR_STOP; extern unsigned IOCTL_SNDCTL_TMR_TEMPO; extern unsigned IOCTL_SNDCTL_TMR_TIMEBASE; extern unsigned IOCTL_SOUND_MIXER_READ_ALTPCM; extern unsigned IOCTL_SOUND_MIXER_READ_BASS; extern unsigned IOCTL_SOUND_MIXER_READ_CAPS; extern unsigned IOCTL_SOUND_MIXER_READ_CD; extern unsigned IOCTL_SOUND_MIXER_READ_DEVMASK; extern unsigned IOCTL_SOUND_MIXER_READ_ENHANCE; extern unsigned IOCTL_SOUND_MIXER_READ_IGAIN; extern unsigned IOCTL_SOUND_MIXER_READ_IMIX; extern unsigned IOCTL_SOUND_MIXER_READ_LINE1; extern unsigned IOCTL_SOUND_MIXER_READ_LINE2; extern unsigned IOCTL_SOUND_MIXER_READ_LINE3; extern unsigned IOCTL_SOUND_MIXER_READ_LINE; extern unsigned IOCTL_SOUND_MIXER_READ_LOUD; extern unsigned IOCTL_SOUND_MIXER_READ_MIC; extern unsigned IOCTL_SOUND_MIXER_READ_MUTE; extern unsigned IOCTL_SOUND_MIXER_READ_OGAIN; extern unsigned IOCTL_SOUND_MIXER_READ_PCM; extern unsigned IOCTL_SOUND_MIXER_READ_RECLEV; extern unsigned IOCTL_SOUND_MIXER_READ_RECMASK; extern unsigned IOCTL_SOUND_MIXER_READ_RECSRC; extern unsigned IOCTL_SOUND_MIXER_READ_SPEAKER; extern unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS; extern unsigned IOCTL_SOUND_MIXER_READ_SYNTH; extern unsigned IOCTL_SOUND_MIXER_READ_TREBLE; extern unsigned IOCTL_SOUND_MIXER_READ_VOLUME; extern unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM; extern unsigned IOCTL_SOUND_MIXER_WRITE_BASS; extern unsigned IOCTL_SOUND_MIXER_WRITE_CD; extern unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE; extern unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN; extern unsigned IOCTL_SOUND_MIXER_WRITE_IMIX; extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE1; extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE2; extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE3; extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE; extern unsigned IOCTL_SOUND_MIXER_WRITE_LOUD; extern unsigned IOCTL_SOUND_MIXER_WRITE_MIC; extern unsigned IOCTL_SOUND_MIXER_WRITE_MUTE; extern unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN; extern unsigned IOCTL_SOUND_MIXER_WRITE_PCM; extern unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV; extern unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC; extern unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER; extern unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH; extern unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE; extern unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME; extern unsigned IOCTL_SOUND_PCM_READ_BITS; extern unsigned IOCTL_SOUND_PCM_READ_CHANNELS; extern unsigned IOCTL_SOUND_PCM_READ_FILTER; extern unsigned IOCTL_SOUND_PCM_READ_RATE; extern unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS; extern unsigned IOCTL_SOUND_PCM_WRITE_FILTER; extern unsigned IOCTL_VT_ACTIVATE; extern unsigned IOCTL_VT_GETMODE; extern unsigned IOCTL_VT_OPENQRY; extern unsigned IOCTL_VT_RELDISP; extern unsigned IOCTL_VT_SETMODE; extern unsigned IOCTL_VT_WAITACTIVE; #endif // SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX && !SANITIZER_ANDROID extern unsigned IOCTL_CYGETDEFTHRESH; extern unsigned IOCTL_CYGETDEFTIMEOUT; extern unsigned IOCTL_CYGETMON; extern unsigned IOCTL_CYGETTHRESH; extern unsigned IOCTL_CYGETTIMEOUT; extern unsigned IOCTL_CYSETDEFTHRESH; extern unsigned IOCTL_CYSETDEFTIMEOUT; extern unsigned IOCTL_CYSETTHRESH; extern unsigned IOCTL_CYSETTIMEOUT; extern unsigned IOCTL_EQL_EMANCIPATE; extern unsigned IOCTL_EQL_ENSLAVE; extern unsigned IOCTL_EQL_GETMASTRCFG; extern unsigned IOCTL_EQL_GETSLAVECFG; extern unsigned IOCTL_EQL_SETMASTRCFG; extern unsigned IOCTL_EQL_SETSLAVECFG; extern unsigned IOCTL_EVIOCGKEYCODE_V2; extern unsigned IOCTL_EVIOCGPROP; extern unsigned IOCTL_EVIOCSKEYCODE_V2; extern unsigned IOCTL_FS_IOC_GETFLAGS; extern unsigned IOCTL_FS_IOC_GETVERSION; extern unsigned IOCTL_FS_IOC_SETFLAGS; extern unsigned IOCTL_FS_IOC_SETVERSION; extern unsigned IOCTL_GIO_CMAP; extern unsigned IOCTL_GIO_FONT; extern unsigned IOCTL_GIO_UNIMAP; extern unsigned IOCTL_GIO_UNISCRNMAP; extern unsigned IOCTL_KDADDIO; extern unsigned IOCTL_KDDELIO; extern unsigned IOCTL_KDGETKEYCODE; extern unsigned IOCTL_KDGKBDIACR; extern unsigned IOCTL_KDGKBENT; extern unsigned IOCTL_KDGKBLED; extern unsigned IOCTL_KDGKBMETA; extern unsigned IOCTL_KDGKBSENT; extern unsigned IOCTL_KDMAPDISP; extern unsigned IOCTL_KDSETKEYCODE; extern unsigned IOCTL_KDSIGACCEPT; extern unsigned IOCTL_KDSKBDIACR; extern unsigned IOCTL_KDSKBENT; extern unsigned IOCTL_KDSKBLED; extern unsigned IOCTL_KDSKBMETA; extern unsigned IOCTL_KDSKBSENT; extern unsigned IOCTL_KDUNMAPDISP; extern unsigned IOCTL_LPABORT; extern unsigned IOCTL_LPABORTOPEN; extern unsigned IOCTL_LPCAREFUL; extern unsigned IOCTL_LPCHAR; extern unsigned IOCTL_LPGETIRQ; extern unsigned IOCTL_LPGETSTATUS; extern unsigned IOCTL_LPRESET; extern unsigned IOCTL_LPSETIRQ; extern unsigned IOCTL_LPTIME; extern unsigned IOCTL_LPWAIT; extern unsigned IOCTL_MTIOCGETCONFIG; extern unsigned IOCTL_MTIOCSETCONFIG; extern unsigned IOCTL_PIO_CMAP; extern unsigned IOCTL_PIO_FONT; extern unsigned IOCTL_PIO_UNIMAP; extern unsigned IOCTL_PIO_UNIMAPCLR; extern unsigned IOCTL_PIO_UNISCRNMAP; extern unsigned IOCTL_SCSI_IOCTL_GET_IDLUN; extern unsigned IOCTL_SCSI_IOCTL_PROBE_HOST; extern unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE; extern unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE; extern unsigned IOCTL_SIOCAIPXITFCRT; extern unsigned IOCTL_SIOCAIPXPRISLT; extern unsigned IOCTL_SIOCAX25ADDUID; extern unsigned IOCTL_SIOCAX25DELUID; extern unsigned IOCTL_SIOCAX25GETPARMS; extern unsigned IOCTL_SIOCAX25GETUID; extern unsigned IOCTL_SIOCAX25NOUID; extern unsigned IOCTL_SIOCAX25SETPARMS; extern unsigned IOCTL_SIOCDEVPLIP; extern unsigned IOCTL_SIOCIPXCFGDATA; extern unsigned IOCTL_SIOCNRDECOBS; extern unsigned IOCTL_SIOCNRGETPARMS; extern unsigned IOCTL_SIOCNRRTCTL; extern unsigned IOCTL_SIOCNRSETPARMS; extern unsigned IOCTL_SNDCTL_DSP_GETISPACE; extern unsigned IOCTL_SNDCTL_DSP_GETOSPACE; extern unsigned IOCTL_TIOCGSERIAL; extern unsigned IOCTL_TIOCSERGETMULTI; extern unsigned IOCTL_TIOCSERSETMULTI; extern unsigned IOCTL_TIOCSSERIAL; #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID extern unsigned IOCTL_GIO_SCRNMAP; extern unsigned IOCTL_KDDISABIO; extern unsigned IOCTL_KDENABIO; extern unsigned IOCTL_KDGETLED; extern unsigned IOCTL_KDGETMODE; extern unsigned IOCTL_KDGKBMODE; extern unsigned IOCTL_KDGKBTYPE; extern unsigned IOCTL_KDMKTONE; extern unsigned IOCTL_KDSETLED; extern unsigned IOCTL_KDSETMODE; extern unsigned IOCTL_KDSKBMODE; extern unsigned IOCTL_KIOCSOUND; extern unsigned IOCTL_PIO_SCRNMAP; #endif extern const int errno_EINVAL; extern const int errno_EOWNERDEAD; extern const int si_SEGV_MAPERR; extern const int si_SEGV_ACCERR; } // namespace __sanitizer #define CHECK_TYPE_SIZE(TYPE) \ COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE)) #define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \ COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *) NULL)->MEMBER) == \ sizeof(((CLASS *) NULL)->MEMBER)); \ COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \ offsetof(CLASS, MEMBER)) // For sigaction, which is a function and struct at the same time, // and thus requires explicit "struct" in sizeof() expression. #define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \ COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *) NULL)->MEMBER) == \ sizeof(((struct CLASS *) NULL)->MEMBER)); \ COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \ offsetof(struct CLASS, MEMBER)) #endif Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix.cc (revision 279191) @@ -1,324 +1,324 @@ //===-- sanitizer_posix.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements POSIX-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_POSIX #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include #if SANITIZER_LINUX #include #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID #include #endif #if SANITIZER_FREEBSD // The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before // that, it was never implemented. So just define it to zero. #undef MAP_NORESERVE #define MAP_NORESERVE 0 #endif namespace __sanitizer { // ------------- sanitizer_common.h uptr GetMmapGranularity() { return GetPageSize(); } #if SANITIZER_WORDSIZE == 32 // Take care of unusable kernel area in top gigabyte. static uptr GetKernelAreaSize() { #if SANITIZER_LINUX const uptr gbyte = 1UL << 30; // Firstly check if there are writable segments // mapped to top gigabyte (e.g. stack). MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr end, prot; while (proc_maps.Next(/*start*/0, &end, /*offset*/0, /*filename*/0, /*filename_size*/0, &prot)) { if ((end >= 3 * gbyte) && (prot & MemoryMappingLayout::kProtectionWrite) != 0) return 0; } #if !SANITIZER_ANDROID // Even if nothing is mapped, top Gb may still be accessible // if we are running on 64-bit kernel. // Uname may report misleading results if personality type // is modified (e.g. under schroot) so check this as well. struct utsname uname_info; int pers = personality(0xffffffffUL); if (!(pers & PER_MASK) && uname(&uname_info) == 0 && internal_strstr(uname_info.machine, "64")) return 0; #endif // SANITIZER_ANDROID // Top gigabyte is reserved for kernel. return gbyte; #else return 0; #endif // SANITIZER_LINUX } #endif // SANITIZER_WORDSIZE == 32 uptr GetMaxVirtualAddress() { #if SANITIZER_WORDSIZE == 64 -# if defined(__powerpc64__) +# if defined(__powerpc64__) || defined(__aarch64__) // On PowerPC64 we have two different address space layouts: 44- and 46-bit. // We somehow need to figure out which one we are using now and choose // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. // Note that with 'ulimit -s unlimited' the stack is moved away from the top // of the address space, so simply checking the stack address is not enough. // This should (does) work for both PowerPC64 Endian modes. + // Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit. return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1; -# elif defined(__aarch64__) - return (1ULL << 39) - 1; # elif defined(__mips64) return (1ULL << 40) - 1; // 0x000000ffffffffffUL; # else return (1ULL << 47) - 1; // 0x00007fffffffffffUL; # endif #else // SANITIZER_WORDSIZE == 32 uptr res = (1ULL << 32) - 1; // 0xffffffff; if (!common_flags()->full_address_space) res -= GetKernelAreaSize(); CHECK_LT(reinterpret_cast(&res), res); return res; #endif // SANITIZER_WORDSIZE } void *MmapOrDie(uptr size, const char *mem_type) { size = RoundUpTo(size, GetPageSizeCached()); uptr res = internal_mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); int reserrno; if (internal_iserror(res, &reserrno)) { static int recursion_count; if (recursion_count) { // The Report() and CHECK calls below may call mmap recursively and fail. // If we went into recursion, just die. RawWrite("ERROR: Failed to mmap\n"); Die(); } recursion_count++; Report("ERROR: %s failed to " "allocate 0x%zx (%zd) bytes of %s (errno: %d)\n", SanitizerToolName, size, size, mem_type, reserrno); DumpProcessMap(); CHECK("unable to mmap" && 0); } IncreaseTotalMmap(size); return (void *)res; } void UnmapOrDie(void *addr, uptr size) { if (!addr || !size) return; uptr res = internal_munmap(addr, size); if (internal_iserror(res)) { Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n", SanitizerToolName, size, size, addr); CHECK("unable to unmap" && 0); } DecreaseTotalMmap(size); } void *MmapNoReserveOrDie(uptr size, const char *mem_type) { uptr PageSize = GetPageSizeCached(); uptr p = internal_mmap(0, RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0); int reserrno; if (internal_iserror(p, &reserrno)) { Report("ERROR: %s failed to " "allocate noreserve 0x%zx (%zd) bytes for '%s' (errno: %d)\n", SanitizerToolName, size, size, mem_type, reserrno); CHECK("unable to mmap" && 0); } IncreaseTotalMmap(size); return (void *)p; } void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { uptr PageSize = GetPageSizeCached(); uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, -1, 0); int reserrno; if (internal_iserror(p, &reserrno)) Report("ERROR: %s failed to " "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n", SanitizerToolName, size, size, fixed_addr, reserrno); IncreaseTotalMmap(size); return (void *)p; } void *MmapFixedOrDie(uptr fixed_addr, uptr size) { uptr PageSize = GetPageSizeCached(); uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); int reserrno; if (internal_iserror(p, &reserrno)) { Report("ERROR: %s failed to " "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n", SanitizerToolName, size, size, fixed_addr, reserrno); CHECK("unable to mmap" && 0); } IncreaseTotalMmap(size); return (void *)p; } void *Mprotect(uptr fixed_addr, uptr size) { return (void *)internal_mmap((void*)fixed_addr, size, PROT_NONE, MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, -1, 0); } void *MapFileToMemory(const char *file_name, uptr *buff_size) { uptr openrv = OpenFile(file_name, false); CHECK(!internal_iserror(openrv)); fd_t fd = openrv; uptr fsize = internal_filesize(fd); CHECK_NE(fsize, (uptr)-1); CHECK_GT(fsize, 0); *buff_size = RoundUpTo(fsize, GetPageSizeCached()); uptr map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0); return internal_iserror(map) ? 0 : (void *)map; } void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) { uptr flags = MAP_SHARED; if (addr) flags |= MAP_FIXED; uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset); if (internal_iserror(p)) { Printf("could not map writable file (%zd, %zu, %zu): %zd\n", fd, offset, size, p); return 0; } return (void *)p; } static inline bool IntervalsAreSeparate(uptr start1, uptr end1, uptr start2, uptr end2) { CHECK(start1 <= end1); CHECK(start2 <= end2); return (end1 < start2) || (end2 < start1); } // FIXME: this is thread-unsafe, but should not cause problems most of the time. // When the shadow is mapped only a single thread usually exists (plus maybe // several worker threads on Mac, which aren't expected to map big chunks of // memory). bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end; while (proc_maps.Next(&start, &end, /*offset*/0, /*filename*/0, /*filename_size*/0, /*protection*/0)) { - if (!IntervalsAreSeparate(start, end, range_start, range_end)) + CHECK_NE(0, end); + if (!IntervalsAreSeparate(start, end - 1, range_start, range_end)) return false; } return true; } void DumpProcessMap() { MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end; const sptr kBufSize = 4095; char *filename = (char*)MmapOrDie(kBufSize, __func__); Report("Process memory map follows:\n"); while (proc_maps.Next(&start, &end, /* file_offset */0, filename, kBufSize, /* protection */0)) { Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename); } Report("End of process memory map.\n"); UnmapOrDie(filename, kBufSize); } const char *GetPwd() { return GetEnv("PWD"); } char *FindPathToBinary(const char *name) { const char *path = GetEnv("PATH"); if (!path) return 0; uptr name_len = internal_strlen(name); InternalScopedBuffer buffer(kMaxPathLength); const char *beg = path; while (true) { const char *end = internal_strchrnul(beg, ':'); uptr prefix_len = end - beg; if (prefix_len + name_len + 2 <= kMaxPathLength) { internal_memcpy(buffer.data(), beg, prefix_len); buffer[prefix_len] = '/'; internal_memcpy(&buffer[prefix_len + 1], name, name_len); buffer[prefix_len + 1 + name_len] = '\0'; if (FileExists(buffer.data())) return internal_strdup(buffer.data()); } if (*end == '\0') break; beg = end + 1; } return 0; } void ReportFile::Write(const char *buffer, uptr length) { SpinMutexLock l(mu); static const char *kWriteError = "ReportFile::Write() can't output requested buffer!\n"; ReopenIfNecessary(); if (length != internal_write(fd, buffer, length)) { internal_write(fd, kWriteError, internal_strlen(kWriteError)); Die(); } } bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) { uptr s, e, off, prot; InternalScopedString buff(kMaxPathLength); MemoryMappingLayout proc_maps(/*cache_enabled*/false); while (proc_maps.Next(&s, &e, &off, buff.data(), buff.size(), &prot)) { if ((prot & MemoryMappingLayout::kProtectionExecute) != 0 && internal_strcmp(module, buff.data()) == 0) { *start = s; *end = e; return true; } } return false; } } // namespace __sanitizer #endif // SANITIZER_POSIX Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cc (revision 279191) @@ -1,193 +1,205 @@ //===-- sanitizer_posix_libcdep.cc ----------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements libc-dependent POSIX-specific functions // from sanitizer_libc.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_POSIX #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_platform_limits_posix.h" #include "sanitizer_stacktrace.h" #include #include #include #include #include #include #include #include #include namespace __sanitizer { u32 GetUid() { return getuid(); } uptr GetThreadSelf() { return (uptr)pthread_self(); } void FlushUnneededShadowMemory(uptr addr, uptr size) { madvise((void*)addr, size, MADV_DONTNEED); } +void NoHugePagesInRegion(uptr addr, uptr size) { +#ifdef MADV_NOHUGEPAGE // May not be defined on old systems. + madvise((void *)addr, size, MADV_NOHUGEPAGE); +#endif // MADV_NOHUGEPAGE +} + +void DontDumpShadowMemory(uptr addr, uptr length) { +#ifdef MADV_DONTDUMP + madvise((void *)addr, length, MADV_DONTDUMP); +#endif +} + static rlim_t getlim(int res) { rlimit rlim; CHECK_EQ(0, getrlimit(res, &rlim)); return rlim.rlim_cur; } static void setlim(int res, rlim_t lim) { // The following magic is to prevent clang from replacing it with memset. volatile struct rlimit rlim; rlim.rlim_cur = lim; rlim.rlim_max = lim; if (setrlimit(res, const_cast(&rlim))) { Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno); Die(); } } void DisableCoreDumperIfNecessary() { if (common_flags()->disable_coredump) { setlim(RLIMIT_CORE, 0); } } bool StackSizeIsUnlimited() { rlim_t stack_size = getlim(RLIMIT_STACK); return (stack_size == RLIM_INFINITY); } void SetStackSizeLimitInBytes(uptr limit) { setlim(RLIMIT_STACK, (rlim_t)limit); CHECK(!StackSizeIsUnlimited()); } bool AddressSpaceIsUnlimited() { rlim_t as_size = getlim(RLIMIT_AS); return (as_size == RLIM_INFINITY); } void SetAddressSpaceUnlimited() { setlim(RLIMIT_AS, RLIM_INFINITY); CHECK(AddressSpaceIsUnlimited()); } void SleepForSeconds(int seconds) { sleep(seconds); } void SleepForMillis(int millis) { usleep(millis * 1000); } void Abort() { abort(); } int Atexit(void (*function)(void)) { #ifndef SANITIZER_GO return atexit(function); #else return 0; #endif } int internal_isatty(fd_t fd) { return isatty(fd); } #ifndef SANITIZER_GO // TODO(glider): different tools may require different altstack size. static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough. void SetAlternateSignalStack() { stack_t altstack, oldstack; CHECK_EQ(0, sigaltstack(0, &oldstack)); // If the alternate stack is already in place, do nothing. // Android always sets an alternate stack, but it's too small for us. if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE)) return; // TODO(glider): the mapped stack should have the MAP_STACK flag in the // future. It is not required by man 2 sigaltstack now (they're using // malloc()). void* base = MmapOrDie(kAltStackSize, __func__); altstack.ss_sp = (char*) base; altstack.ss_flags = 0; altstack.ss_size = kAltStackSize; CHECK_EQ(0, sigaltstack(&altstack, 0)); } void UnsetAlternateSignalStack() { stack_t altstack, oldstack; altstack.ss_sp = 0; altstack.ss_flags = SS_DISABLE; altstack.ss_size = kAltStackSize; // Some sane value required on Darwin. CHECK_EQ(0, sigaltstack(&altstack, &oldstack)); UnmapOrDie(oldstack.ss_sp, oldstack.ss_size); } typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); static void MaybeInstallSigaction(int signum, SignalHandlerType handler) { if (!IsDeadlySignal(signum)) return; struct sigaction sigact; internal_memset(&sigact, 0, sizeof(sigact)); sigact.sa_sigaction = (sa_sigaction_t)handler; // Do not block the signal from being received in that signal's handler. // Clients are responsible for handling this correctly. sigact.sa_flags = SA_SIGINFO | SA_NODEFER; if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; CHECK_EQ(0, internal_sigaction(signum, &sigact, 0)); VReport(1, "Installed the sigaction for signal %d\n", signum); } void InstallDeadlySignalHandlers(SignalHandlerType handler) { // Set the alternate signal stack for the main thread. // This will cause SetAlternateSignalStack to be called twice, but the stack // will be actually set only once. if (common_flags()->use_sigaltstack) SetAlternateSignalStack(); MaybeInstallSigaction(SIGSEGV, handler); MaybeInstallSigaction(SIGBUS, handler); } #endif // SANITIZER_GO bool IsAccessibleMemoryRange(uptr beg, uptr size) { uptr page_size = GetPageSizeCached(); // Checking too large memory ranges is slow. CHECK_LT(size, page_size * 10); int sock_pair[2]; if (pipe(sock_pair)) return false; uptr bytes_written = internal_write(sock_pair[1], reinterpret_cast(beg), size); int write_errno; bool result; if (internal_iserror(bytes_written, &write_errno)) { CHECK_EQ(EFAULT, write_errno); result = false; } else { result = (bytes_written == size); } internal_close(sock_pair[0]); internal_close(sock_pair[1]); return result; } } // namespace __sanitizer #endif // SANITIZER_POSIX Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_quarantine.h (revision 279191) @@ -1,179 +1,185 @@ //===-- sanitizer_quarantine.h ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Memory quarantine for AddressSanitizer and potentially other tools. // Quarantine caches some specified amount of memory in per-thread caches, // then evicts to global FIFO queue. When the queue reaches specified threshold, // oldest memory is recycled. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_QUARANTINE_H #define SANITIZER_QUARANTINE_H #include "sanitizer_internal_defs.h" #include "sanitizer_mutex.h" #include "sanitizer_list.h" namespace __sanitizer { template class QuarantineCache; struct QuarantineBatch { static const uptr kSize = 1021; QuarantineBatch *next; uptr size; uptr count; void *batch[kSize]; }; COMPILER_CHECK(sizeof(QuarantineBatch) <= (1 << 13)); // 8Kb. // The callback interface is: // void Callback::Recycle(Node *ptr); // void *cb.Allocate(uptr size); // void cb.Deallocate(void *ptr); template class Quarantine { public: typedef QuarantineCache Cache; explicit Quarantine(LinkerInitialized) : cache_(LINKER_INITIALIZED) { } void Init(uptr size, uptr cache_size) { - max_size_ = size; - min_size_ = size / 10 * 9; // 90% of max size. + atomic_store(&max_size_, size, memory_order_release); + atomic_store(&min_size_, size / 10 * 9, + memory_order_release); // 90% of max size. max_cache_size_ = cache_size; } + uptr GetSize() const { return atomic_load(&max_size_, memory_order_acquire); } + void Put(Cache *c, Callback cb, Node *ptr, uptr size) { c->Enqueue(cb, ptr, size); if (c->Size() > max_cache_size_) Drain(c, cb); } void NOINLINE Drain(Cache *c, Callback cb) { { SpinMutexLock l(&cache_mutex_); cache_.Transfer(c); } - if (cache_.Size() > max_size_ && recycle_mutex_.TryLock()) + if (cache_.Size() > GetSize() && recycle_mutex_.TryLock()) Recycle(cb); } private: // Read-only data. char pad0_[kCacheLineSize]; - uptr max_size_; - uptr min_size_; + atomic_uintptr_t max_size_; + atomic_uintptr_t min_size_; uptr max_cache_size_; char pad1_[kCacheLineSize]; SpinMutex cache_mutex_; SpinMutex recycle_mutex_; Cache cache_; char pad2_[kCacheLineSize]; void NOINLINE Recycle(Callback cb) { Cache tmp; + uptr min_size = atomic_load(&min_size_, memory_order_acquire); { SpinMutexLock l(&cache_mutex_); - while (cache_.Size() > min_size_) { + while (cache_.Size() > min_size) { QuarantineBatch *b = cache_.DequeueBatch(); tmp.EnqueueBatch(b); } } recycle_mutex_.Unlock(); DoRecycle(&tmp, cb); } void NOINLINE DoRecycle(Cache *c, Callback cb) { while (QuarantineBatch *b = c->DequeueBatch()) { const uptr kPrefetch = 16; for (uptr i = 0; i < kPrefetch; i++) PREFETCH(b->batch[i]); for (uptr i = 0; i < b->count; i++) { PREFETCH(b->batch[i + kPrefetch]); cb.Recycle((Node*)b->batch[i]); } cb.Deallocate(b); } } }; // Per-thread cache of memory blocks. template class QuarantineCache { public: explicit QuarantineCache(LinkerInitialized) { } QuarantineCache() : size_() { list_.clear(); } uptr Size() const { return atomic_load(&size_, memory_order_relaxed); } void Enqueue(Callback cb, void *ptr, uptr size) { if (list_.empty() || list_.back()->count == QuarantineBatch::kSize) { AllocBatch(cb); size += sizeof(QuarantineBatch); // Count the batch in Quarantine size. } QuarantineBatch *b = list_.back(); + CHECK(b); b->batch[b->count++] = ptr; b->size += size; SizeAdd(size); } void Transfer(QuarantineCache *c) { list_.append_back(&c->list_); SizeAdd(c->Size()); atomic_store(&c->size_, 0, memory_order_relaxed); } void EnqueueBatch(QuarantineBatch *b) { list_.push_back(b); SizeAdd(b->size); } QuarantineBatch *DequeueBatch() { if (list_.empty()) return 0; QuarantineBatch *b = list_.front(); list_.pop_front(); SizeSub(b->size); return b; } private: IntrusiveList list_; atomic_uintptr_t size_; void SizeAdd(uptr add) { atomic_store(&size_, Size() + add, memory_order_relaxed); } void SizeSub(uptr sub) { atomic_store(&size_, Size() - sub, memory_order_relaxed); } NOINLINE QuarantineBatch* AllocBatch(Callback cb) { QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b)); + CHECK(b); b->count = 0; b->size = 0; list_.push_back(b); return b; } }; } // namespace __sanitizer #endif // #ifndef SANITIZER_QUARANTINE_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cc (revision 279191) @@ -1,160 +1,163 @@ //===-- sanitizer_stackdepot.cc -------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. //===----------------------------------------------------------------------===// #include "sanitizer_stackdepot.h" #include "sanitizer_common.h" #include "sanitizer_stackdepotbase.h" namespace __sanitizer { struct StackDepotNode { StackDepotNode *link; u32 id; atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20; - uptr size; + u32 size; + u32 tag; uptr stack[1]; // [size] static const u32 kTabSizeLog = 20; // Lower kTabSizeLog bits are equal for all items in one bucket. // We use these bits to store the per-stack use counter. static const u32 kUseCountBits = kTabSizeLog; static const u32 kMaxUseCount = 1 << kUseCountBits; static const u32 kUseCountMask = (1 << kUseCountBits) - 1; static const u32 kHashMask = ~kUseCountMask; typedef StackTrace args_type; bool eq(u32 hash, const args_type &args) const { u32 hash_bits = atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask; - if ((hash & kHashMask) != hash_bits || args.size != size) return false; + if ((hash & kHashMask) != hash_bits || args.size != size || args.tag != tag) + return false; uptr i = 0; for (; i < size; i++) { if (stack[i] != args.trace[i]) return false; } return true; } static uptr storage_size(const args_type &args) { return sizeof(StackDepotNode) + (args.size - 1) * sizeof(uptr); } static u32 hash(const args_type &args) { // murmur2 const u32 m = 0x5bd1e995; const u32 seed = 0x9747b28c; const u32 r = 24; u32 h = seed ^ (args.size * sizeof(uptr)); for (uptr i = 0; i < args.size; i++) { u32 k = args.trace[i]; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; } h ^= h >> 13; h *= m; h ^= h >> 15; return h; } static bool is_valid(const args_type &args) { return args.size > 0 && args.trace; } void store(const args_type &args, u32 hash) { atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed); size = args.size; + tag = args.tag; internal_memcpy(stack, args.trace, size * sizeof(uptr)); } args_type load() const { - return args_type(&stack[0], size); + return args_type(&stack[0], size, tag); } StackDepotHandle get_handle() { return StackDepotHandle(this); } typedef StackDepotHandle handle_type; }; COMPILER_CHECK(StackDepotNode::kMaxUseCount == (u32)kStackDepotMaxUseCount); u32 StackDepotHandle::id() { return node_->id; } int StackDepotHandle::use_count() { return atomic_load(&node_->hash_and_use_count, memory_order_relaxed) & StackDepotNode::kUseCountMask; } void StackDepotHandle::inc_use_count_unsafe() { u32 prev = atomic_fetch_add(&node_->hash_and_use_count, 1, memory_order_relaxed) & StackDepotNode::kUseCountMask; CHECK_LT(prev + 1, StackDepotNode::kMaxUseCount); } // FIXME(dvyukov): this single reserved bit is used in TSan. typedef StackDepotBase StackDepot; static StackDepot theDepot; StackDepotStats *StackDepotGetStats() { return theDepot.GetStats(); } u32 StackDepotPut(StackTrace stack) { StackDepotHandle h = theDepot.Put(stack); return h.valid() ? h.id() : 0; } StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) { return theDepot.Put(stack); } StackTrace StackDepotGet(u32 id) { return theDepot.Get(id); } void StackDepotLockAll() { theDepot.LockAll(); } void StackDepotUnlockAll() { theDepot.UnlockAll(); } bool StackDepotReverseMap::IdDescPair::IdComparator( const StackDepotReverseMap::IdDescPair &a, const StackDepotReverseMap::IdDescPair &b) { return a.id < b.id; } StackDepotReverseMap::StackDepotReverseMap() : map_(StackDepotGetStats()->n_uniq_ids + 100) { for (int idx = 0; idx < StackDepot::kTabSize; idx++) { atomic_uintptr_t *p = &theDepot.tab[idx]; uptr v = atomic_load(p, memory_order_consume); StackDepotNode *s = (StackDepotNode*)(v & ~1); for (; s; s = s->link) { IdDescPair pair = {s->id, s}; map_.push_back(pair); } } InternalSort(&map_, map_.size(), IdDescPair::IdComparator); } StackTrace StackDepotReverseMap::Get(u32 id) { if (!map_.size()) return StackTrace(); IdDescPair pair = {id, 0}; uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair, IdDescPair::IdComparator); if (idx > map_.size()) return StackTrace(); return map_[idx].desc->load(); } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.cc (revision 279191) @@ -1,131 +1,116 @@ //===-- sanitizer_stacktrace.cc -------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. //===----------------------------------------------------------------------===// #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_stacktrace.h" namespace __sanitizer { -uptr StackTrace::GetPreviousInstructionPc(uptr pc) { -#if defined(__arm__) - // Cancel Thumb bit. - pc = pc & (~1); -#endif -#if defined(__powerpc__) || defined(__powerpc64__) - // PCs are always 4 byte aligned. - return pc - 4; -#elif defined(__sparc__) || defined(__mips__) - return pc - 8; -#else - return pc - 1; -#endif -} - uptr StackTrace::GetNextInstructionPc(uptr pc) { #if defined(__mips__) return pc + 8; #else return pc + 1; #endif } uptr StackTrace::GetCurrentPc() { return GET_CALLER_PC(); } void BufferedStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) { size = cnt + !!extra_top_pc; CHECK_LE(size, kStackTraceMax); internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0])); if (extra_top_pc) trace_buffer[cnt] = extra_top_pc; top_frame_bp = 0; } // Check if given pointer points into allocated stack area. static inline bool IsValidFrame(uptr frame, uptr stack_top, uptr stack_bottom) { return frame > stack_bottom && frame < stack_top - 2 * sizeof (uhwptr); } // In GCC on ARM bp points to saved lr, not fp, so we should check the next // cell in stack to be a saved frame pointer. GetCanonicFrame returns the // pointer to saved frame pointer in any case. static inline uhwptr *GetCanonicFrame(uptr bp, uptr stack_top, uptr stack_bottom) { #ifdef __arm__ if (!IsValidFrame(bp, stack_top, stack_bottom)) return 0; uhwptr *bp_prev = (uhwptr *)bp; if (IsValidFrame((uptr)bp_prev[0], stack_top, stack_bottom)) return bp_prev; // The next frame pointer does not look right. This could be a GCC frame, step // back by 1 word and try again. if (IsValidFrame((uptr)bp_prev[-1], stack_top, stack_bottom)) return bp_prev - 1; // Nope, this does not look right either. This means the frame after next does // not have a valid frame pointer, but we can still extract the caller PC. // Unfortunately, there is no way to decide between GCC and LLVM frame // layouts. Assume LLVM. return bp_prev; #else return (uhwptr*)bp; #endif } void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top, - uptr stack_bottom, uptr max_depth) { + uptr stack_bottom, u32 max_depth) { CHECK_GE(max_depth, 2); trace_buffer[0] = pc; size = 1; if (stack_top < 4096) return; // Sanity check for stack top. uhwptr *frame = GetCanonicFrame(bp, stack_top, stack_bottom); // Lowest possible address that makes sense as the next frame pointer. // Goes up as we walk the stack. uptr bottom = stack_bottom; // Avoid infinite loop when frame == frame[0] by using frame > prev_frame. while (IsValidFrame((uptr)frame, stack_top, bottom) && IsAligned((uptr)frame, sizeof(*frame)) && size < max_depth) { uhwptr pc1 = frame[1]; if (pc1 != pc) { trace_buffer[size++] = (uptr) pc1; } bottom = (uptr)frame; frame = GetCanonicFrame((uptr)frame[0], stack_top, bottom); } } static bool MatchPc(uptr cur_pc, uptr trace_pc, uptr threshold) { return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold; } void BufferedStackTrace::PopStackFrames(uptr count) { CHECK_LT(count, size); size -= count; for (uptr i = 0; i < size; ++i) { trace_buffer[i] = trace_buffer[i + count]; } } uptr BufferedStackTrace::LocatePcInTrace(uptr pc) { // Use threshold to find PC in stack trace, as PC we want to unwind from may // slightly differ from return address in the actual unwinded stack trace. - const int kPcThreshold = 288; + const int kPcThreshold = 304; for (uptr i = 0; i < size; ++i) { if (MatchPc(pc, trace[i], kPcThreshold)) return i; } return 0; } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace.h (revision 279191) @@ -1,113 +1,138 @@ //===-- sanitizer_stacktrace.h ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. //===----------------------------------------------------------------------===// #ifndef SANITIZER_STACKTRACE_H #define SANITIZER_STACKTRACE_H #include "sanitizer_internal_defs.h" namespace __sanitizer { -static const uptr kStackTraceMax = 256; +static const u32 kStackTraceMax = 256; #if SANITIZER_LINUX && (defined(__aarch64__) || defined(__powerpc__) || \ defined(__powerpc64__) || defined(__sparc__) || \ defined(__mips__)) # define SANITIZER_CAN_FAST_UNWIND 0 #elif SANITIZER_WINDOWS # define SANITIZER_CAN_FAST_UNWIND 0 #else # define SANITIZER_CAN_FAST_UNWIND 1 #endif // Fast unwind is the only option on Mac for now; we will need to // revisit this macro when slow unwind works on Mac, see // https://code.google.com/p/address-sanitizer/issues/detail?id=137 #if SANITIZER_MAC # define SANITIZER_CAN_SLOW_UNWIND 0 #else # define SANITIZER_CAN_SLOW_UNWIND 1 #endif struct StackTrace { const uptr *trace; - uptr size; + u32 size; + u32 tag; - StackTrace() : trace(nullptr), size(0) {} - StackTrace(const uptr *trace, uptr size) : trace(trace), size(size) {} + static const int TAG_UNKNOWN = 0; + static const int TAG_ALLOC = 1; + static const int TAG_DEALLOC = 2; + static const int TAG_CUSTOM = 100; // Tool specific tags start here. + StackTrace() : trace(nullptr), size(0), tag(0) {} + StackTrace(const uptr *trace, u32 size) : trace(trace), size(size), tag(0) {} + StackTrace(const uptr *trace, u32 size, u32 tag) + : trace(trace), size(size), tag(tag) {} + // Prints a symbolized stacktrace, followed by an empty line. void Print() const; static bool WillUseFastUnwind(bool request_fast_unwind) { if (!SANITIZER_CAN_FAST_UNWIND) return false; else if (!SANITIZER_CAN_SLOW_UNWIND) return true; return request_fast_unwind; } static uptr GetCurrentPc(); - static uptr GetPreviousInstructionPc(uptr pc); + static inline uptr GetPreviousInstructionPc(uptr pc); static uptr GetNextInstructionPc(uptr pc); typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer, int out_size); }; +// Performance-critical, must be in the header. +ALWAYS_INLINE +uptr StackTrace::GetPreviousInstructionPc(uptr pc) { +#if defined(__arm__) + // Cancel Thumb bit. + pc = pc & (~1); +#endif +#if defined(__powerpc__) || defined(__powerpc64__) + // PCs are always 4 byte aligned. + return pc - 4; +#elif defined(__sparc__) || defined(__mips__) + return pc - 8; +#else + return pc - 1; +#endif +} + // StackTrace that owns the buffer used to store the addresses. struct BufferedStackTrace : public StackTrace { uptr trace_buffer[kStackTraceMax]; uptr top_frame_bp; // Optional bp of a top frame. BufferedStackTrace() : StackTrace(trace_buffer, 0), top_frame_bp(0) {} void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0); - void Unwind(uptr max_depth, uptr pc, uptr bp, void *context, uptr stack_top, + void Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top, uptr stack_bottom, bool request_fast_unwind); private: void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom, - uptr max_depth); - void SlowUnwindStack(uptr pc, uptr max_depth); + u32 max_depth); + void SlowUnwindStack(uptr pc, u32 max_depth); void SlowUnwindStackWithContext(uptr pc, void *context, - uptr max_depth); + u32 max_depth); void PopStackFrames(uptr count); uptr LocatePcInTrace(uptr pc); BufferedStackTrace(const BufferedStackTrace &); void operator=(const BufferedStackTrace &); }; } // namespace __sanitizer // Use this macro if you want to print stack trace with the caller // of the current function in the top frame. #define GET_CALLER_PC_BP_SP \ uptr bp = GET_CURRENT_FRAME(); \ uptr pc = GET_CALLER_PC(); \ uptr local_stack; \ uptr sp = (uptr)&local_stack #define GET_CALLER_PC_BP \ uptr bp = GET_CURRENT_FRAME(); \ uptr pc = GET_CALLER_PC(); // Use this macro if you want to print stack trace with the current // function in the top frame. #define GET_CURRENT_PC_BP_SP \ uptr bp = GET_CURRENT_FRAME(); \ uptr pc = StackTrace::GetCurrentPc(); \ uptr local_stack; \ uptr sp = (uptr)&local_stack #endif // SANITIZER_STACKTRACE_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc (revision 279191) @@ -1,71 +1,71 @@ //===-- sanitizer_stacktrace_libcdep.cc -----------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. //===----------------------------------------------------------------------===// #include "sanitizer_common.h" #include "sanitizer_placement_new.h" #include "sanitizer_stacktrace.h" #include "sanitizer_stacktrace_printer.h" #include "sanitizer_symbolizer.h" namespace __sanitizer { void StackTrace::Print() const { if (trace == nullptr || size == 0) { Printf(" \n\n"); return; } InternalScopedString frame_desc(GetPageSizeCached() * 2); uptr frame_num = 0; for (uptr i = 0; i < size && trace[i]; i++) { // PCs in stack traces are actually the return addresses, that is, // addresses of the next instructions after the call. uptr pc = GetPreviousInstructionPc(trace[i]); SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc); CHECK(frames); for (SymbolizedStack *cur = frames; cur; cur = cur->next) { frame_desc.clear(); RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++, cur->info, common_flags()->strip_path_prefix); Printf("%s\n", frame_desc.data()); } frames->ClearAll(); } // Always print a trailing empty line after stack trace. Printf("\n"); } -void BufferedStackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context, +void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top, uptr stack_bottom, bool request_fast_unwind) { top_frame_bp = (max_depth > 0) ? bp : 0; // Avoid doing any work for small max_depth. if (max_depth == 0) { size = 0; return; } if (max_depth == 1) { size = 1; trace_buffer[0] = pc; return; } if (!WillUseFastUnwind(request_fast_unwind)) { if (context) SlowUnwindStackWithContext(pc, context, max_depth); else SlowUnwindStack(pc, max_depth); } else { FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth); } } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc (revision 279191) @@ -1,211 +1,211 @@ //===-- sanitizer_symbolizer_libbacktrace.cc ------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. // Libbacktrace implementation of symbolizer parts. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #include "sanitizer_internal_defs.h" #include "sanitizer_symbolizer.h" #include "sanitizer_symbolizer_libbacktrace.h" #if SANITIZER_LIBBACKTRACE # include "backtrace-supported.h" # if SANITIZER_POSIX && BACKTRACE_SUPPORTED && !BACKTRACE_USES_MALLOC # include "backtrace.h" # if SANITIZER_CP_DEMANGLE # undef ARRAY_SIZE # include "demangle.h" # endif # else # define SANITIZER_LIBBACKTRACE 0 # endif #endif namespace __sanitizer { #if SANITIZER_LIBBACKTRACE namespace { # if SANITIZER_CP_DEMANGLE struct CplusV3DemangleData { char *buf; uptr size, allocated; }; extern "C" { static void CplusV3DemangleCallback(const char *s, size_t l, void *vdata) { CplusV3DemangleData *data = (CplusV3DemangleData *)vdata; uptr needed = data->size + l + 1; if (needed > data->allocated) { data->allocated *= 2; if (needed > data->allocated) data->allocated = needed; char *buf = (char *)InternalAlloc(data->allocated); if (data->buf) { internal_memcpy(buf, data->buf, data->size); InternalFree(data->buf); } data->buf = buf; } internal_memcpy(data->buf + data->size, s, l); data->buf[data->size + l] = '\0'; data->size += l; } } // extern "C" char *CplusV3Demangle(const char *name) { CplusV3DemangleData data; data.buf = 0; data.size = 0; data.allocated = 0; if (cplus_demangle_v3_callback(name, DMGL_PARAMS | DMGL_ANSI, CplusV3DemangleCallback, &data)) { if (data.size + 64 > data.allocated) return data.buf; char *buf = internal_strdup(data.buf); InternalFree(data.buf); return buf; } if (data.buf) InternalFree(data.buf); return 0; } # endif // SANITIZER_CP_DEMANGLE struct SymbolizeCodeCallbackArg { SymbolizedStack *first; SymbolizedStack *last; const char *module_name; uptr module_offset; void append(SymbolizedStack *f) { if (last != nullptr) { last->next = f; last = f; } else { first = f; last = f; } } }; extern "C" { static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr, const char *filename, int lineno, const char *function) { SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata; if (function) { SymbolizedStack *cur = SymbolizedStack::New(addr); cdata->append(cur); AddressInfo *info = &cur->info; info->FillAddressAndModuleInfo(addr, cdata->module_name, cdata->module_offset); info->function = LibbacktraceSymbolizer::Demangle(function, true); if (filename) info->file = internal_strdup(filename); info->line = lineno; } return 0; } static void SymbolizeCodeCallback(void *vdata, uintptr_t addr, const char *symname, uintptr_t, uintptr_t) { SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata; if (symname) { SymbolizedStack *cur = SymbolizedStack::New(addr); cdata->append(cur); AddressInfo *info = &cur->info; info->FillAddressAndModuleInfo(addr, cdata->module_name, cdata->module_offset); info->function = LibbacktraceSymbolizer::Demangle(symname, true); } } static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname, uintptr_t symval, uintptr_t symsize) { DataInfo *info = (DataInfo *)vdata; if (symname && symval) { info->name = LibbacktraceSymbolizer::Demangle(symname, true); info->start = symval; info->size = symsize; } } static void ErrorCallback(void *, const char *, int) {} } // extern "C" } // namespace LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { // State created in backtrace_create_state is leaked. void *state = (void *)(backtrace_create_state("/proc/self/exe", 0, ErrorCallback, NULL)); if (!state) return 0; return new(*alloc) LibbacktraceSymbolizer(state); } SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr, const char *module_name, uptr module_offset) { SymbolizeCodeCallbackArg data; data.first = nullptr; data.last = nullptr; data.module_name = module_name; data.module_offset = module_offset; backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback, ErrorCallback, &data); if (data.first) return data.first; backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback, ErrorCallback, &data); return data.first; } -bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) { - backtrace_syminfo((backtrace_state *)state_, info->address, - SymbolizeDataCallback, ErrorCallback, info); +bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { + backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeDataCallback, + ErrorCallback, info); return true; } #else // SANITIZER_LIBBACKTRACE LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { return 0; } SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr, const char *module_name, uptr module_offset) { (void)state_; return nullptr; } -bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) { +bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; } #endif // SANITIZER_LIBBACKTRACE char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) { #if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE if (char *demangled = CplusV3Demangle(name)) return demangled; #endif if (always_alloc) return internal_strdup(name); return 0; } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h (revision 279191) @@ -1,50 +1,50 @@ //===-- sanitizer_symbolizer_libbacktrace.h ---------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. // Header for libbacktrace symbolizer. //===----------------------------------------------------------------------===// #ifndef SANITIZER_SYMBOLIZER_LIBBACKTRACE_H #define SANITIZER_SYMBOLIZER_LIBBACKTRACE_H #include "sanitizer_platform.h" #include "sanitizer_common.h" #include "sanitizer_symbolizer.h" #ifndef SANITIZER_LIBBACKTRACE # define SANITIZER_LIBBACKTRACE 0 #endif #ifndef SANITIZER_CP_DEMANGLE # define SANITIZER_CP_DEMANGLE 0 #endif namespace __sanitizer { class LibbacktraceSymbolizer { public: static LibbacktraceSymbolizer *get(LowLevelAllocator *alloc); SymbolizedStack *SymbolizeCode(uptr addr, const char *module_name, uptr module_offset); - bool SymbolizeData(DataInfo *info); + bool SymbolizeData(uptr addr, DataInfo *info); // May return NULL if demangling failed. static char *Demangle(const char *name, bool always_alloc = false); private: explicit LibbacktraceSymbolizer(void *state) : state_(state) {} void *state_; // Leaked. }; } // namespace __sanitizer #endif // SANITIZER_SYMBOLIZER_LIBBACKTRACE_H Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc (revision 279191) @@ -1,766 +1,766 @@ //===-- sanitizer_symbolizer_posix_libcdep.cc -----------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. // POSIX-specific implementation of symbolizer parts. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_POSIX #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_internal_defs.h" #include "sanitizer_linux.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_symbolizer.h" #include "sanitizer_symbolizer_libbacktrace.h" #include #include #include #include // C++ demangling function, as required by Itanium C++ ABI. This is weak, // because we do not require a C++ ABI library to be linked to a program // using sanitizers; if it's not present, we'll just use the mangled name. namespace __cxxabiv1 { extern "C" SANITIZER_WEAK_ATTRIBUTE char *__cxa_demangle(const char *mangled, char *buffer, size_t *length, int *status); } namespace __sanitizer { // Attempts to demangle the name via __cxa_demangle from __cxxabiv1. static const char *DemangleCXXABI(const char *name) { // FIXME: __cxa_demangle aggressively insists on allocating memory. // There's not much we can do about that, short of providing our // own demangler (libc++abi's implementation could be adapted so that // it does not allocate). For now, we just call it anyway, and we leak // the returned value. if (__cxxabiv1::__cxa_demangle) if (const char *demangled_name = __cxxabiv1::__cxa_demangle(name, 0, 0, 0)) return demangled_name; return name; } // Extracts the prefix of "str" that consists of any characters not // present in "delims" string, and copies this prefix to "result", allocating // space for it. // Returns a pointer to "str" after skipping extracted prefix and first // delimiter char. static const char *ExtractToken(const char *str, const char *delims, char **result) { uptr prefix_len = internal_strcspn(str, delims); *result = (char*)InternalAlloc(prefix_len + 1); internal_memcpy(*result, str, prefix_len); (*result)[prefix_len] = '\0'; const char *prefix_end = str + prefix_len; if (*prefix_end != '\0') prefix_end++; return prefix_end; } // Same as ExtractToken, but converts extracted token to integer. static const char *ExtractInt(const char *str, const char *delims, int *result) { char *buff; const char *ret = ExtractToken(str, delims, &buff); if (buff != 0) { *result = (int)internal_atoll(buff); } InternalFree(buff); return ret; } static const char *ExtractUptr(const char *str, const char *delims, uptr *result) { char *buff; const char *ret = ExtractToken(str, delims, &buff); if (buff != 0) { *result = (uptr)internal_atoll(buff); } InternalFree(buff); return ret; } class ExternalSymbolizerInterface { public: // Can't declare pure virtual functions in sanitizer runtimes: // __cxa_pure_virtual might be unavailable. virtual char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { UNIMPLEMENTED(); } }; // SymbolizerProcess encapsulates communication between the tool and // external symbolizer program, running in a different subprocess. // SymbolizerProcess may not be used from two threads simultaneously. class SymbolizerProcess : public ExternalSymbolizerInterface { public: explicit SymbolizerProcess(const char *path) : path_(path), input_fd_(kInvalidFd), output_fd_(kInvalidFd), times_restarted_(0), failed_to_start_(false), reported_invalid_path_(false) { CHECK(path_); CHECK_NE(path_[0], '\0'); } char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { // Start or restart symbolizer if we failed to send command to it. if (char *res = SendCommandImpl(is_data, module_name, module_offset)) return res; Restart(); } if (!failed_to_start_) { Report("WARNING: Failed to use and restart external symbolizer!\n"); failed_to_start_ = true; } return 0; } private: bool Restart() { if (input_fd_ != kInvalidFd) internal_close(input_fd_); if (output_fd_ != kInvalidFd) internal_close(output_fd_); return StartSymbolizerSubprocess(); } char *SendCommandImpl(bool is_data, const char *module_name, uptr module_offset) { if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd) return 0; CHECK(module_name); if (!RenderInputCommand(buffer_, kBufferSize, is_data, module_name, module_offset)) return 0; if (!writeToSymbolizer(buffer_, internal_strlen(buffer_))) return 0; if (!readFromSymbolizer(buffer_, kBufferSize)) return 0; return buffer_; } bool readFromSymbolizer(char *buffer, uptr max_length) { if (max_length == 0) return true; uptr read_len = 0; while (true) { uptr just_read = internal_read(input_fd_, buffer + read_len, max_length - read_len - 1); // We can't read 0 bytes, as we don't expect external symbolizer to close // its stdout. if (just_read == 0 || just_read == (uptr)-1) { Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); return false; } read_len += just_read; if (ReachedEndOfOutput(buffer, read_len)) break; } buffer[read_len] = '\0'; return true; } bool writeToSymbolizer(const char *buffer, uptr length) { if (length == 0) return true; uptr write_len = internal_write(output_fd_, buffer, length); if (write_len == 0 || write_len == (uptr)-1) { Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); return false; } return true; } bool StartSymbolizerSubprocess() { if (!FileExists(path_)) { if (!reported_invalid_path_) { Report("WARNING: invalid path to external symbolizer!\n"); reported_invalid_path_ = true; } return false; } int *infd = NULL; int *outfd = NULL; // The client program may close its stdin and/or stdout and/or stderr // thus allowing socketpair to reuse file descriptors 0, 1 or 2. // In this case the communication between the forked processes may be // broken if either the parent or the child tries to close or duplicate // these descriptors. The loop below produces two pairs of file // descriptors, each greater than 2 (stderr). int sock_pair[5][2]; for (int i = 0; i < 5; i++) { if (pipe(sock_pair[i]) == -1) { for (int j = 0; j < i; j++) { internal_close(sock_pair[j][0]); internal_close(sock_pair[j][1]); } Report("WARNING: Can't create a socket pair to start " "external symbolizer (errno: %d)\n", errno); return false; } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) { if (infd == NULL) { infd = sock_pair[i]; } else { outfd = sock_pair[i]; for (int j = 0; j < i; j++) { if (sock_pair[j] == infd) continue; internal_close(sock_pair[j][0]); internal_close(sock_pair[j][1]); } break; } } } CHECK(infd); CHECK(outfd); // Real fork() may call user callbacks registered with pthread_atfork(). int pid = internal_fork(); if (pid == -1) { // Fork() failed. internal_close(infd[0]); internal_close(infd[1]); internal_close(outfd[0]); internal_close(outfd[1]); Report("WARNING: failed to fork external symbolizer " " (errno: %d)\n", errno); return false; } else if (pid == 0) { // Child subprocess. internal_close(STDOUT_FILENO); internal_close(STDIN_FILENO); internal_dup2(outfd[0], STDIN_FILENO); internal_dup2(infd[1], STDOUT_FILENO); internal_close(outfd[0]); internal_close(outfd[1]); internal_close(infd[0]); internal_close(infd[1]); for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) internal_close(fd); ExecuteWithDefaultArgs(path_); internal__exit(1); } // Continue execution in parent process. internal_close(outfd[0]); internal_close(infd[1]); input_fd_ = infd[0]; output_fd_ = outfd[1]; // Check that symbolizer subprocess started successfully. int pid_status; SleepForMillis(kSymbolizerStartupTimeMillis); int exited_pid = waitpid(pid, &pid_status, WNOHANG); if (exited_pid != 0) { // Either waitpid failed, or child has already exited. Report("WARNING: external symbolizer didn't start up correctly!\n"); return false; } return true; } virtual bool RenderInputCommand(char *buffer, uptr max_length, bool is_data, const char *module_name, uptr module_offset) const { UNIMPLEMENTED(); } virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const { UNIMPLEMENTED(); } virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const { UNIMPLEMENTED(); } const char *path_; int input_fd_; int output_fd_; static const uptr kBufferSize = 16 * 1024; char buffer_[kBufferSize]; static const uptr kMaxTimesRestarted = 5; static const int kSymbolizerStartupTimeMillis = 10; uptr times_restarted_; bool failed_to_start_; bool reported_invalid_path_; }; // For now we assume the following protocol: // For each request of the form // // passed to STDIN, external symbolizer prints to STDOUT response: // // :: // // :: // ... // class LLVMSymbolizerProcess : public SymbolizerProcess { public: explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {} private: bool RenderInputCommand(char *buffer, uptr max_length, bool is_data, const char *module_name, uptr module_offset) const { internal_snprintf(buffer, max_length, "%s\"%s\" 0x%zx\n", is_data ? "DATA " : "", module_name, module_offset); return true; } bool ReachedEndOfOutput(const char *buffer, uptr length) const { // Empty line marks the end of llvm-symbolizer output. return length >= 2 && buffer[length - 1] == '\n' && buffer[length - 2] == '\n'; } void ExecuteWithDefaultArgs(const char *path_to_binary) const { #if defined(__x86_64__) const char* const kSymbolizerArch = "--default-arch=x86_64"; #elif defined(__i386__) const char* const kSymbolizerArch = "--default-arch=i386"; #elif defined(__powerpc64__) && defined(__BIG_ENDIAN__) const char* const kSymbolizerArch = "--default-arch=powerpc64"; #elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) const char* const kSymbolizerArch = "--default-arch=powerpc64le"; #else const char* const kSymbolizerArch = "--default-arch=unknown"; #endif const char *const inline_flag = common_flags()->symbolize_inline_frames ? "--inlining=true" : "--inlining=false"; execl(path_to_binary, path_to_binary, inline_flag, kSymbolizerArch, (char *)0); } }; class Addr2LineProcess : public SymbolizerProcess { public: Addr2LineProcess(const char *path, const char *module_name) : SymbolizerProcess(path), module_name_(internal_strdup(module_name)) {} const char *module_name() const { return module_name_; } private: bool RenderInputCommand(char *buffer, uptr max_length, bool is_data, const char *module_name, uptr module_offset) const { if (is_data) return false; CHECK_EQ(0, internal_strcmp(module_name, module_name_)); internal_snprintf(buffer, max_length, "0x%zx\n", module_offset); return true; } bool ReachedEndOfOutput(const char *buffer, uptr length) const { // Output should consist of two lines. int num_lines = 0; for (uptr i = 0; i < length; ++i) { if (buffer[i] == '\n') num_lines++; if (num_lines >= 2) return true; } return false; } void ExecuteWithDefaultArgs(const char *path_to_binary) const { execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0); } const char *module_name_; // Owned, leaked. }; class Addr2LinePool : public ExternalSymbolizerInterface { public: explicit Addr2LinePool(const char *addr2line_path, LowLevelAllocator *allocator) : addr2line_path_(addr2line_path), allocator_(allocator), addr2line_pool_(16) {} char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { if (is_data) return 0; Addr2LineProcess *addr2line = 0; for (uptr i = 0; i < addr2line_pool_.size(); ++i) { if (0 == internal_strcmp(module_name, addr2line_pool_[i]->module_name())) { addr2line = addr2line_pool_[i]; break; } } if (!addr2line) { addr2line = new(*allocator_) Addr2LineProcess(addr2line_path_, module_name); addr2line_pool_.push_back(addr2line); } return addr2line->SendCommand(is_data, module_name, module_offset); } private: const char *addr2line_path_; LowLevelAllocator *allocator_; InternalMmapVector addr2line_pool_; }; #if SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset, char *Buffer, int MaxLength); SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset, char *Buffer, int MaxLength); SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_symbolize_flush(); SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int __sanitizer_symbolize_demangle(const char *Name, char *Buffer, int MaxLength); } // extern "C" class InternalSymbolizer { public: typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); static InternalSymbolizer *get(LowLevelAllocator *alloc) { if (__sanitizer_symbolize_code != 0 && __sanitizer_symbolize_data != 0) { return new(*alloc) InternalSymbolizer(); } return 0; } char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data : __sanitizer_symbolize_code; if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize)) return buffer_; return 0; } void Flush() { if (__sanitizer_symbolize_flush) __sanitizer_symbolize_flush(); } const char *Demangle(const char *name) { if (__sanitizer_symbolize_demangle) { for (uptr res_length = 1024; res_length <= InternalSizeClassMap::kMaxSize;) { char *res_buff = static_cast(InternalAlloc(res_length)); uptr req_length = __sanitizer_symbolize_demangle(name, res_buff, res_length); if (req_length > res_length) { res_length = req_length + 1; InternalFree(res_buff); continue; } return res_buff; } } return name; } private: InternalSymbolizer() { } static const int kBufferSize = 16 * 1024; static const int kMaxDemangledNameSize = 1024; char buffer_[kBufferSize]; }; #else // SANITIZER_SUPPORTS_WEAK_HOOKS class InternalSymbolizer { public: static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; } char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { return 0; } void Flush() { } const char *Demangle(const char *name) { return name; } }; #endif // SANITIZER_SUPPORTS_WEAK_HOOKS class POSIXSymbolizer : public Symbolizer { public: POSIXSymbolizer(ExternalSymbolizerInterface *external_symbolizer, InternalSymbolizer *internal_symbolizer, LibbacktraceSymbolizer *libbacktrace_symbolizer) : Symbolizer(), external_symbolizer_(external_symbolizer), internal_symbolizer_(internal_symbolizer), libbacktrace_symbolizer_(libbacktrace_symbolizer) {} SymbolizedStack *SymbolizePC(uptr addr) override { BlockingMutexLock l(&mu_); const char *module_name; uptr module_offset; if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset)) return SymbolizedStack::New(addr); // First, try to use libbacktrace symbolizer (if it's available). if (libbacktrace_symbolizer_ != 0) { mu_.CheckLocked(); if (SymbolizedStack *res = libbacktrace_symbolizer_->SymbolizeCode( addr, module_name, module_offset)) return res; } // Always fill data about module name and offset. SymbolizedStack *res = SymbolizedStack::New(addr); res->info.FillAddressAndModuleInfo(addr, module_name, module_offset); const char *str = SendCommand(false, module_name, module_offset); if (str == 0) { // Symbolizer was not initialized or failed. return res; } bool top_frame = true; SymbolizedStack *last = res; while (true) { char *function_name = 0; str = ExtractToken(str, "\n", &function_name); CHECK(function_name); if (function_name[0] == '\0') { // There are no more frames. break; } SymbolizedStack *cur; if (top_frame) { cur = res; top_frame = false; } else { cur = SymbolizedStack::New(addr); cur->info.FillAddressAndModuleInfo(addr, module_name, module_offset); last->next = cur; last = cur; } AddressInfo *info = &cur->info; info->function = function_name; // Parse :: buffer. char *file_line_info = 0; str = ExtractToken(str, "\n", &file_line_info); CHECK(file_line_info); const char *line_info = ExtractToken(file_line_info, ":", &info->file); line_info = ExtractInt(line_info, ":", &info->line); line_info = ExtractInt(line_info, "", &info->column); InternalFree(file_line_info); // Functions and filenames can be "??", in which case we write 0 // to address info to mark that names are unknown. if (0 == internal_strcmp(info->function, "??")) { InternalFree(info->function); info->function = 0; } if (0 == internal_strcmp(info->file, "??")) { InternalFree(info->file); info->file = 0; } } return res; } bool SymbolizeData(uptr addr, DataInfo *info) override { BlockingMutexLock l(&mu_); LoadedModule *module = FindModuleForAddress(addr); if (module == 0) return false; const char *module_name = module->full_name(); uptr module_offset = addr - module->base_address(); info->Clear(); info->module = internal_strdup(module_name); info->module_offset = module_offset; // First, try to use libbacktrace symbolizer (if it's available). if (libbacktrace_symbolizer_ != 0) { mu_.CheckLocked(); - if (libbacktrace_symbolizer_->SymbolizeData(info)) + if (libbacktrace_symbolizer_->SymbolizeData(addr, info)) return true; } const char *str = SendCommand(true, module_name, module_offset); if (str == 0) return true; str = ExtractToken(str, "\n", &info->name); str = ExtractUptr(str, " ", &info->start); str = ExtractUptr(str, "\n", &info->size); info->start += module->base_address(); return true; } bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, uptr *module_address) override { BlockingMutexLock l(&mu_); return FindModuleNameAndOffsetForAddress(pc, module_name, module_address); } bool CanReturnFileLineInfo() override { return internal_symbolizer_ != 0 || external_symbolizer_ != 0 || libbacktrace_symbolizer_ != 0; } void Flush() override { BlockingMutexLock l(&mu_); if (internal_symbolizer_ != 0) { SymbolizerScope sym_scope(this); internal_symbolizer_->Flush(); } } const char *Demangle(const char *name) override { BlockingMutexLock l(&mu_); // Run hooks even if we don't use internal symbolizer, as cxxabi // demangle may call system functions. SymbolizerScope sym_scope(this); // Try to use libbacktrace demangler (if available). if (libbacktrace_symbolizer_ != 0) { if (const char *demangled = libbacktrace_symbolizer_->Demangle(name)) return demangled; } if (internal_symbolizer_ != 0) return internal_symbolizer_->Demangle(name); return DemangleCXXABI(name); } void PrepareForSandboxing() override { #if SANITIZER_LINUX && !SANITIZER_ANDROID BlockingMutexLock l(&mu_); // Cache /proc/self/exe on Linux. CacheBinaryName(); #endif } private: char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { mu_.CheckLocked(); // First, try to use internal symbolizer. if (internal_symbolizer_) { SymbolizerScope sym_scope(this); return internal_symbolizer_->SendCommand(is_data, module_name, module_offset); } // Otherwise, fall back to external symbolizer. if (external_symbolizer_) { SymbolizerScope sym_scope(this); return external_symbolizer_->SendCommand(is_data, module_name, module_offset); } return 0; } LoadedModule *FindModuleForAddress(uptr address) { mu_.CheckLocked(); bool modules_were_reloaded = false; if (modules_ == 0 || !modules_fresh_) { modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate( kMaxNumberOfModuleContexts * sizeof(LoadedModule))); CHECK(modules_); n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts, /* filter */ 0); CHECK_GT(n_modules_, 0); CHECK_LT(n_modules_, kMaxNumberOfModuleContexts); modules_fresh_ = true; modules_were_reloaded = true; } for (uptr i = 0; i < n_modules_; i++) { if (modules_[i].containsAddress(address)) { return &modules_[i]; } } // Reload the modules and look up again, if we haven't tried it yet. if (!modules_were_reloaded) { // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors. // It's too aggressive to reload the list of modules each time we fail // to find a module for a given address. modules_fresh_ = false; return FindModuleForAddress(address); } return 0; } bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name, uptr *module_offset) { mu_.CheckLocked(); LoadedModule *module = FindModuleForAddress(address); if (module == 0) return false; *module_name = module->full_name(); *module_offset = address - module->base_address(); return true; } // 16K loaded modules should be enough for everyone. static const uptr kMaxNumberOfModuleContexts = 1 << 14; LoadedModule *modules_; // Array of module descriptions is leaked. uptr n_modules_; // If stale, need to reload the modules before looking up addresses. bool modules_fresh_; BlockingMutex mu_; ExternalSymbolizerInterface *external_symbolizer_; // Leaked. InternalSymbolizer *const internal_symbolizer_; // Leaked. LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked. }; Symbolizer *Symbolizer::PlatformInit() { if (!common_flags()->symbolize) { return new(symbolizer_allocator_) POSIXSymbolizer(0, 0, 0); } InternalSymbolizer* internal_symbolizer = InternalSymbolizer::get(&symbolizer_allocator_); ExternalSymbolizerInterface *external_symbolizer = 0; LibbacktraceSymbolizer *libbacktrace_symbolizer = 0; if (!internal_symbolizer) { libbacktrace_symbolizer = LibbacktraceSymbolizer::get(&symbolizer_allocator_); if (!libbacktrace_symbolizer) { const char *path_to_external = common_flags()->external_symbolizer_path; if (path_to_external && path_to_external[0] == '\0') { // External symbolizer is explicitly disabled. Do nothing. } else { // Find path to llvm-symbolizer if it's not provided. if (!path_to_external) path_to_external = FindPathToBinary("llvm-symbolizer"); if (path_to_external) { external_symbolizer = new(symbolizer_allocator_) LLVMSymbolizerProcess(path_to_external); } else if (common_flags()->allow_addr2line) { // If llvm-symbolizer is not found, try to use addr2line. if (const char *addr2line_path = FindPathToBinary("addr2line")) { external_symbolizer = new(symbolizer_allocator_) Addr2LinePool(addr2line_path, &symbolizer_allocator_); } } } } } return new(symbolizer_allocator_) POSIXSymbolizer( external_symbolizer, internal_symbolizer, libbacktrace_symbolizer); } } // namespace __sanitizer #endif // SANITIZER_POSIX Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc (revision 279191) @@ -1,158 +1,158 @@ //===-- sanitizer_unwind_posix.cc ----------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains the unwind.h-based (aka "slow") stack unwinding routines // available to the tools on Linux, Android, FreeBSD and OS X. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_POSIX #include "sanitizer_common.h" #include "sanitizer_stacktrace.h" #if SANITIZER_ANDROID #include // for dlopen() #endif #if SANITIZER_FREEBSD #define _GNU_SOURCE // to declare _Unwind_Backtrace() from #endif #include namespace __sanitizer { //------------------------- SlowUnwindStack ----------------------------------- typedef struct { uptr absolute_pc; uptr stack_top; uptr stack_size; } backtrace_frame_t; extern "C" { typedef void *(*acquire_my_map_info_list_func)(); typedef void (*release_my_map_info_list_func)(void *map); typedef sptr (*unwind_backtrace_signal_arch_func)( void *siginfo, void *sigcontext, void *map_info_list, backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth); acquire_my_map_info_list_func acquire_my_map_info_list; release_my_map_info_list_func release_my_map_info_list; unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch; } // extern "C" #if SANITIZER_ANDROID void SanitizerInitializeUnwinder() { void *p = dlopen("libcorkscrew.so", RTLD_LAZY); if (!p) { VReport(1, "Failed to open libcorkscrew.so. You may see broken stack traces " "in SEGV reports."); return; } acquire_my_map_info_list = (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list"); release_my_map_info_list = (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list"); unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym( p, "unwind_backtrace_signal_arch"); if (!acquire_my_map_info_list || !release_my_map_info_list || !unwind_backtrace_signal_arch) { VReport(1, "Failed to find one of the required symbols in libcorkscrew.so. " "You may see broken stack traces in SEGV reports."); acquire_my_map_info_list = 0; unwind_backtrace_signal_arch = 0; release_my_map_info_list = 0; } } #endif #ifdef __arm__ #define UNWIND_STOP _URC_END_OF_STACK #define UNWIND_CONTINUE _URC_NO_REASON #else #define UNWIND_STOP _URC_NORMAL_STOP #define UNWIND_CONTINUE _URC_NO_REASON #endif uptr Unwind_GetIP(struct _Unwind_Context *ctx) { #ifdef __arm__ uptr val; _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE, 15 /* r15 = PC */, _UVRSD_UINT32, &val); CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed"); // Clear the Thumb bit. return val & ~(uptr)1; #else return _Unwind_GetIP(ctx); #endif } struct UnwindTraceArg { BufferedStackTrace *stack; - uptr max_depth; + u32 max_depth; }; _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { UnwindTraceArg *arg = (UnwindTraceArg*)param; CHECK_LT(arg->stack->size, arg->max_depth); uptr pc = Unwind_GetIP(ctx); arg->stack->trace_buffer[arg->stack->size++] = pc; if (arg->stack->size == arg->max_depth) return UNWIND_STOP; return UNWIND_CONTINUE; } -void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { +void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { CHECK_GE(max_depth, 2); size = 0; UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; _Unwind_Backtrace(Unwind_Trace, &arg); // We need to pop a few frames so that pc is on top. uptr to_pop = LocatePcInTrace(pc); // trace_buffer[0] belongs to the current function so we always pop it, // unless there is only 1 frame in the stack trace (1 frame is always better // than 0!). // 1-frame stacks don't normally happen, but this depends on the actual // unwinder implementation (libgcc, libunwind, etc) which is outside of our // control. if (to_pop == 0 && size > 1) to_pop = 1; PopStackFrames(to_pop); trace_buffer[0] = pc; } void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, - uptr max_depth) { + u32 max_depth) { CHECK_GE(max_depth, 2); if (!unwind_backtrace_signal_arch) { SlowUnwindStack(pc, max_depth); return; } void *map = acquire_my_map_info_list(); CHECK(map); InternalScopedBuffer frames(kStackTraceMax); // siginfo argument appears to be unused. sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map, frames.data(), /* ignore_depth */ 0, max_depth); release_my_map_info_list(map); if (res < 0) return; CHECK_LE((uptr)res, kStackTraceMax); size = 0; // +2 compensate for libcorkscrew unwinder returning addresses of call // instructions instead of raw return addresses. for (sptr i = 0; i < res; ++i) trace_buffer[size++] = frames[i].absolute_pc + 2; } } // namespace __sanitizer #endif // SANITIZER_POSIX Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/sanitizer_win.cc (revision 279191) @@ -1,531 +1,621 @@ //===-- sanitizer_win.cc --------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements windows-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_WINDOWS #define WIN32_LEAN_AND_MEAN #define NOGDI #include #include #include +#include #include #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_mutex.h" #include "sanitizer_placement_new.h" #include "sanitizer_stacktrace.h" namespace __sanitizer { #include "sanitizer_syscall_generic.inc" // --------------------- sanitizer_common.h uptr GetPageSize() { return 1U << 14; // FIXME: is this configurable? } uptr GetMmapGranularity() { return 1U << 16; // FIXME: is this configurable? } uptr GetMaxVirtualAddress() { SYSTEM_INFO si; GetSystemInfo(&si); return (uptr)si.lpMaximumApplicationAddress; } bool FileExists(const char *filename) { UNIMPLEMENTED(); } uptr internal_getpid() { return GetProcessId(GetCurrentProcess()); } // In contrast to POSIX, on Windows GetCurrentThreadId() // returns a system-unique identifier. uptr GetTid() { return GetCurrentThreadId(); } uptr GetThreadSelf() { return GetTid(); } #if !SANITIZER_GO void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom) { CHECK(stack_top); CHECK(stack_bottom); MEMORY_BASIC_INFORMATION mbi; CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0); // FIXME: is it possible for the stack to not be a single allocation? // Are these values what ASan expects to get (reserved, not committed; // including stack guard page) ? *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize; *stack_bottom = (uptr)mbi.AllocationBase; } #endif // #if !SANITIZER_GO void *MmapOrDie(uptr size, const char *mem_type) { void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (rv == 0) { Report("ERROR: %s failed to " "allocate 0x%zx (%zd) bytes of %s (error code: %d)\n", SanitizerToolName, size, size, mem_type, GetLastError()); CHECK("unable to mmap" && 0); } return rv; } void UnmapOrDie(void *addr, uptr size) { if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) { Report("ERROR: %s failed to " "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n", SanitizerToolName, size, size, addr, GetLastError()); CHECK("unable to unmap" && 0); } } void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { // FIXME: is this really "NoReserve"? On Win32 this does not matter much, // but on Win64 it does. void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (p == 0) Report("ERROR: %s failed to " "allocate %p (%zd) bytes at %p (error code: %d)\n", SanitizerToolName, size, size, fixed_addr, GetLastError()); return p; } void *MmapFixedOrDie(uptr fixed_addr, uptr size) { return MmapFixedNoReserve(fixed_addr, size); } void *MmapNoReserveOrDie(uptr size, const char *mem_type) { // FIXME: make this really NoReserve? return MmapOrDie(size, mem_type); } void *Mprotect(uptr fixed_addr, uptr size) { - return VirtualAlloc((LPVOID)fixed_addr, size, - MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); + void *res = VirtualAlloc((LPVOID)fixed_addr, size, + MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); + if (res == 0) + Report("WARNING: %s failed to " + "mprotect %p (%zd) bytes at %p (error code: %d)\n", + SanitizerToolName, size, size, fixed_addr, GetLastError()); + return res; } void FlushUnneededShadowMemory(uptr addr, uptr size) { // This is almost useless on 32-bits. - // FIXME: add madvice-analog when we move to 64-bits. + // FIXME: add madvise-analog when we move to 64-bits. } +void NoHugePagesInRegion(uptr addr, uptr size) { + // FIXME: probably similar to FlushUnneededShadowMemory. +} + +void DontDumpShadowMemory(uptr addr, uptr length) { + // This is almost useless on 32-bits. + // FIXME: add madvise-analog when we move to 64-bits. +} + bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { - // FIXME: shall we do anything here on Windows? - return true; + MEMORY_BASIC_INFORMATION mbi; + CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi))); + return mbi.Protect == PAGE_NOACCESS && + (uptr)mbi.BaseAddress + mbi.RegionSize >= range_end; } void *MapFileToMemory(const char *file_name, uptr *buff_size) { UNIMPLEMENTED(); } void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) { UNIMPLEMENTED(); } static const int kMaxEnvNameLength = 128; static const DWORD kMaxEnvValueLength = 32767; namespace { struct EnvVariable { char name[kMaxEnvNameLength]; char value[kMaxEnvValueLength]; }; } // namespace static const int kEnvVariables = 5; static EnvVariable env_vars[kEnvVariables]; static int num_env_vars; const char *GetEnv(const char *name) { // Note: this implementation caches the values of the environment variables // and limits their quantity. for (int i = 0; i < num_env_vars; i++) { if (0 == internal_strcmp(name, env_vars[i].name)) return env_vars[i].value; } CHECK_LT(num_env_vars, kEnvVariables); DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value, kMaxEnvValueLength); if (rv > 0 && rv < kMaxEnvValueLength) { CHECK_LT(internal_strlen(name), kMaxEnvNameLength); internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength); num_env_vars++; return env_vars[num_env_vars - 1].value; } return 0; } const char *GetPwd() { UNIMPLEMENTED(); } u32 GetUid() { UNIMPLEMENTED(); } +namespace { +struct ModuleInfo { + HMODULE handle; + uptr base_address; + uptr end_address; +}; + +int CompareModulesBase(const void *pl, const void *pr) { + const ModuleInfo &l = *(ModuleInfo *)pl, &r = *(ModuleInfo *)pr; + if (l.base_address < r.base_address) + return -1; + return l.base_address > r.base_address; +} +} // namespace + void DumpProcessMap() { - UNIMPLEMENTED(); + Report("Dumping process modules:\n"); + HANDLE cur_process = GetCurrentProcess(); + + // Query the list of modules. Start by assuming there are no more than 256 + // modules and retry if that's not sufficient. + ModuleInfo *modules; + size_t num_modules; + { + HMODULE *hmodules = 0; + uptr modules_buffer_size = sizeof(HMODULE) * 256; + DWORD bytes_required; + while (!hmodules) { + hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__); + CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size, + &bytes_required)); + if (bytes_required > modules_buffer_size) { + // Either there turned out to be more than 256 hmodules, or new hmodules + // could have loaded since the last try. Retry. + UnmapOrDie(hmodules, modules_buffer_size); + hmodules = 0; + modules_buffer_size = bytes_required; + } + } + + num_modules = bytes_required / sizeof(HMODULE); + modules = + (ModuleInfo *)MmapOrDie(num_modules * sizeof(ModuleInfo), __FUNCTION__); + for (size_t i = 0; i < num_modules; ++i) { + modules[i].handle = hmodules[i]; + MODULEINFO mi; + if (!GetModuleInformation(cur_process, hmodules[i], &mi, sizeof(mi))) + continue; + modules[i].base_address = (uptr)mi.lpBaseOfDll; + modules[i].end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage; + } + UnmapOrDie(hmodules, modules_buffer_size); + } + + qsort(modules, num_modules, sizeof(ModuleInfo), CompareModulesBase); + + for (size_t i = 0; i < num_modules; ++i) { + const ModuleInfo &mi = modules[i]; + char module_name[MAX_PATH]; + bool got_module_name = GetModuleFileNameEx( + cur_process, mi.handle, module_name, sizeof(module_name)); + if (mi.end_address != 0) { + Printf("\t%p-%p %s\n", mi.base_address, mi.end_address, + got_module_name ? module_name : "[no name]"); + } else if (got_module_name) { + Printf("\t??\?-??? %s\n", module_name); + } else { + Printf("\t???\n"); + } + } + UnmapOrDie(modules, num_modules * sizeof(ModuleInfo)); } void DisableCoreDumperIfNecessary() { // Do nothing. } void ReExec() { UNIMPLEMENTED(); } void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { (void)args; // Nothing here for now. } bool StackSizeIsUnlimited() { UNIMPLEMENTED(); } void SetStackSizeLimitInBytes(uptr limit) { UNIMPLEMENTED(); } bool AddressSpaceIsUnlimited() { UNIMPLEMENTED(); } void SetAddressSpaceUnlimited() { UNIMPLEMENTED(); } char *FindPathToBinary(const char *name) { // Nothing here for now. return 0; } void SleepForSeconds(int seconds) { Sleep(seconds * 1000); } void SleepForMillis(int millis) { Sleep(millis); } u64 NanoTime() { return 0; } void Abort() { - abort(); - internal__exit(-1); // abort is not NORETURN on Windows. + if (::IsDebuggerPresent()) + __debugbreak(); + internal__exit(3); } uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter) { UNIMPLEMENTED(); }; #ifndef SANITIZER_GO int Atexit(void (*function)(void)) { return atexit(function); } #endif // ------------------ sanitizer_libc.h uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, u64 offset) { UNIMPLEMENTED(); } uptr internal_munmap(void *addr, uptr length) { UNIMPLEMENTED(); } uptr internal_close(fd_t fd) { UNIMPLEMENTED(); } int internal_isatty(fd_t fd) { return _isatty(fd); } uptr internal_open(const char *filename, int flags) { UNIMPLEMENTED(); } uptr internal_open(const char *filename, int flags, u32 mode) { UNIMPLEMENTED(); } uptr OpenFile(const char *filename, bool write) { UNIMPLEMENTED(); } uptr internal_read(fd_t fd, void *buf, uptr count) { UNIMPLEMENTED(); } uptr internal_write(fd_t fd, const void *buf, uptr count) { if (fd != kStderrFd) UNIMPLEMENTED(); static HANDLE output_stream = 0; // Abort immediately if we know printing is not possible. if (output_stream == INVALID_HANDLE_VALUE) return 0; // If called for the first time, try to use stderr to output stuff, // falling back to stdout if anything goes wrong. bool fallback_to_stdout = false; if (output_stream == 0) { output_stream = GetStdHandle(STD_ERROR_HANDLE); // We don't distinguish "no such handle" from error. if (output_stream == 0) output_stream = INVALID_HANDLE_VALUE; if (output_stream == INVALID_HANDLE_VALUE) { // Retry with stdout? output_stream = GetStdHandle(STD_OUTPUT_HANDLE); if (output_stream == 0) output_stream = INVALID_HANDLE_VALUE; if (output_stream == INVALID_HANDLE_VALUE) return 0; } else { // Successfully got an stderr handle. However, if WriteFile() fails, // we can still try to fallback to stdout. fallback_to_stdout = true; } } DWORD ret; if (WriteFile(output_stream, buf, count, &ret, 0)) return ret; // Re-try with stdout if using a valid stderr handle fails. if (fallback_to_stdout) { output_stream = GetStdHandle(STD_OUTPUT_HANDLE); if (output_stream == 0) output_stream = INVALID_HANDLE_VALUE; if (output_stream != INVALID_HANDLE_VALUE) return internal_write(fd, buf, count); } return 0; } uptr internal_stat(const char *path, void *buf) { UNIMPLEMENTED(); } uptr internal_lstat(const char *path, void *buf) { UNIMPLEMENTED(); } uptr internal_fstat(fd_t fd, void *buf) { UNIMPLEMENTED(); } uptr internal_filesize(fd_t fd) { UNIMPLEMENTED(); } uptr internal_dup2(int oldfd, int newfd) { UNIMPLEMENTED(); } uptr internal_readlink(const char *path, char *buf, uptr bufsize) { UNIMPLEMENTED(); } uptr internal_sched_yield() { Sleep(0); return 0; } void internal__exit(int exitcode) { ExitProcess(exitcode); } uptr internal_ftruncate(fd_t fd, uptr size) { UNIMPLEMENTED(); } uptr internal_rename(const char *oldpath, const char *newpath) { UNIMPLEMENTED(); } uptr GetRSS() { return 0; } +void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } +void internal_join_thread(void *th) { } + // ---------------------- BlockingMutex ---------------- {{{1 const uptr LOCK_UNINITIALIZED = 0; const uptr LOCK_READY = (uptr)-1; BlockingMutex::BlockingMutex(LinkerInitialized li) { // FIXME: see comments in BlockingMutex::Lock() for the details. CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED); CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); owner_ = LOCK_READY; } BlockingMutex::BlockingMutex() { CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); owner_ = LOCK_READY; } void BlockingMutex::Lock() { if (owner_ == LOCK_UNINITIALIZED) { // FIXME: hm, global BlockingMutex objects are not initialized?!? // This might be a side effect of the clang+cl+link Frankenbuild... new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1)); // FIXME: If it turns out the linker doesn't invoke our // constructors, we should probably manually Lock/Unlock all the global // locks while we're starting in one thread to avoid double-init races. } EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_); CHECK_EQ(owner_, LOCK_READY); owner_ = GetThreadSelf(); } void BlockingMutex::Unlock() { CHECK_EQ(owner_, GetThreadSelf()); owner_ = LOCK_READY; LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); } void BlockingMutex::CheckLocked() { CHECK_EQ(owner_, GetThreadSelf()); } uptr GetTlsSize() { return 0; } void InitTlsSize() { } void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { #ifdef SANITIZER_GO *stk_addr = 0; *stk_size = 0; *tls_addr = 0; *tls_size = 0; #else uptr stack_top, stack_bottom; GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); *stk_addr = stack_bottom; *stk_size = stack_top - stack_bottom; *tls_addr = 0; *tls_size = 0; #endif } #if !SANITIZER_GO -void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { +void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { CHECK_GE(max_depth, 2); // FIXME: CaptureStackBackTrace might be too slow for us. // FIXME: Compare with StackWalk64. // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc size = CaptureStackBackTrace(2, Min(max_depth, kStackTraceMax), (void**)trace, 0); if (size == 0) return; // Skip the RTL frames by searching for the PC in the stacktrace. uptr pc_location = LocatePcInTrace(pc); PopStackFrames(pc_location); } void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, - uptr max_depth) { + u32 max_depth) { CONTEXT ctx = *(CONTEXT *)context; STACKFRAME64 stack_frame; memset(&stack_frame, 0, sizeof(stack_frame)); size = 0; #if defined(_WIN64) int machine_type = IMAGE_FILE_MACHINE_AMD64; stack_frame.AddrPC.Offset = ctx.Rip; stack_frame.AddrFrame.Offset = ctx.Rbp; stack_frame.AddrStack.Offset = ctx.Rsp; #else int machine_type = IMAGE_FILE_MACHINE_I386; stack_frame.AddrPC.Offset = ctx.Eip; stack_frame.AddrFrame.Offset = ctx.Ebp; stack_frame.AddrStack.Offset = ctx.Esp; #endif stack_frame.AddrPC.Mode = AddrModeFlat; stack_frame.AddrFrame.Mode = AddrModeFlat; stack_frame.AddrStack.Mode = AddrModeFlat; while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(), &stack_frame, &ctx, NULL, &SymFunctionTableAccess64, &SymGetModuleBase64, NULL) && size < Min(max_depth, kStackTraceMax)) { trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset; } } #endif // #if !SANITIZER_GO void ReportFile::Write(const char *buffer, uptr length) { SpinMutexLock l(mu); ReopenIfNecessary(); if (length != internal_write(fd, buffer, length)) { // stderr may be closed, but we may be able to print to the debugger // instead. This is the case when launching a program from Visual Studio, // and the following routine should write to its console. OutputDebugStringA(buffer); } } void SetAlternateSignalStack() { // FIXME: Decide what to do on Windows. } void UnsetAlternateSignalStack() { // FIXME: Decide what to do on Windows. } void InstallDeadlySignalHandlers(SignalHandlerType handler) { (void)handler; // FIXME: Decide what to do on Windows. } bool IsDeadlySignal(int signum) { // FIXME: Decide what to do on Windows. return false; } bool IsAccessibleMemoryRange(uptr beg, uptr size) { // FIXME: Actually implement this function. return true; } } // namespace __sanitizer #endif // _WIN32 Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/scripts/check_lint.sh (revision 279191) @@ -1,133 +1,140 @@ #!/bin/sh SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # Guess path to LLVM_CHECKOUT if not provided if [ "${LLVM_CHECKOUT}" = "" ]; then LLVM_CHECKOUT="${SCRIPT_DIR}/../../../../../" fi # python tools setup CPPLINT=${SCRIPT_DIR}/cpplint.py LITLINT=${SCRIPT_DIR}/litlint.py if [ "${PYTHON_EXECUTABLE}" != "" ]; then CPPLINT="${PYTHON_EXECUTABLE} ${CPPLINT}" LITLINT="${PYTHON_EXECUTABLE} ${LITLINT}" fi # Filters # TODO: remove some of these filters LLVM_LINT_FILTER=-,+whitespace COMMON_LINT_FILTER=-build/include,-build/header_guard,-legal/copyright,-whitespace/comments,-readability/casting,\ -build/namespaces ASAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int ASAN_TEST_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/sizeof,-runtime/int,-runtime/printf,-runtime/threadsafe_fn ASAN_LIT_TEST_LINT_FILTER=${ASAN_TEST_LINT_FILTER},-whitespace/line_length TSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} TSAN_TEST_LINT_FILTER=${TSAN_RTL_LINT_FILTER},-runtime/threadsafe_fn,-runtime/int TSAN_LIT_TEST_LINT_FILTER=${TSAN_TEST_LINT_FILTER},-whitespace/line_length MSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} LSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER} LSAN_LIT_TEST_LINT_FILTER=${LSAN_RTL_LINT_FILTER},-whitespace/line_length DFSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/printf,-runtime/references,-readability/function COMMON_RTL_INC_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/sizeof,-runtime/printf,-readability/fn_size SANITIZER_INCLUDES_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int -MKTEMP="mktemp -q /tmp/tmp.XXXXXXXXXX" + +MKTEMP_DIR=$(mktemp -qd /tmp/check_lint.XXXXXXXXXX) +MKTEMP="mktemp -q ${MKTEMP_DIR}/tmp.XXXXXXXXXX" +cleanup() { + rm -rf $MKTEMP_DIR +} +trap cleanup EXIT + cd ${LLVM_CHECKOUT} EXITSTATUS=0 ERROR_LOG=$(${MKTEMP}) run_lint() { FILTER=$1 shift TASK_LOG=$(${MKTEMP}) ${CPPLINT} --filter=${FILTER} "$@" 2>$TASK_LOG if [ "$?" != "0" ]; then cat $TASK_LOG | grep -v "Done processing" | grep -v "Total errors found" \ | grep -v "Skipping input" >> $ERROR_LOG fi if [ "${SILENT}" != "1" ]; then cat $TASK_LOG fi ${LITLINT} "$@" 2>>$ERROR_LOG } run_lint ${LLVM_LINT_FILTER} --filter=${LLVM_LINT_FILTER} \ lib/Transforms/Instrumentation/*Sanitizer.cpp & if [ "${COMPILER_RT}" = "" ]; then COMPILER_RT=projects/compiler-rt fi LIT_TESTS=${COMPILER_RT}/test # Headers SANITIZER_INCLUDES=${COMPILER_RT}/include/sanitizer run_lint ${SANITIZER_INCLUDES_LINT_FILTER} ${SANITIZER_INCLUDES}/*.h & # Sanitizer_common COMMON_RTL=${COMPILER_RT}/lib/sanitizer_common run_lint ${COMMON_RTL_INC_LINT_FILTER} ${COMMON_RTL}/*.cc \ ${COMMON_RTL}/*.h \ ${COMMON_RTL}/tests/*.cc & # Interception INTERCEPTION=${COMPILER_RT}/lib/interception run_lint ${ASAN_RTL_LINT_FILTER} ${INTERCEPTION}/*.cc \ ${INTERCEPTION}/*.h & # ASan ASAN_RTL=${COMPILER_RT}/lib/asan run_lint ${ASAN_RTL_LINT_FILTER} ${ASAN_RTL}/*.cc \ ${ASAN_RTL}/*.h & run_lint ${ASAN_TEST_LINT_FILTER} ${ASAN_RTL}/tests/*.cc \ ${ASAN_RTL}/tests/*.h & run_lint ${ASAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/asan/*/*.cc & # TSan TSAN_RTL=${COMPILER_RT}/lib/tsan run_lint ${TSAN_RTL_LINT_FILTER} ${TSAN_RTL}/rtl/*.cc \ ${TSAN_RTL}/rtl/*.h & run_lint ${TSAN_TEST_LINT_FILTER} ${TSAN_RTL}/tests/rtl/*.cc \ ${TSAN_RTL}/tests/rtl/*.h \ ${TSAN_RTL}/tests/unit/*.cc & run_lint ${TSAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/tsan/*.cc & # MSan MSAN_RTL=${COMPILER_RT}/lib/msan run_lint ${MSAN_RTL_LINT_FILTER} ${MSAN_RTL}/*.cc \ ${MSAN_RTL}/*.h & # LSan LSAN_RTL=${COMPILER_RT}/lib/lsan run_lint ${LSAN_RTL_LINT_FILTER} ${LSAN_RTL}/*.cc \ ${LSAN_RTL}/*.h & run_lint ${LSAN_LIT_TEST_LINT_FILTER} ${LIT_TESTS}/lsan/*/*.cc & # DFSan DFSAN_RTL=${COMPILER_RT}/lib/dfsan run_lint ${DFSAN_RTL_LINT_FILTER} ${DFSAN_RTL}/*.cc \ ${DFSAN_RTL}/*.h & ${DFSAN_RTL}/scripts/check_custom_wrappers.sh >> $ERROR_LOG # Misc files FILES=${COMMON_RTL}/*.inc TMPFILES="" for FILE in $FILES; do TMPFILE="$(${MKTEMP}).$(basename ${FILE}).cc" cp -f $FILE $TMPFILE run_lint ${COMMON_RTL_INC_LINT_FILTER} $TMPFILE & TMPFILES="$TMPFILES $TMPFILE" done wait for temp in $TMPFILES; do rm -f $temp done if [ -s $ERROR_LOG ]; then cat $ERROR_LOG exit 1 fi exit 0 Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/scripts/sancov.py =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/scripts/sancov.py (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/scripts/sancov.py (revision 279191) @@ -1,147 +1,147 @@ #!/usr/bin/env python # Merge or print the coverage data collected by asan's coverage. # Input files are sequences of 4-byte integers. # We need to merge these integers into a set and then # either print them (as hex) or dump them into another file. import array import struct import sys import bisect import os.path prog_name = ""; def Usage(): print >> sys.stderr, "Usage: \n" + \ " " + prog_name + " merge file1 [file2 ...] > output\n" \ " " + prog_name + " print file1 [file2 ...]\n" \ " " + prog_name + " unpack file1 [file2 ...]\n" \ " " + prog_name + " rawunpack file1 [file2 ...]\n" exit(1) def ReadOneFile(path): with open(path, mode="rb") as f: f.seek(0, 2) size = f.tell() f.seek(0, 0) s = set(array.array('I', f.read(size))) print >>sys.stderr, "%s: read %d PCs from %s" % (prog_name, size / 4, path) return s def Merge(files): s = set() for f in files: s = s.union(ReadOneFile(f)) print >> sys.stderr, "%s: %d files merged; %d PCs total" % \ (prog_name, len(files), len(s)) return sorted(s) def PrintFiles(files): s = Merge(files) for i in s: print "0x%x" % i def MergeAndPrint(files): if sys.stdout.isatty(): Usage() s = Merge(files) a = array.array('I', s) a.tofile(sys.stdout) def UnpackOneFile(path): with open(path, mode="rb") as f: print >> sys.stderr, "%s: unpacking %s" % (prog_name, path) while True: header = f.read(12) if not header: return if len(header) < 12: break pid, module_length, blob_size = struct.unpack('iII', header) module = f.read(module_length) blob = f.read(blob_size) assert(len(module) == module_length) assert(len(blob) == blob_size) extracted_file = "%s.%d.sancov" % (module, pid) print >> sys.stderr, "%s: extracting %s" % \ (prog_name, extracted_file) # The packed file may contain multiple blobs for the same pid/module # pair. Append to the end of the file instead of overwriting. with open(extracted_file, 'ab') as f2: f2.write(blob) # fail raise Exception('Error reading file %s' % path) def Unpack(files): for f in files: UnpackOneFile(f) def UnpackOneRawFile(path, map_path): mem_map = [] with open(map_path, mode="rt") as f_map: print >> sys.stderr, "%s: reading map %s" % (prog_name, map_path) bits = int(f_map.readline()) for line in f_map: parts = line.rstrip().split() mem_map.append((int(parts[0], 16), int(parts[1], 16), int(parts[2], 16), ' '.join(parts[3:]))) mem_map.sort(key=lambda m : m[0]) mem_map_keys = [m[0] for m in mem_map] with open(path, mode="rb") as f: print >> sys.stderr, "%s: unpacking %s" % (prog_name, path) f.seek(0, 2) size = f.tell() f.seek(0, 0) if bits == 64: typecode = 'L' else: typecode = 'I' pcs = array.array(typecode, f.read(size)) mem_map_pcs = [[] for i in range(0, len(mem_map))] for pc in pcs: if pc == 0: continue map_idx = bisect.bisect(mem_map_keys, pc) - 1 (start, end, base, module_path) = mem_map[map_idx] assert pc >= start if pc >= end: print >> sys.stderr, "warning: %s: pc %x outside of any known mapping" % (prog_name, pc) continue mem_map_pcs[map_idx].append(pc - base) for ((start, end, base, module_path), pc_list) in zip(mem_map, mem_map_pcs): if len(pc_list) == 0: continue assert path.endswith('.sancov.raw') dst_path = module_path + '.' + os.path.basename(path)[:-4] - print "writing %d PCs to %s" % (len(pc_list), dst_path) + print >> sys.stderr, "%s: writing %d PCs to %s" % (prog_name, len(pc_list), dst_path) arr = array.array('I') arr.fromlist(sorted(pc_list)) with open(dst_path, 'ab') as f2: arr.tofile(f2) def RawUnpack(files): for f in files: if not f.endswith('.sancov.raw'): raise Exception('Unexpected raw file name %s' % f) f_map = f[:-3] + 'map' UnpackOneRawFile(f, f_map) if __name__ == '__main__': prog_name = sys.argv[0] if len(sys.argv) <= 2: Usage(); if sys.argv[1] == "print": PrintFiles(sys.argv[2:]) elif sys.argv[1] == "merge": MergeAndPrint(sys.argv[2:]) elif sys.argv[1] == "unpack": Unpack(sys.argv[2:]) elif sys.argv[1] == "rawunpack": RawUnpack(sys.argv[2:]) else: Usage() Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_allocator_test.cc (revision 279191) @@ -1,862 +1,860 @@ //===-- sanitizer_allocator_test.cc ---------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer/AddressSanitizer runtime. // Tests for sanitizer_allocator.h. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_test_utils.h" #include "sanitizer_pthread_wrappers.h" #include "gtest/gtest.h" #include #include #include #include // Too slow for debug build -#if TSAN_DEBUG == 0 +#if !SANITIZER_DEBUG -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 static const uptr kAllocatorSpace = 0x700000000000ULL; static const uptr kAllocatorSize = 0x010000000000ULL; // 1T. static const u64 kAddressSpaceSize = 1ULL << 47; typedef SizeClassAllocator64< kAllocatorSpace, kAllocatorSize, 16, DefaultSizeClassMap> Allocator64; typedef SizeClassAllocator64< kAllocatorSpace, kAllocatorSize, 16, CompactSizeClassMap> Allocator64Compact; +#elif defined(__mips64) +static const u64 kAddressSpaceSize = 1ULL << 40; #else static const u64 kAddressSpaceSize = 1ULL << 32; #endif static const uptr kRegionSizeLog = FIRST_32_SECOND_64(20, 24); static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog; typedef SizeClassAllocator32< 0, kAddressSpaceSize, /*kMetadataSize*/16, CompactSizeClassMap, kRegionSizeLog, FlatByteMap > Allocator32Compact; template void TestSizeClassMap() { typedef SizeClassMap SCMap; // SCMap::Print(); SCMap::Validate(); } TEST(SanitizerCommon, DefaultSizeClassMap) { TestSizeClassMap(); } TEST(SanitizerCommon, CompactSizeClassMap) { TestSizeClassMap(); } TEST(SanitizerCommon, InternalSizeClassMap) { TestSizeClassMap(); } template void TestSizeClassAllocator() { Allocator *a = new Allocator; a->Init(); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000, 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000}; std::vector allocated; uptr last_total_allocated = 0; for (int i = 0; i < 3; i++) { // Allocate a bunch of chunks. for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) { uptr size = sizes[s]; if (!a->CanAllocate(size, 1)) continue; // printf("s = %ld\n", size); uptr n_iter = std::max((uptr)6, 8000000 / size); // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter); for (uptr i = 0; i < n_iter; i++) { uptr class_id0 = Allocator::SizeClassMapT::ClassID(size); char *x = (char*)cache.Allocate(a, class_id0); x[0] = 0; x[size - 1] = 0; x[size / 2] = 0; allocated.push_back(x); CHECK_EQ(x, a->GetBlockBegin(x)); CHECK_EQ(x, a->GetBlockBegin(x + size - 1)); CHECK(a->PointerIsMine(x)); CHECK(a->PointerIsMine(x + size - 1)); CHECK(a->PointerIsMine(x + size / 2)); CHECK_GE(a->GetActuallyAllocatedSize(x), size); uptr class_id = a->GetSizeClass(x); CHECK_EQ(class_id, Allocator::SizeClassMapT::ClassID(size)); uptr *metadata = reinterpret_cast(a->GetMetaData(x)); metadata[0] = reinterpret_cast(x) + 1; metadata[1] = 0xABCD; } } // Deallocate all. for (uptr i = 0; i < allocated.size(); i++) { void *x = allocated[i]; uptr *metadata = reinterpret_cast(a->GetMetaData(x)); CHECK_EQ(metadata[0], reinterpret_cast(x) + 1); CHECK_EQ(metadata[1], 0xABCD); cache.Deallocate(a, a->GetSizeClass(x), x); } allocated.clear(); uptr total_allocated = a->TotalMemoryUsed(); if (last_total_allocated == 0) last_total_allocated = total_allocated; CHECK_EQ(last_total_allocated, total_allocated); } // Check that GetBlockBegin never crashes. for (uptr x = 0, step = kAddressSpaceSize / 100000; x < kAddressSpaceSize - step; x += step) if (a->PointerIsMine(reinterpret_cast(x))) Ident(a->GetBlockBegin(reinterpret_cast(x))); a->TestOnlyUnmap(); delete a; } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64) { TestSizeClassAllocator(); } TEST(SanitizerCommon, SizeClassAllocator64Compact) { TestSizeClassAllocator(); } #endif TEST(SanitizerCommon, SizeClassAllocator32Compact) { TestSizeClassAllocator(); } template void SizeClassAllocatorMetadataStress() { Allocator *a = new Allocator; a->Init(); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); const uptr kNumAllocs = 1 << 13; void *allocated[kNumAllocs]; void *meta[kNumAllocs]; for (uptr i = 0; i < kNumAllocs; i++) { void *x = cache.Allocate(a, 1 + i % 50); allocated[i] = x; meta[i] = a->GetMetaData(x); } // Get Metadata kNumAllocs^2 times. for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) { uptr idx = i % kNumAllocs; void *m = a->GetMetaData(allocated[idx]); EXPECT_EQ(m, meta[idx]); } for (uptr i = 0; i < kNumAllocs; i++) { cache.Deallocate(a, 1 + i % 50, allocated[i]); } a->TestOnlyUnmap(); delete a; } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) { SizeClassAllocatorMetadataStress(); } TEST(SanitizerCommon, SizeClassAllocator64CompactMetadataStress) { SizeClassAllocatorMetadataStress(); } -#endif // SANITIZER_WORDSIZE == 64 +#endif // SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator32CompactMetadataStress) { SizeClassAllocatorMetadataStress(); } template void SizeClassAllocatorGetBlockBeginStress() { Allocator *a = new Allocator; a->Init(); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); uptr max_size_class = Allocator::kNumClasses - 1; uptr size = Allocator::SizeClassMapT::Size(max_size_class); u64 G8 = 1ULL << 33; // Make sure we correctly compute GetBlockBegin() w/o overflow. for (size_t i = 0; i <= G8 / size; i++) { void *x = cache.Allocate(a, max_size_class); void *beg = a->GetBlockBegin(x); // if ((i & (i - 1)) == 0) // fprintf(stderr, "[%zd] %p %p\n", i, x, beg); EXPECT_EQ(x, beg); } a->TestOnlyUnmap(); delete a; } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64GetBlockBegin) { SizeClassAllocatorGetBlockBeginStress(); } TEST(SanitizerCommon, SizeClassAllocator64CompactGetBlockBegin) { SizeClassAllocatorGetBlockBeginStress(); } TEST(SanitizerCommon, SizeClassAllocator32CompactGetBlockBegin) { SizeClassAllocatorGetBlockBeginStress(); } -#endif // SANITIZER_WORDSIZE == 64 +#endif // SANITIZER_CAN_USE_ALLOCATOR64 struct TestMapUnmapCallback { static int map_count, unmap_count; void OnMap(uptr p, uptr size) const { map_count++; } void OnUnmap(uptr p, uptr size) const { unmap_count++; } }; int TestMapUnmapCallback::map_count; int TestMapUnmapCallback::unmap_count; -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) { TestMapUnmapCallback::map_count = 0; TestMapUnmapCallback::unmap_count = 0; typedef SizeClassAllocator64< kAllocatorSpace, kAllocatorSize, 16, DefaultSizeClassMap, TestMapUnmapCallback> Allocator64WithCallBack; Allocator64WithCallBack *a = new Allocator64WithCallBack; a->Init(); EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state. SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); AllocatorStats stats; stats.Init(); a->AllocateBatch(&stats, &cache, 32); EXPECT_EQ(TestMapUnmapCallback::map_count, 3); // State + alloc + metadata. a->TestOnlyUnmap(); EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); // The whole thing. delete a; } #endif TEST(SanitizerCommon, SizeClassAllocator32MapUnmapCallback) { TestMapUnmapCallback::map_count = 0; TestMapUnmapCallback::unmap_count = 0; typedef SizeClassAllocator32< 0, kAddressSpaceSize, /*kMetadataSize*/16, CompactSizeClassMap, kRegionSizeLog, FlatByteMap, TestMapUnmapCallback> Allocator32WithCallBack; Allocator32WithCallBack *a = new Allocator32WithCallBack; a->Init(); EXPECT_EQ(TestMapUnmapCallback::map_count, 0); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); AllocatorStats stats; stats.Init(); a->AllocateBatch(&stats, &cache, 32); EXPECT_EQ(TestMapUnmapCallback::map_count, 1); a->TestOnlyUnmap(); EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); delete a; // fprintf(stderr, "Map: %d Unmap: %d\n", // TestMapUnmapCallback::map_count, // TestMapUnmapCallback::unmap_count); } TEST(SanitizerCommon, LargeMmapAllocatorMapUnmapCallback) { TestMapUnmapCallback::map_count = 0; TestMapUnmapCallback::unmap_count = 0; LargeMmapAllocator a; - a.Init(); + a.Init(/* may_return_null */ false); AllocatorStats stats; stats.Init(); void *x = a.Allocate(&stats, 1 << 20, 1); EXPECT_EQ(TestMapUnmapCallback::map_count, 1); a.Deallocate(&stats, x); EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); } template void FailInAssertionOnOOM() { Allocator a; a.Init(); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); AllocatorStats stats; stats.Init(); for (int i = 0; i < 1000000; i++) { a.AllocateBatch(&stats, &cache, 52); } a.TestOnlyUnmap(); } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64Overflow) { EXPECT_DEATH(FailInAssertionOnOOM(), "Out of memory"); } #endif #if !defined(_WIN32) // FIXME: This currently fails on Windows. TEST(SanitizerCommon, LargeMmapAllocator) { LargeMmapAllocator<> a; - a.Init(); + a.Init(/* may_return_null */ false); AllocatorStats stats; stats.Init(); static const int kNumAllocs = 1000; char *allocated[kNumAllocs]; static const uptr size = 4000; // Allocate some. for (int i = 0; i < kNumAllocs; i++) { allocated[i] = (char *)a.Allocate(&stats, size, 1); CHECK(a.PointerIsMine(allocated[i])); } // Deallocate all. CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs); for (int i = 0; i < kNumAllocs; i++) { char *p = allocated[i]; CHECK(a.PointerIsMine(p)); a.Deallocate(&stats, p); } // Check that non left. CHECK_EQ(a.TotalMemoryUsed(), 0); // Allocate some more, also add metadata. for (int i = 0; i < kNumAllocs; i++) { char *x = (char *)a.Allocate(&stats, size, 1); CHECK_GE(a.GetActuallyAllocatedSize(x), size); uptr *meta = reinterpret_cast(a.GetMetaData(x)); *meta = i; allocated[i] = x; } for (int i = 0; i < kNumAllocs * kNumAllocs; i++) { char *p = allocated[i % kNumAllocs]; CHECK(a.PointerIsMine(p)); CHECK(a.PointerIsMine(p + 2000)); } CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs); // Deallocate all in reverse order. for (int i = 0; i < kNumAllocs; i++) { int idx = kNumAllocs - i - 1; char *p = allocated[idx]; uptr *meta = reinterpret_cast(a.GetMetaData(p)); CHECK_EQ(*meta, idx); CHECK(a.PointerIsMine(p)); a.Deallocate(&stats, p); } CHECK_EQ(a.TotalMemoryUsed(), 0); // Test alignments. uptr max_alignment = SANITIZER_WORDSIZE == 64 ? (1 << 28) : (1 << 24); for (uptr alignment = 8; alignment <= max_alignment; alignment *= 2) { const uptr kNumAlignedAllocs = 100; for (uptr i = 0; i < kNumAlignedAllocs; i++) { uptr size = ((i % 10) + 1) * 4096; char *p = allocated[i] = (char *)a.Allocate(&stats, size, alignment); CHECK_EQ(p, a.GetBlockBegin(p)); CHECK_EQ(p, a.GetBlockBegin(p + size - 1)); CHECK_EQ(p, a.GetBlockBegin(p + size / 2)); CHECK_EQ(0, (uptr)allocated[i] % alignment); p[0] = p[size - 1] = 0; } for (uptr i = 0; i < kNumAlignedAllocs; i++) { a.Deallocate(&stats, allocated[i]); } } // Regression test for boundary condition in GetBlockBegin(). uptr page_size = GetPageSizeCached(); char *p = (char *)a.Allocate(&stats, page_size, 1); CHECK_EQ(p, a.GetBlockBegin(p)); CHECK_EQ(p, (char *)a.GetBlockBegin(p + page_size - 1)); CHECK_NE(p, (char *)a.GetBlockBegin(p + page_size)); a.Deallocate(&stats, p); } #endif template void TestCombinedAllocator() { typedef CombinedAllocator Allocator; Allocator *a = new Allocator; - a->Init(); + a->Init(/* may_return_null */ true); AllocatorCache cache; memset(&cache, 0, sizeof(cache)); a->InitCache(&cache); - bool allocator_may_return_null = common_flags()->allocator_may_return_null; - common_flags()->allocator_may_return_null = true; EXPECT_EQ(a->Allocate(&cache, -1, 1), (void*)0); EXPECT_EQ(a->Allocate(&cache, -1, 1024), (void*)0); EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1), (void*)0); EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1024), (void*)0); EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1023, 1024), (void*)0); - common_flags()->allocator_may_return_null = false; + // Set to false + a->SetMayReturnNull(false); EXPECT_DEATH(a->Allocate(&cache, -1, 1), "allocator is terminating the process"); - // Restore the original value. - common_flags()->allocator_may_return_null = allocator_may_return_null; const uptr kNumAllocs = 100000; const uptr kNumIter = 10; for (uptr iter = 0; iter < kNumIter; iter++) { std::vector allocated; for (uptr i = 0; i < kNumAllocs; i++) { uptr size = (i % (1 << 14)) + 1; if ((i % 1024) == 0) size = 1 << (10 + (i % 14)); void *x = a->Allocate(&cache, size, 1); uptr *meta = reinterpret_cast(a->GetMetaData(x)); CHECK_EQ(*meta, 0); *meta = size; allocated.push_back(x); } random_shuffle(allocated.begin(), allocated.end()); for (uptr i = 0; i < kNumAllocs; i++) { void *x = allocated[i]; uptr *meta = reinterpret_cast(a->GetMetaData(x)); CHECK_NE(*meta, 0); CHECK(a->PointerIsMine(x)); *meta = 0; a->Deallocate(&cache, x); } allocated.clear(); a->SwallowCache(&cache); } a->DestroyCache(&cache); a->TestOnlyUnmap(); } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, CombinedAllocator64) { TestCombinedAllocator, SizeClassAllocatorLocalCache > (); } TEST(SanitizerCommon, CombinedAllocator64Compact) { TestCombinedAllocator, SizeClassAllocatorLocalCache > (); } #endif #if !defined(_WIN32) // FIXME: This currently fails on Windows. TEST(SanitizerCommon, CombinedAllocator32Compact) { TestCombinedAllocator, SizeClassAllocatorLocalCache > (); } #endif template void TestSizeClassAllocatorLocalCache() { AllocatorCache cache; typedef typename AllocatorCache::Allocator Allocator; Allocator *a = new Allocator(); a->Init(); memset(&cache, 0, sizeof(cache)); cache.Init(0); const uptr kNumAllocs = 10000; const int kNumIter = 100; uptr saved_total = 0; for (int class_id = 1; class_id <= 5; class_id++) { for (int it = 0; it < kNumIter; it++) { void *allocated[kNumAllocs]; for (uptr i = 0; i < kNumAllocs; i++) { allocated[i] = cache.Allocate(a, class_id); } for (uptr i = 0; i < kNumAllocs; i++) { cache.Deallocate(a, class_id, allocated[i]); } cache.Drain(a); uptr total_allocated = a->TotalMemoryUsed(); if (it) CHECK_EQ(saved_total, total_allocated); saved_total = total_allocated; } } a->TestOnlyUnmap(); delete a; } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64LocalCache) { TestSizeClassAllocatorLocalCache< SizeClassAllocatorLocalCache >(); } TEST(SanitizerCommon, SizeClassAllocator64CompactLocalCache) { TestSizeClassAllocatorLocalCache< SizeClassAllocatorLocalCache >(); } #endif TEST(SanitizerCommon, SizeClassAllocator32CompactLocalCache) { TestSizeClassAllocatorLocalCache< SizeClassAllocatorLocalCache >(); } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 typedef SizeClassAllocatorLocalCache AllocatorCache; static AllocatorCache static_allocator_cache; void *AllocatorLeakTestWorker(void *arg) { typedef AllocatorCache::Allocator Allocator; Allocator *a = (Allocator*)(arg); static_allocator_cache.Allocate(a, 10); static_allocator_cache.Drain(a); return 0; } TEST(SanitizerCommon, AllocatorLeakTest) { typedef AllocatorCache::Allocator Allocator; Allocator a; a.Init(); uptr total_used_memory = 0; for (int i = 0; i < 100; i++) { pthread_t t; PTHREAD_CREATE(&t, 0, AllocatorLeakTestWorker, &a); PTHREAD_JOIN(t, 0); if (i == 0) total_used_memory = a.TotalMemoryUsed(); EXPECT_EQ(a.TotalMemoryUsed(), total_used_memory); } a.TestOnlyUnmap(); } // Struct which is allocated to pass info to new threads. The new thread frees // it. struct NewThreadParams { AllocatorCache *thread_cache; AllocatorCache::Allocator *allocator; uptr class_id; }; // Called in a new thread. Just frees its argument. static void *DeallocNewThreadWorker(void *arg) { NewThreadParams *params = reinterpret_cast(arg); params->thread_cache->Deallocate(params->allocator, params->class_id, params); return NULL; } // The allocator cache is supposed to be POD and zero initialized. We should be // able to call Deallocate on a zeroed cache, and it will self-initialize. TEST(Allocator, AllocatorCacheDeallocNewThread) { AllocatorCache::Allocator allocator; allocator.Init(); AllocatorCache main_cache; AllocatorCache child_cache; memset(&main_cache, 0, sizeof(main_cache)); memset(&child_cache, 0, sizeof(child_cache)); uptr class_id = DefaultSizeClassMap::ClassID(sizeof(NewThreadParams)); NewThreadParams *params = reinterpret_cast( main_cache.Allocate(&allocator, class_id)); params->thread_cache = &child_cache; params->allocator = &allocator; params->class_id = class_id; pthread_t t; PTHREAD_CREATE(&t, 0, DeallocNewThreadWorker, params); PTHREAD_JOIN(t, 0); } #endif TEST(Allocator, Basic) { char *p = (char*)InternalAlloc(10); EXPECT_NE(p, (char*)0); char *p2 = (char*)InternalAlloc(20); EXPECT_NE(p2, (char*)0); EXPECT_NE(p2, p); InternalFree(p); InternalFree(p2); } TEST(Allocator, Stress) { const int kCount = 1000; char *ptrs[kCount]; unsigned rnd = 42; for (int i = 0; i < kCount; i++) { uptr sz = my_rand_r(&rnd) % 1000; char *p = (char*)InternalAlloc(sz); EXPECT_NE(p, (char*)0); ptrs[i] = p; } for (int i = 0; i < kCount; i++) { InternalFree(ptrs[i]); } } TEST(Allocator, LargeAlloc) { void *p = InternalAlloc(10 << 20); InternalFree(p); } TEST(Allocator, ScopedBuffer) { const int kSize = 512; { InternalScopedBuffer int_buf(kSize); EXPECT_EQ(sizeof(int) * kSize, int_buf.size()); // NOLINT } InternalScopedBuffer char_buf(kSize); EXPECT_EQ(sizeof(char) * kSize, char_buf.size()); // NOLINT internal_memset(char_buf.data(), 'c', kSize); for (int i = 0; i < kSize; i++) { EXPECT_EQ('c', char_buf[i]); } } void IterationTestCallback(uptr chunk, void *arg) { reinterpret_cast *>(arg)->insert(chunk); } template void TestSizeClassAllocatorIteration() { Allocator *a = new Allocator; a->Init(); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000, 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000}; std::vector allocated; // Allocate a bunch of chunks. for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) { uptr size = sizes[s]; if (!a->CanAllocate(size, 1)) continue; // printf("s = %ld\n", size); uptr n_iter = std::max((uptr)6, 80000 / size); // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter); for (uptr j = 0; j < n_iter; j++) { uptr class_id0 = Allocator::SizeClassMapT::ClassID(size); void *x = cache.Allocate(a, class_id0); allocated.push_back(x); } } std::set reported_chunks; a->ForceLock(); a->ForEachChunk(IterationTestCallback, &reported_chunks); a->ForceUnlock(); for (uptr i = 0; i < allocated.size(); i++) { // Don't use EXPECT_NE. Reporting the first mismatch is enough. ASSERT_NE(reported_chunks.find(reinterpret_cast(allocated[i])), reported_chunks.end()); } a->TestOnlyUnmap(); delete a; } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64Iteration) { TestSizeClassAllocatorIteration(); } #endif TEST(SanitizerCommon, SizeClassAllocator32Iteration) { TestSizeClassAllocatorIteration(); } TEST(SanitizerCommon, LargeMmapAllocatorIteration) { LargeMmapAllocator<> a; - a.Init(); + a.Init(/* may_return_null */ false); AllocatorStats stats; stats.Init(); static const uptr kNumAllocs = 1000; char *allocated[kNumAllocs]; static const uptr size = 40; // Allocate some. for (uptr i = 0; i < kNumAllocs; i++) allocated[i] = (char *)a.Allocate(&stats, size, 1); std::set reported_chunks; a.ForceLock(); a.ForEachChunk(IterationTestCallback, &reported_chunks); a.ForceUnlock(); for (uptr i = 0; i < kNumAllocs; i++) { // Don't use EXPECT_NE. Reporting the first mismatch is enough. ASSERT_NE(reported_chunks.find(reinterpret_cast(allocated[i])), reported_chunks.end()); } for (uptr i = 0; i < kNumAllocs; i++) a.Deallocate(&stats, allocated[i]); } TEST(SanitizerCommon, LargeMmapAllocatorBlockBegin) { LargeMmapAllocator<> a; - a.Init(); + a.Init(/* may_return_null */ false); AllocatorStats stats; stats.Init(); static const uptr kNumAllocs = 1024; static const uptr kNumExpectedFalseLookups = 10000000; char *allocated[kNumAllocs]; static const uptr size = 4096; // Allocate some. for (uptr i = 0; i < kNumAllocs; i++) { allocated[i] = (char *)a.Allocate(&stats, size, 1); } a.ForceLock(); for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) { // if ((i & (i - 1)) == 0) fprintf(stderr, "[%zd]\n", i); char *p1 = allocated[i % kNumAllocs]; EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1)); EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 + size / 2)); EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 + size - 1)); EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 - 100)); } for (uptr i = 0; i < kNumExpectedFalseLookups; i++) { void *p = reinterpret_cast(i % 1024); EXPECT_EQ((void *)0, a.GetBlockBeginFastLocked(p)); p = reinterpret_cast(~0L - (i % 1024)); EXPECT_EQ((void *)0, a.GetBlockBeginFastLocked(p)); } a.ForceUnlock(); for (uptr i = 0; i < kNumAllocs; i++) a.Deallocate(&stats, allocated[i]); } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 // Regression test for out-of-memory condition in PopulateFreeList(). TEST(SanitizerCommon, SizeClassAllocator64PopulateFreeListOOM) { // In a world where regions are small and chunks are huge... typedef SizeClassMap<63, 128, 16> SpecialSizeClassMap; typedef SizeClassAllocator64 SpecialAllocator64; const uptr kRegionSize = kAllocatorSize / SpecialSizeClassMap::kNumClassesRounded; SpecialAllocator64 *a = new SpecialAllocator64; a->Init(); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); // ...one man is on a mission to overflow a region with a series of // successive allocations. const uptr kClassID = 107; const uptr kAllocationSize = DefaultSizeClassMap::Size(kClassID); ASSERT_LT(2 * kAllocationSize, kRegionSize); ASSERT_GT(3 * kAllocationSize, kRegionSize); cache.Allocate(a, kClassID); EXPECT_DEATH(cache.Allocate(a, kClassID) && cache.Allocate(a, kClassID), "The process has exhausted"); a->TestOnlyUnmap(); delete a; } #endif TEST(SanitizerCommon, TwoLevelByteMap) { const u64 kSize1 = 1 << 6, kSize2 = 1 << 12; const u64 n = kSize1 * kSize2; TwoLevelByteMap m; m.TestOnlyInit(); for (u64 i = 0; i < n; i += 7) { m.set(i, (i % 100) + 1); } for (u64 j = 0; j < n; j++) { if (j % 7) EXPECT_EQ(m[j], 0); else EXPECT_EQ(m[j], (j % 100) + 1); } m.TestOnlyUnmap(); } typedef TwoLevelByteMap<1 << 12, 1 << 13, TestMapUnmapCallback> TestByteMap; struct TestByteMapParam { TestByteMap *m; size_t shard; size_t num_shards; }; void *TwoLevelByteMapUserThread(void *param) { TestByteMapParam *p = (TestByteMapParam*)param; for (size_t i = p->shard; i < p->m->size(); i += p->num_shards) { size_t val = (i % 100) + 1; p->m->set(i, val); EXPECT_EQ((*p->m)[i], val); } return 0; } TEST(SanitizerCommon, ThreadedTwoLevelByteMap) { TestByteMap m; m.TestOnlyInit(); TestMapUnmapCallback::map_count = 0; TestMapUnmapCallback::unmap_count = 0; static const int kNumThreads = 4; pthread_t t[kNumThreads]; TestByteMapParam p[kNumThreads]; for (int i = 0; i < kNumThreads; i++) { p[i].m = &m; p[i].shard = i; p[i].num_shards = kNumThreads; PTHREAD_CREATE(&t[i], 0, TwoLevelByteMapUserThread, &p[i]); } for (int i = 0; i < kNumThreads; i++) { PTHREAD_JOIN(t[i], 0); } EXPECT_EQ((uptr)TestMapUnmapCallback::map_count, m.size1()); EXPECT_EQ((uptr)TestMapUnmapCallback::unmap_count, 0UL); m.TestOnlyUnmap(); EXPECT_EQ((uptr)TestMapUnmapCallback::map_count, m.size1()); EXPECT_EQ((uptr)TestMapUnmapCallback::unmap_count, m.size1()); } -#endif // #if TSAN_DEBUG==0 +#endif // #if !SANITIZER_DEBUG Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc (revision 279191) @@ -1,496 +1,496 @@ //===-- sanitizer_deadlock_detector_test.cc -------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of Sanitizer runtime. // Tests for sanitizer_deadlock_detector.h // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_deadlock_detector.h" #include "sanitizer_test_utils.h" #include "gtest/gtest.h" #include #include #include using namespace __sanitizer; using namespace std; typedef BasicBitVector BV1; typedef BasicBitVector<> BV2; typedef TwoLevelBitVector<> BV3; typedef TwoLevelBitVector<3, BasicBitVector > BV4; // Poor man's unique_ptr. template struct ScopedDD { ScopedDD() { dp = new DeadlockDetector; dp->clear(); dtls.clear(); } ~ScopedDD() { delete dp; } DeadlockDetector *dp; DeadlockDetectorTLS dtls; }; template void RunBasicTest() { uptr path[10]; ScopedDD sdd; DeadlockDetector &d = *sdd.dp; DeadlockDetectorTLS &dtls = sdd.dtls; set s; for (size_t i = 0; i < d.size() * 3; i++) { uptr node = d.newNode(0); EXPECT_TRUE(s.insert(node).second); } d.clear(); s.clear(); // Add size() nodes. for (size_t i = 0; i < d.size(); i++) { uptr node = d.newNode(0); EXPECT_TRUE(s.insert(node).second); } // Remove all nodes. for (set::iterator it = s.begin(); it != s.end(); ++it) d.removeNode(*it); // The nodes should be reused. for (size_t i = 0; i < d.size(); i++) { uptr node = d.newNode(0); EXPECT_FALSE(s.insert(node).second); } // Cycle: n1->n2->n1 { d.clear(); dtls.clear(); uptr n1 = d.newNode(1); uptr n2 = d.newNode(2); EXPECT_FALSE(d.onLock(&dtls, n1)); EXPECT_FALSE(d.onLock(&dtls, n2)); d.onUnlock(&dtls, n2); d.onUnlock(&dtls, n1); EXPECT_FALSE(d.onLock(&dtls, n2)); EXPECT_EQ(0U, d.findPathToLock(&dtls, n1, path, 1)); EXPECT_EQ(2U, d.findPathToLock(&dtls, n1, path, 10)); EXPECT_EQ(2U, d.findPathToLock(&dtls, n1, path, 2)); EXPECT_TRUE(d.onLock(&dtls, n1)); EXPECT_EQ(path[0], n1); EXPECT_EQ(path[1], n2); EXPECT_EQ(d.getData(n1), 1U); EXPECT_EQ(d.getData(n2), 2U); d.onUnlock(&dtls, n1); d.onUnlock(&dtls, n2); } // Cycle: n1->n2->n3->n1 { d.clear(); dtls.clear(); uptr n1 = d.newNode(1); uptr n2 = d.newNode(2); uptr n3 = d.newNode(3); EXPECT_FALSE(d.onLock(&dtls, n1)); EXPECT_FALSE(d.onLock(&dtls, n2)); d.onUnlock(&dtls, n2); d.onUnlock(&dtls, n1); EXPECT_FALSE(d.onLock(&dtls, n2)); EXPECT_FALSE(d.onLock(&dtls, n3)); d.onUnlock(&dtls, n3); d.onUnlock(&dtls, n2); EXPECT_FALSE(d.onLock(&dtls, n3)); EXPECT_EQ(0U, d.findPathToLock(&dtls, n1, path, 2)); EXPECT_EQ(3U, d.findPathToLock(&dtls, n1, path, 10)); EXPECT_TRUE(d.onLock(&dtls, n1)); EXPECT_EQ(path[0], n1); EXPECT_EQ(path[1], n2); EXPECT_EQ(path[2], n3); EXPECT_EQ(d.getData(n1), 1U); EXPECT_EQ(d.getData(n2), 2U); EXPECT_EQ(d.getData(n3), 3U); d.onUnlock(&dtls, n1); d.onUnlock(&dtls, n3); } } TEST(DeadlockDetector, BasicTest) { RunBasicTest(); RunBasicTest(); RunBasicTest(); RunBasicTest(); } template void RunRemoveNodeTest() { ScopedDD sdd; DeadlockDetector &d = *sdd.dp; DeadlockDetectorTLS &dtls = sdd.dtls; uptr l0 = d.newNode(0); uptr l1 = d.newNode(1); uptr l2 = d.newNode(2); uptr l3 = d.newNode(3); uptr l4 = d.newNode(4); uptr l5 = d.newNode(5); // l0=>l1=>l2 d.onLock(&dtls, l0); d.onLock(&dtls, l1); d.onLock(&dtls, l2); d.onUnlock(&dtls, l1); d.onUnlock(&dtls, l0); d.onUnlock(&dtls, l2); // l3=>l4=>l5 d.onLock(&dtls, l3); d.onLock(&dtls, l4); d.onLock(&dtls, l5); d.onUnlock(&dtls, l4); d.onUnlock(&dtls, l3); d.onUnlock(&dtls, l5); set locks; locks.insert(l0); locks.insert(l1); locks.insert(l2); locks.insert(l3); locks.insert(l4); locks.insert(l5); for (uptr i = 6; i < d.size(); i++) { uptr lt = d.newNode(i); locks.insert(lt); d.onLock(&dtls, lt); d.onUnlock(&dtls, lt); d.removeNode(lt); } EXPECT_EQ(locks.size(), d.size()); // l2=>l0 EXPECT_FALSE(d.onLock(&dtls, l2)); EXPECT_TRUE(d.onLock(&dtls, l0)); d.onUnlock(&dtls, l2); d.onUnlock(&dtls, l0); // l4=>l3 EXPECT_FALSE(d.onLock(&dtls, l4)); EXPECT_TRUE(d.onLock(&dtls, l3)); d.onUnlock(&dtls, l4); d.onUnlock(&dtls, l3); EXPECT_EQ(d.size(), d.testOnlyGetEpoch()); d.removeNode(l2); d.removeNode(l3); locks.clear(); // make sure no edges from or to l0,l1,l4,l5 left. for (uptr i = 4; i < d.size(); i++) { uptr lt = d.newNode(i); locks.insert(lt); uptr a, b; // l0 => lt? a = l0; b = lt; EXPECT_FALSE(d.onLock(&dtls, a)); EXPECT_FALSE(d.onLock(&dtls, b)); d.onUnlock(&dtls, a); d.onUnlock(&dtls, b); // l1 => lt? a = l1; b = lt; EXPECT_FALSE(d.onLock(&dtls, a)); EXPECT_FALSE(d.onLock(&dtls, b)); d.onUnlock(&dtls, a); d.onUnlock(&dtls, b); // lt => l4? a = lt; b = l4; EXPECT_FALSE(d.onLock(&dtls, a)); EXPECT_FALSE(d.onLock(&dtls, b)); d.onUnlock(&dtls, a); d.onUnlock(&dtls, b); // lt => l5? a = lt; b = l5; EXPECT_FALSE(d.onLock(&dtls, a)); EXPECT_FALSE(d.onLock(&dtls, b)); d.onUnlock(&dtls, a); d.onUnlock(&dtls, b); d.removeNode(lt); } // Still the same epoch. EXPECT_EQ(d.size(), d.testOnlyGetEpoch()); EXPECT_EQ(locks.size(), d.size() - 4); // l2 and l3 should have ben reused. EXPECT_EQ(locks.count(l2), 1U); EXPECT_EQ(locks.count(l3), 1U); } TEST(DeadlockDetector, RemoveNodeTest) { RunRemoveNodeTest(); RunRemoveNodeTest(); RunRemoveNodeTest(); RunRemoveNodeTest(); } template void RunMultipleEpochsTest() { ScopedDD sdd; DeadlockDetector &d = *sdd.dp; DeadlockDetectorTLS &dtls = sdd.dtls; set locks; for (uptr i = 0; i < d.size(); i++) { EXPECT_TRUE(locks.insert(d.newNode(i)).second); } EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); for (uptr i = 0; i < d.size(); i++) { EXPECT_TRUE(locks.insert(d.newNode(i)).second); EXPECT_EQ(d.testOnlyGetEpoch(), d.size() * 2); } locks.clear(); uptr l0 = d.newNode(0); uptr l1 = d.newNode(0); d.onLock(&dtls, l0); d.onLock(&dtls, l1); d.onUnlock(&dtls, l0); EXPECT_EQ(d.testOnlyGetEpoch(), 3 * d.size()); for (uptr i = 0; i < d.size(); i++) { EXPECT_TRUE(locks.insert(d.newNode(i)).second); } EXPECT_EQ(d.testOnlyGetEpoch(), 4 * d.size()); -#if TSAN_DEBUG == 0 +#if !SANITIZER_DEBUG // EXPECT_DEATH clones a thread with 4K stack, // which is overflown by tsan memory accesses functions in debug mode. // Can not handle the locks from the previous epoch. // The caller should update the lock id. EXPECT_DEATH(d.onLock(&dtls, l0), "CHECK failed.*current_epoch_"); #endif } TEST(DeadlockDetector, MultipleEpochsTest) { RunMultipleEpochsTest(); RunMultipleEpochsTest(); RunMultipleEpochsTest(); RunMultipleEpochsTest(); } template void RunCorrectEpochFlush() { ScopedDD sdd; DeadlockDetector &d = *sdd.dp; DeadlockDetectorTLS &dtls = sdd.dtls; vector locks1; for (uptr i = 0; i < d.size(); i++) locks1.push_back(d.newNode(i)); EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); d.onLock(&dtls, locks1[3]); d.onLock(&dtls, locks1[4]); d.onLock(&dtls, locks1[5]); // We have a new epoch, old locks in dtls will have to be forgotten. uptr l0 = d.newNode(0); EXPECT_EQ(d.testOnlyGetEpoch(), d.size() * 2); uptr l1 = d.newNode(0); EXPECT_EQ(d.testOnlyGetEpoch(), d.size() * 2); d.onLock(&dtls, l0); d.onLock(&dtls, l1); EXPECT_TRUE(d.testOnlyHasEdgeRaw(0, 1)); EXPECT_FALSE(d.testOnlyHasEdgeRaw(1, 0)); EXPECT_FALSE(d.testOnlyHasEdgeRaw(3, 0)); EXPECT_FALSE(d.testOnlyHasEdgeRaw(4, 0)); EXPECT_FALSE(d.testOnlyHasEdgeRaw(5, 0)); } TEST(DeadlockDetector, CorrectEpochFlush) { RunCorrectEpochFlush(); RunCorrectEpochFlush(); } template void RunTryLockTest() { ScopedDD sdd; DeadlockDetector &d = *sdd.dp; DeadlockDetectorTLS &dtls = sdd.dtls; uptr l0 = d.newNode(0); uptr l1 = d.newNode(0); uptr l2 = d.newNode(0); EXPECT_FALSE(d.onLock(&dtls, l0)); EXPECT_FALSE(d.onTryLock(&dtls, l1)); EXPECT_FALSE(d.onLock(&dtls, l2)); EXPECT_TRUE(d.isHeld(&dtls, l0)); EXPECT_TRUE(d.isHeld(&dtls, l1)); EXPECT_TRUE(d.isHeld(&dtls, l2)); EXPECT_FALSE(d.testOnlyHasEdge(l0, l1)); EXPECT_TRUE(d.testOnlyHasEdge(l1, l2)); d.onUnlock(&dtls, l0); d.onUnlock(&dtls, l1); d.onUnlock(&dtls, l2); } TEST(DeadlockDetector, TryLockTest) { RunTryLockTest(); RunTryLockTest(); } template void RunOnFirstLockTest() { ScopedDD sdd; DeadlockDetector &d = *sdd.dp; DeadlockDetectorTLS &dtls = sdd.dtls; uptr l0 = d.newNode(0); uptr l1 = d.newNode(0); EXPECT_FALSE(d.onFirstLock(&dtls, l0)); // dtls has old epoch. d.onLock(&dtls, l0); d.onUnlock(&dtls, l0); EXPECT_TRUE(d.onFirstLock(&dtls, l0)); // Ok, same ecpoch, first lock. EXPECT_FALSE(d.onFirstLock(&dtls, l1)); // Second lock. d.onLock(&dtls, l1); d.onUnlock(&dtls, l1); d.onUnlock(&dtls, l0); EXPECT_TRUE(d.onFirstLock(&dtls, l0)); // Ok d.onUnlock(&dtls, l0); vector locks1; for (uptr i = 0; i < d.size(); i++) locks1.push_back(d.newNode(i)); EXPECT_TRUE(d.onFirstLock(&dtls, l0)); // Epoch has changed, but not in dtls. uptr l3 = d.newNode(0); d.onLock(&dtls, l3); d.onUnlock(&dtls, l3); EXPECT_FALSE(d.onFirstLock(&dtls, l0)); // Epoch has changed in dtls. } TEST(DeadlockDetector, onFirstLockTest) { RunOnFirstLockTest(); } template void RunRecusriveLockTest() { ScopedDD sdd; DeadlockDetector &d = *sdd.dp; DeadlockDetectorTLS &dtls = sdd.dtls; uptr l0 = d.newNode(0); uptr l1 = d.newNode(0); uptr l2 = d.newNode(0); uptr l3 = d.newNode(0); EXPECT_FALSE(d.onLock(&dtls, l0)); EXPECT_FALSE(d.onLock(&dtls, l1)); EXPECT_FALSE(d.onLock(&dtls, l0)); // Recurisve. EXPECT_FALSE(d.onLock(&dtls, l2)); d.onUnlock(&dtls, l0); EXPECT_FALSE(d.onLock(&dtls, l3)); d.onUnlock(&dtls, l0); d.onUnlock(&dtls, l1); d.onUnlock(&dtls, l2); d.onUnlock(&dtls, l3); EXPECT_TRUE(d.testOnlyHasEdge(l0, l1)); EXPECT_TRUE(d.testOnlyHasEdge(l0, l2)); EXPECT_TRUE(d.testOnlyHasEdge(l0, l3)); } TEST(DeadlockDetector, RecusriveLockTest) { RunRecusriveLockTest(); } template void RunLockContextTest() { ScopedDD sdd; DeadlockDetector &d = *sdd.dp; DeadlockDetectorTLS &dtls = sdd.dtls; uptr l0 = d.newNode(0); uptr l1 = d.newNode(0); uptr l2 = d.newNode(0); uptr l3 = d.newNode(0); uptr l4 = d.newNode(0); EXPECT_FALSE(d.onLock(&dtls, l0, 10)); EXPECT_FALSE(d.onLock(&dtls, l1, 11)); EXPECT_FALSE(d.onLock(&dtls, l2, 12)); EXPECT_FALSE(d.onLock(&dtls, l3, 13)); EXPECT_EQ(10U, d.findLockContext(&dtls, l0)); EXPECT_EQ(11U, d.findLockContext(&dtls, l1)); EXPECT_EQ(12U, d.findLockContext(&dtls, l2)); EXPECT_EQ(13U, d.findLockContext(&dtls, l3)); d.onUnlock(&dtls, l0); EXPECT_EQ(0U, d.findLockContext(&dtls, l0)); EXPECT_EQ(11U, d.findLockContext(&dtls, l1)); EXPECT_EQ(12U, d.findLockContext(&dtls, l2)); EXPECT_EQ(13U, d.findLockContext(&dtls, l3)); d.onUnlock(&dtls, l2); EXPECT_EQ(0U, d.findLockContext(&dtls, l0)); EXPECT_EQ(11U, d.findLockContext(&dtls, l1)); EXPECT_EQ(0U, d.findLockContext(&dtls, l2)); EXPECT_EQ(13U, d.findLockContext(&dtls, l3)); EXPECT_FALSE(d.onLock(&dtls, l4, 14)); EXPECT_EQ(14U, d.findLockContext(&dtls, l4)); } TEST(DeadlockDetector, LockContextTest) { RunLockContextTest(); } template void RunRemoveEdgesTest() { ScopedDD sdd; DeadlockDetector &d = *sdd.dp; DeadlockDetectorTLS &dtls = sdd.dtls; vector node(BV::kSize); u32 stk_from = 0, stk_to = 0; int unique_tid = 0; for (size_t i = 0; i < BV::kSize; i++) node[i] = d.newNode(0); for (size_t i = 0; i < BV::kSize; i++) EXPECT_FALSE(d.onLock(&dtls, node[i], i + 1)); for (size_t i = 0; i < BV::kSize; i++) { for (uptr j = i + 1; j < BV::kSize; j++) { EXPECT_TRUE( d.findEdge(node[i], node[j], &stk_from, &stk_to, &unique_tid)); EXPECT_EQ(stk_from, i + 1); EXPECT_EQ(stk_to, j + 1); } } EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); // Remove and re-create half of the nodes. for (uptr i = 1; i < BV::kSize; i += 2) d.removeNode(node[i]); for (uptr i = 1; i < BV::kSize; i += 2) node[i] = d.newNode(0); EXPECT_EQ(d.testOnlyGetEpoch(), d.size()); // The edges from or to the removed nodes should be gone. for (size_t i = 0; i < BV::kSize; i++) { for (uptr j = i + 1; j < BV::kSize; j++) { if ((i % 2) || (j % 2)) EXPECT_FALSE( d.findEdge(node[i], node[j], &stk_from, &stk_to, &unique_tid)); else EXPECT_TRUE( d.findEdge(node[i], node[j], &stk_from, &stk_to, &unique_tid)); } } } TEST(DeadlockDetector, RemoveEdgesTest) { RunRemoveEdgesTest(); } Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_flags_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_flags_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_flags_test.cc (revision 279191) @@ -1,86 +1,142 @@ //===-- sanitizer_flags_test.cc -------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer/AddressSanitizer runtime. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" #include "gtest/gtest.h" #include namespace __sanitizer { static const char kFlagName[] = "flag_name"; +static const char kFlagDesc[] = "flag description"; template static void TestFlag(T start_value, const char *env, T final_value) { T flag = start_value; - ParseFlag(env, &flag, kFlagName, "flag description"); + + FlagParser parser; + RegisterFlag(&parser, kFlagName, kFlagDesc, &flag); + + parser.ParseString(env); + EXPECT_EQ(final_value, flag); } -static void TestStrFlag(const char *start_value, const char *env, - const char *final_value) { +template <> +void TestFlag(const char *start_value, const char *env, + const char *final_value) { const char *flag = start_value; - ParseFlag(env, &flag, kFlagName, "flag description"); + + FlagParser parser; + RegisterFlag(&parser, kFlagName, kFlagDesc, &flag); + + parser.ParseString(env); + EXPECT_EQ(0, internal_strcmp(final_value, flag)); } TEST(SanitizerCommon, BooleanFlags) { - TestFlag(true, "--flag_name", true); - TestFlag(false, "flag_name", false); - TestFlag(false, "--flag_name=1", true); - TestFlag(true, "asdas flag_name=0 asdas", false); - TestFlag(true, " --flag_name=0 ", false); + TestFlag(false, "flag_name=1", true); TestFlag(false, "flag_name=yes", true); TestFlag(false, "flag_name=true", true); + TestFlag(true, "flag_name=0", false); TestFlag(true, "flag_name=no", false); TestFlag(true, "flag_name=false", false); } TEST(SanitizerCommon, IntFlags) { TestFlag(-11, 0, -11); - TestFlag(-11, "flag_name", -11); - TestFlag(-11, "--flag_name=", 0); - TestFlag(-11, "--flag_name=42", 42); - TestFlag(-11, "--flag_name=-42", -42); + TestFlag(-11, "flag_name=0", 0); + TestFlag(-11, "flag_name=42", 42); + TestFlag(-11, "flag_name=-42", -42); + + // Unrecognized flags are ignored. + TestFlag(-11, "--flag_name=42", -11); + TestFlag(-11, "zzzzzzz=42", -11); + + EXPECT_DEATH(TestFlag(-11, "flag_name", 0), "expected '='"); + EXPECT_DEATH(TestFlag(-11, "flag_name=42U", 0), + "Invalid value for int option"); } TEST(SanitizerCommon, StrFlags) { - TestStrFlag("zzz", 0, "zzz"); - TestStrFlag("zzz", "flag_name", "zzz"); - TestStrFlag("zzz", "--flag_name=", ""); - TestStrFlag("", "--flag_name=abc", "abc"); - TestStrFlag("", "--flag_name='abc zxc'", "abc zxc"); - TestStrFlag("", "--flag_name='abc zxcc'", "abc zxcc"); - TestStrFlag("", "--flag_name=\"abc qwe\" asd", "abc qwe"); - TestStrFlag("", "other_flag_name=zzz", ""); + TestFlag("zzz", 0, "zzz"); + TestFlag("zzz", "flag_name=", ""); + TestFlag("zzz", "flag_name=abc", "abc"); + TestFlag("", "flag_name=abc", "abc"); + TestFlag("", "flag_name='abc zxc'", "abc zxc"); + // TestStrFlag("", "flag_name=\"abc qwe\" asd", "abc qwe"); } static void TestTwoFlags(const char *env, bool expected_flag1, - const char *expected_flag2) { + const char *expected_flag2, + const char *name1 = "flag1", + const char *name2 = "flag2") { bool flag1 = !expected_flag1; const char *flag2 = ""; - ParseFlag(env, &flag1, "flag1", "flag1 description"); - ParseFlag(env, &flag2, "flag2", "flag2 description"); + + FlagParser parser; + RegisterFlag(&parser, name1, kFlagDesc, &flag1); + RegisterFlag(&parser, name2, kFlagDesc, &flag2); + + parser.ParseString(env); + EXPECT_EQ(expected_flag1, flag1); EXPECT_EQ(0, internal_strcmp(flag2, expected_flag2)); } TEST(SanitizerCommon, MultipleFlags) { TestTwoFlags("flag1=1 flag2='zzz'", true, "zzz"); TestTwoFlags("flag2='qxx' flag1=0", false, "qxx"); TestTwoFlags("flag1=false:flag2='zzz'", false, "zzz"); TestTwoFlags("flag2=qxx:flag1=yes", true, "qxx"); + TestTwoFlags("flag2=qxx\nflag1=yes", true, "qxx"); + TestTwoFlags("flag2=qxx\r\nflag1=yes", true, "qxx"); + TestTwoFlags("flag2=qxx\tflag1=yes", true, "qxx"); +} + +TEST(SanitizerCommon, CommonSuffixFlags) { + TestTwoFlags("flag=1 other_flag='zzz'", true, "zzz", "flag", "other_flag"); + TestTwoFlags("other_flag='zzz' flag=1", true, "zzz", "flag", "other_flag"); + TestTwoFlags("other_flag=' flag=0 ' flag=1", true, " flag=0 ", "flag", + "other_flag"); + TestTwoFlags("flag=1 other_flag=' flag=0 '", true, " flag=0 ", "flag", + "other_flag"); +} + +TEST(SanitizerCommon, CommonFlags) { + CommonFlags cf; + FlagParser parser; + RegisterCommonFlags(&parser, &cf); + + cf.SetDefaults(); + EXPECT_TRUE(cf.symbolize); + EXPECT_STREQ(".", cf.coverage_dir); + + cf.symbolize = false; + cf.coverage = true; + cf.coverage_direct = true; + cf.log_path = "path/one"; + + parser.ParseString("symbolize=1:coverage_direct=false log_path='path/two'"); + EXPECT_TRUE(cf.symbolize); + EXPECT_TRUE(cf.coverage); + EXPECT_FALSE(cf.coverage_direct); + EXPECT_STREQ("path/two", cf.log_path); } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_libc_test.cc (revision 279191) @@ -1,127 +1,161 @@ //===-- sanitizer_libc_test.cc --------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Tests for sanitizer_libc.h. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_platform.h" #include "gtest/gtest.h" #if SANITIZER_LINUX || SANITIZER_MAC # define SANITIZER_TEST_HAS_STAT_H 1 # include #else # define SANITIZER_TEST_HAS_STAT_H 0 #endif // A regression test for internal_memmove() implementation. TEST(SanitizerCommon, InternalMemmoveRegression) { char src[] = "Hello World"; char *dest = src + 6; __sanitizer::internal_memmove(dest, src, 5); EXPECT_EQ(dest[0], src[0]); EXPECT_EQ(dest[4], src[4]); } TEST(SanitizerCommon, mem_is_zero) { size_t size = 128; char *x = new char[size]; memset(x, 0, size); for (size_t pos = 0; pos < size; pos++) { x[pos] = 1; for (size_t beg = 0; beg < size; beg++) { for (size_t end = beg; end < size; end++) { // fprintf(stderr, "pos %zd beg %zd end %zd \n", pos, beg, end); if (beg <= pos && pos < end) EXPECT_FALSE(__sanitizer::mem_is_zero(x + beg, end - beg)); else EXPECT_TRUE(__sanitizer::mem_is_zero(x + beg, end - beg)); } } x[pos] = 0; } delete [] x; } struct stat_and_more { struct stat st; unsigned char z; }; +static void temp_file_name(char *buf, size_t bufsize, const char *prefix) { + const char *tmpdir = "/tmp"; +#if SANITIZER_ANDROID + // I don't know a way to query temp directory location on Android without + // going through Java interfaces. The code below is not ideal, but should + // work. May require "adb root", but it is needed for almost any use of ASan + // on Android already. + tmpdir = GetEnv("EXTERNAL_STORAGE"); +#endif + u32 uid = GetUid(); + internal_snprintf(buf, bufsize, "%s/%s%d", tmpdir, prefix, uid); +} + // FIXME: File manipulations are not yet supported on Windows #if !defined(_WIN32) TEST(SanitizerCommon, FileOps) { const char *str1 = "qwerty"; uptr len1 = internal_strlen(str1); const char *str2 = "zxcv"; uptr len2 = internal_strlen(str2); - u32 uid = GetUid(); - char temp_filename[128]; -#if SANITIZER_ANDROID - // I don't know a way to query temp directory location on Android without - // going through Java interfaces. The code below is not ideal, but should - // work. May require "adb root", but it is needed for almost any use of ASan - // on Android already. - internal_snprintf(temp_filename, sizeof(temp_filename), - "%s/sanitizer_common.tmp.%d", - GetEnv("EXTERNAL_STORAGE"), uid); -#else - internal_snprintf(temp_filename, sizeof(temp_filename), - "/tmp/sanitizer_common.tmp.%d", uid); -#endif - uptr openrv = OpenFile(temp_filename, true); + char tmpfile[128]; + temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.fileops.tmp."); + uptr openrv = OpenFile(tmpfile, true); EXPECT_FALSE(internal_iserror(openrv)); fd_t fd = openrv; EXPECT_EQ(len1, internal_write(fd, str1, len1)); EXPECT_EQ(len2, internal_write(fd, str2, len2)); internal_close(fd); - openrv = OpenFile(temp_filename, false); + openrv = OpenFile(tmpfile, false); EXPECT_FALSE(internal_iserror(openrv)); fd = openrv; uptr fsize = internal_filesize(fd); EXPECT_EQ(len1 + len2, fsize); #if SANITIZER_TEST_HAS_STAT_H struct stat st1, st2, st3; - EXPECT_EQ(0u, internal_stat(temp_filename, &st1)); - EXPECT_EQ(0u, internal_lstat(temp_filename, &st2)); + EXPECT_EQ(0u, internal_stat(tmpfile, &st1)); + EXPECT_EQ(0u, internal_lstat(tmpfile, &st2)); EXPECT_EQ(0u, internal_fstat(fd, &st3)); EXPECT_EQ(fsize, (uptr)st3.st_size); // Verify that internal_fstat does not write beyond the end of the supplied // buffer. struct stat_and_more sam; memset(&sam, 0xAB, sizeof(sam)); EXPECT_EQ(0u, internal_fstat(fd, &sam.st)); EXPECT_EQ(0xAB, sam.z); EXPECT_NE(0xAB, sam.st.st_size); EXPECT_NE(0, sam.st.st_size); #endif char buf[64] = {}; EXPECT_EQ(len1, internal_read(fd, buf, len1)); EXPECT_EQ(0, internal_memcmp(buf, str1, len1)); EXPECT_EQ((char)0, buf[len1 + 1]); internal_memset(buf, 0, len1); EXPECT_EQ(len2, internal_read(fd, buf, len2)); EXPECT_EQ(0, internal_memcmp(buf, str2, len2)); internal_close(fd); + internal_unlink(tmpfile); } #endif TEST(SanitizerCommon, InternalStrFunctions) { const char *haystack = "haystack"; EXPECT_EQ(haystack + 2, internal_strchr(haystack, 'y')); EXPECT_EQ(haystack + 2, internal_strchrnul(haystack, 'y')); EXPECT_EQ(0, internal_strchr(haystack, 'z')); EXPECT_EQ(haystack + 8, internal_strchrnul(haystack, 'z')); } + +// FIXME: File manipulations are not yet supported on Windows +#if !defined(_WIN32) && !SANITIZER_MAC +TEST(SanitizerCommon, InternalMmapWithOffset) { + char tmpfile[128]; + temp_file_name(tmpfile, sizeof(tmpfile), + "sanitizer_common.internalmmapwithoffset.tmp."); + uptr res = OpenFile(tmpfile, true); + ASSERT_FALSE(internal_iserror(res)); + fd_t fd = res; + + uptr page_size = GetPageSizeCached(); + res = internal_ftruncate(fd, page_size * 2); + ASSERT_FALSE(internal_iserror(res)); + + res = internal_lseek(fd, page_size, SEEK_SET); + ASSERT_FALSE(internal_iserror(res)); + + res = internal_write(fd, "AB", 2); + ASSERT_FALSE(internal_iserror(res)); + + char *p = (char *)MapWritableFileToMemory(nullptr, page_size, fd, page_size); + ASSERT_NE(nullptr, p); + + ASSERT_EQ('A', p[0]); + ASSERT_EQ('B', p[1]); + + internal_close(fd); + internal_munmap(p, page_size); + internal_unlink(tmpfile); +} +#endif Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_printf_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_printf_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_printf_test.cc (revision 279191) @@ -1,156 +1,153 @@ //===-- sanitizer_printf_test.cc ------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Tests for sanitizer_printf.cc // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "gtest/gtest.h" #include #include namespace __sanitizer { TEST(Printf, Basic) { char buf[1024]; uptr len = internal_snprintf(buf, sizeof(buf), "a%db%zdc%ue%zuf%xh%zxq%pe%sr", (int)-1, (long)-2, // NOLINT (unsigned)-4, (unsigned long)5, // NOLINT (unsigned)10, (unsigned long)11, // NOLINT (void*)0x123, "_string_"); EXPECT_EQ(len, strlen(buf)); - void *ptr; - if (sizeof(ptr) == 4) { - EXPECT_STREQ("a-1b-2c4294967292e5fahbq" - "0x00000123e_string_r", buf); - } else { - EXPECT_STREQ("a-1b-2c4294967292e5fahbq" - "0x000000000123e_string_r", buf); - } + + std::string expectedString = "a-1b-2c4294967292e5fahbq0x"; + expectedString += std::string(SANITIZER_POINTER_FORMAT_LENGTH - 3, '0'); + expectedString += "123e_string_r"; + EXPECT_STREQ(expectedString.c_str(), buf); } TEST(Printf, OverflowStr) { char buf[] = "123456789"; uptr len = internal_snprintf(buf, 4, "%s", "abcdef"); // NOLINT EXPECT_EQ(len, (uptr)6); EXPECT_STREQ("abc", buf); EXPECT_EQ(buf[3], 0); EXPECT_EQ(buf[4], '5'); EXPECT_EQ(buf[5], '6'); EXPECT_EQ(buf[6], '7'); EXPECT_EQ(buf[7], '8'); EXPECT_EQ(buf[8], '9'); EXPECT_EQ(buf[9], 0); } TEST(Printf, OverflowInt) { char buf[] = "123456789"; internal_snprintf(buf, 4, "%d", -123456789); // NOLINT EXPECT_STREQ("-12", buf); EXPECT_EQ(buf[3], 0); EXPECT_EQ(buf[4], '5'); EXPECT_EQ(buf[5], '6'); EXPECT_EQ(buf[6], '7'); EXPECT_EQ(buf[7], '8'); EXPECT_EQ(buf[8], '9'); EXPECT_EQ(buf[9], 0); } TEST(Printf, OverflowUint) { char buf[] = "123456789"; uptr val; if (sizeof(val) == 4) { val = (uptr)0x12345678; } else { val = (uptr)0x123456789ULL; } internal_snprintf(buf, 4, "a%zx", val); // NOLINT EXPECT_STREQ("a12", buf); EXPECT_EQ(buf[3], 0); EXPECT_EQ(buf[4], '5'); EXPECT_EQ(buf[5], '6'); EXPECT_EQ(buf[6], '7'); EXPECT_EQ(buf[7], '8'); EXPECT_EQ(buf[8], '9'); EXPECT_EQ(buf[9], 0); } TEST(Printf, OverflowPtr) { char buf[] = "123456789"; void *p; if (sizeof(p) == 4) { p = (void*)0x1234567; } else { p = (void*)0x123456789ULL; } internal_snprintf(buf, 4, "%p", p); // NOLINT EXPECT_STREQ("0x0", buf); EXPECT_EQ(buf[3], 0); EXPECT_EQ(buf[4], '5'); EXPECT_EQ(buf[5], '6'); EXPECT_EQ(buf[6], '7'); EXPECT_EQ(buf[7], '8'); EXPECT_EQ(buf[8], '9'); EXPECT_EQ(buf[9], 0); } #if defined(_WIN32) // Oh well, MSVS headers don't define snprintf. # define snprintf _snprintf #endif template static void TestAgainstLibc(const char *fmt, T arg1, T arg2) { char buf[1024]; uptr len = internal_snprintf(buf, sizeof(buf), fmt, arg1, arg2); char buf2[1024]; snprintf(buf2, sizeof(buf2), fmt, arg1, arg2); EXPECT_EQ(len, strlen(buf)); EXPECT_STREQ(buf2, buf); } TEST(Printf, MinMax) { TestAgainstLibc("%d-%d", INT_MIN, INT_MAX); // NOLINT TestAgainstLibc("%u-%u", 0, UINT_MAX); // NOLINT TestAgainstLibc("%x-%x", 0, UINT_MAX); // NOLINT #if !defined(_WIN32) // %z* format doesn't seem to be supported by MSVS. TestAgainstLibc("%zd-%zd", LONG_MIN, LONG_MAX); // NOLINT TestAgainstLibc("%zu-%zu", 0, ULONG_MAX); // NOLINT TestAgainstLibc("%zx-%zx", 0, ULONG_MAX); // NOLINT #endif } TEST(Printf, Padding) { TestAgainstLibc("%3d - %3d", 1, 0); TestAgainstLibc("%3d - %3d", -1, 123); TestAgainstLibc("%3d - %3d", -1, -123); TestAgainstLibc("%3d - %3d", 12, 1234); TestAgainstLibc("%3d - %3d", -12, -1234); TestAgainstLibc("%03d - %03d", 1, 0); TestAgainstLibc("%03d - %03d", -1, 123); TestAgainstLibc("%03d - %03d", -1, -123); TestAgainstLibc("%03d - %03d", 12, 1234); TestAgainstLibc("%03d - %03d", -12, -1234); } TEST(Printf, Precision) { char buf[1024]; uptr len = internal_snprintf(buf, sizeof(buf), "%.*s", 3, "12345"); EXPECT_EQ(3U, len); EXPECT_STREQ("123", buf); len = internal_snprintf(buf, sizeof(buf), "%.*s", 6, "12345"); EXPECT_EQ(5U, len); EXPECT_STREQ("12345", buf); } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc (revision 279191) @@ -1,57 +1,58 @@ //===-- sanitizer_procmaps_test.cc ----------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer/AddressSanitizer runtime. // //===----------------------------------------------------------------------===// #if !defined(_WIN32) // There are no /proc/maps on Windows. #include "sanitizer_common/sanitizer_procmaps.h" #include "gtest/gtest.h" #include static void noop() {} extern const char *argv0; namespace __sanitizer { #if SANITIZER_LINUX && !SANITIZER_ANDROID TEST(MemoryMappingLayout, CodeRange) { uptr start, end; bool res = GetCodeRangeForFile("[vdso]", &start, &end); EXPECT_EQ(res, true); EXPECT_GT(start, 0U); EXPECT_LT(start, end); } #endif TEST(MemoryMappingLayout, DumpListOfModules) { const char *last_slash = strrchr(argv0, '/'); const char *binary_name = last_slash ? last_slash + 1 : argv0; MemoryMappingLayout memory_mapping(false); const uptr kMaxModules = 100; LoadedModule *modules = (LoadedModule *)malloc(kMaxModules * sizeof(LoadedModule)); uptr n_modules = memory_mapping.DumpListOfModules(modules, kMaxModules, 0); EXPECT_GT(n_modules, 0U); bool found = false; for (uptr i = 0; i < n_modules; ++i) { if (modules[i].containsAddress((uptr)&noop)) { // Verify that the module name is sane. if (strstr(modules[i].full_name(), binary_name) != 0) found = true; } + modules[i].clear(); } EXPECT_TRUE(found); free(modules); } } // namespace __sanitizer #endif // !defined(_WIN32) Index: projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h (revision 279191) @@ -1,121 +1,127 @@ //===-- sanitizer_test_utils.h ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of *Sanitizer runtime. // Common unit tests utilities. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_TEST_UTILS_H #define SANITIZER_TEST_UTILS_H #if defined(_WIN32) // should always be the first include on Windows. # include // MSVS headers define max/min as macros, so std::max/min gets crazy. # undef max # undef min #endif #if !defined(SANITIZER_EXTERNAL_TEST_CONFIG) # define INCLUDED_FROM_SANITIZER_TEST_UTILS_H # include "sanitizer_test_config.h" # undef INCLUDED_FROM_SANITIZER_TEST_UTILS_H #endif #include #if defined(_MSC_VER) # define NOINLINE __declspec(noinline) #else // defined(_MSC_VER) # define NOINLINE __attribute__((noinline)) #endif // defined(_MSC_VER) #if !defined(_MSC_VER) || defined(__clang__) # define UNUSED __attribute__((unused)) # define USED __attribute__((used)) #else # define UNUSED # define USED #endif #if !defined(__has_feature) #define __has_feature(x) 0 #endif #ifndef ATTRIBUTE_NO_SANITIZE_ADDRESS # if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) # define ATTRIBUTE_NO_SANITIZE_ADDRESS \ __attribute__((no_sanitize_address)) # else # define ATTRIBUTE_NO_SANITIZE_ADDRESS # endif #endif // ATTRIBUTE_NO_SANITIZE_ADDRESS #if __LP64__ || defined(_WIN64) # define SANITIZER_WORDSIZE 64 #else # define SANITIZER_WORDSIZE 32 #endif // Make the compiler thinks that something is going on there. inline void break_optimization(void *arg) { #if !defined(_WIN32) || defined(__clang__) __asm__ __volatile__("" : : "r" (arg) : "memory"); #endif } // This function returns its parameter but in such a way that compiler // can not prove it. template NOINLINE static T Ident(T t) { T ret = t; break_optimization(&ret); return ret; } // Simple stand-alone pseudorandom number generator. // Current algorithm is ANSI C linear congruential PRNG. static inline uint32_t my_rand_r(uint32_t* state) { return (*state = *state * 1103515245 + 12345) >> 16; } static uint32_t global_seed = 0; static inline uint32_t my_rand() { return my_rand_r(&global_seed); } // Set availability of platform-specific functions. #if !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) # define SANITIZER_TEST_HAS_POSIX_MEMALIGN 1 #else # define SANITIZER_TEST_HAS_POSIX_MEMALIGN 0 #endif #if !defined(__APPLE__) && !defined(__FreeBSD__) && \ !defined(__ANDROID__) && !defined(_WIN32) # define SANITIZER_TEST_HAS_MEMALIGN 1 # define SANITIZER_TEST_HAS_PVALLOC 1 # define SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE 1 #else # define SANITIZER_TEST_HAS_MEMALIGN 0 # define SANITIZER_TEST_HAS_PVALLOC 0 # define SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE 0 #endif #if !defined(__APPLE__) # define SANITIZER_TEST_HAS_STRNLEN 1 #else # define SANITIZER_TEST_HAS_STRNLEN 0 #endif +#if defined(__FreeBSD__) +# define SANITIZER_TEST_HAS_PRINTF_L 1 +#else +# define SANITIZER_TEST_HAS_PRINTF_L 0 +#endif + #endif // SANITIZER_TEST_UTILS_H Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/Makefile.old =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/Makefile.old (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/Makefile.old (nonexistent) @@ -1,106 +0,0 @@ -DEBUG=0 -LDFLAGS=-ldl -lrt -lpthread -pie -CXXFLAGS = -std=c++11 -fPIE -fno-rtti -g -Wall -Werror \ - -DGTEST_HAS_RTTI=0 -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG) -CLANG=clang -FILECHECK=FileCheck -# Silence warnings that Clang produces for gtest code. -# Use -Wno-attributes so that gcc doesn't complain about unknown warning types. -CXXFLAGS += -Wno-attributes -ifeq ($(DEBUG), 0) - CXXFLAGS += -O3 -endif -ifeq ($(CXX), $(CLANG)++) - CXXFLAGS+= -Wno-unused-private-field -Wno-static-in-inline -Wgnu -endif - -LIBTSAN=rtl/libtsan.a -GTEST_ROOT=third_party/googletest -GTEST_INCLUDE=-I$(GTEST_ROOT)/include -GTEST_BUILD_DIR=$(GTEST_ROOT)/build -GTEST_LIB_NAME=gtest-all.o -GTEST_LIB=$(GTEST_BUILD_DIR)/$(GTEST_LIB_NAME) - -SANITIZER_TESTS_PATH=../sanitizer_common/tests -SANITIZER_COMMON_TESTS_SRC=$(wildcard $(SANITIZER_TESTS_PATH)/*_test.cc) -SANITIZER_COMMON_EXCLUDED_TESTS=$(SANITIZER_TESTS_PATH)/sanitizer_nolibc_test.cc -SANITIZER_COMMON_GOOD_TESTS=$(filter-out $(SANITIZER_COMMON_EXCLUDED_TESTS), $(SANITIZER_COMMON_TESTS_SRC)) -SANITIZER_COMMON_TESTS_OBJ=$(patsubst %.cc,%.o,$(SANITIZER_COMMON_GOOD_TESTS)) -RTL_TEST_SRC=$(wildcard tests/rtl/*.cc) -RTL_TEST_OBJ=$(patsubst %.cc,%.o,$(RTL_TEST_SRC)) -UNIT_TEST_SRC=$(wildcard tests/unit/*_test.cc) -UNIT_TEST_OBJ=$(patsubst %.cc,%.o,$(UNIT_TEST_SRC)) -UNIT_TEST_HDR=$(wildcard rtl/*.h) $(wildcard ../sanitizer_common/*.h) -LIT_TESTS_PATH=../../test/tsan - -INCLUDES=-Irtl -I.. -I../../include $(GTEST_INCLUDE) - -all: libtsan test - -help: - @ echo "A little help is always welcome!" - @ echo "The most useful targets are:" - @ echo " make install_deps # Install third-party dependencies required for building" - @ echo " make presubmit # Run it every time before committing" - @ echo - @ echo "For more info, see http://code.google.com/p/thread-sanitizer/wiki/Development" - -$(LIBTSAN): libtsan - -libtsan: - $(MAKE) -C rtl -f Makefile.old DEBUG=$(DEBUG) - -%.o: %.cc $(UNIT_TEST_HDR) $(LIBTSAN) - $(CXX) $(CXXFLAGS) $(CFLAGS) $(INCLUDES) -o $@ -c $< - -tsan_test: $(UNIT_TEST_OBJ) $(RTL_TEST_OBJ) \ - $(SANITIZER_COMMON_TESTS_OBJ) $(LIBTSAN) $(GTEST_LIB) - $(CXX) -Wl,--whole-archive $^ -Wl,--no-whole-archive -o $@ $(LDFLAGS) - -test: libtsan tsan_test - -run: all - (ulimit -s 8192; ./tsan_test) - CC=$(CLANG) CXX=$(CLANG)++ FILECHECK=$(FILECHECK) $(LIT_TESTS_PATH)/test_output.sh - -presubmit: - ../sanitizer_common/scripts/check_lint.sh - # Debug build with clang. - $(MAKE) -f Makefile.old clean - $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=$(CLANG) CXX=$(CLANG)++ - # Release build with clang. - $(MAKE) -f Makefile.old clean - $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=$(CLANG) CXX=$(CLANG)++ - ./check_memcpy.sh - # Debug build with gcc - $(MAKE) -f Makefile.old clean - $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=gcc CXX=g++ - # Release build with gcc - $(MAKE) -f Makefile.old clean - $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=gcc CXX=g++ - ./check_memcpy.sh - ./check_analyze.sh - # Sanity check for Go runtime - (cd go && ./buildgo.sh) - # Check cmake build - ./check_cmake.sh - @ echo PRESUBMIT PASSED - -install_deps: - rm -rf third_party - mkdir third_party - (cd third_party && \ - svn co -r613 http://googletest.googlecode.com/svn/trunk googletest \ - ) - -$(GTEST_LIB): - mkdir -p $(GTEST_BUILD_DIR) && \ - cd $(GTEST_BUILD_DIR) && \ - $(MAKE) -f ../make/Makefile CXXFLAGS="$(CXXFLAGS)" CFLAGS="$(CFLAGS)" CC=$(CC) CXX=$(CXX) $(GTEST_LIB_NAME) - -clean: - rm -f asm_*.s libtsan.nm libtsan.objdump */*.o tsan_test - rm -rf $(GTEST_BUILD_DIR) - $(MAKE) clean -C rtl -f Makefile.old - rm -f go/*.s - rm -rf build Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/check_analyze.sh =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/check_analyze.sh (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/check_analyze.sh (revision 279191) @@ -1,49 +1,44 @@ #!/bin/bash set -u RES=$(./analyze_libtsan.sh) PrintRes() { printf "%s\n" "$RES" } PrintRes -wmops="write1 \ - write2 \ - write4 \ - write8" -rmops="read1 \ - read2 \ - read4 \ - read8" -func="func_entry \ - func_exit" - check() { res=$(PrintRes | egrep "$1 .* $2 $3; ") if [ "$res" == "" ]; then echo FAILED $1 must contain $2 $3 exit 1 fi } -for f in $wmops; do - check $f rsp 3 - check $f push 1 - check $f pop 5 +for f in write1; do + check $f rsp 1 + check $f push 2 + check $f pop 2 done -for f in $rmops; do - check $f rsp 3 - check $f push 1 - check $f pop 4 +for f in write2 write4 write8; do + check $f rsp 1 + check $f push 3 + check $f pop 3 done -for f in $func; do +for f in read1 read2 read4 read8; do + check $f rsp 1 + check $f push 5 + check $f pop 5 +done + +for f in func_entry func_exit; do check $f rsp 0 check $f push 0 check $f pop 0 check $f call 1 # TraceSwitch() done echo LGTM Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/dd/dd_rtl.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/dd/dd_rtl.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/dd/dd_rtl.cc (revision 279191) @@ -1,151 +1,159 @@ //===-- dd_rtl.cc ---------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "dd_rtl.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_stackdepot.h" namespace __dsan { static Context *ctx; static u32 CurrentStackTrace(Thread *thr, uptr skip) { BufferedStackTrace stack; thr->ignore_interceptors = true; stack.Unwind(1000, 0, 0, 0, 0, 0, false); thr->ignore_interceptors = false; if (stack.size <= skip) return 0; return StackDepotPut(StackTrace(stack.trace + skip, stack.size - skip)); } static void PrintStackTrace(Thread *thr, u32 stk) { StackTrace stack = StackDepotGet(stk); thr->ignore_interceptors = true; stack.Print(); thr->ignore_interceptors = false; } static void ReportDeadlock(Thread *thr, DDReport *rep) { if (rep == 0) return; BlockingMutexLock lock(&ctx->report_mutex); Printf("==============================\n"); Printf("WARNING: lock-order-inversion (potential deadlock)\n"); for (int i = 0; i < rep->n; i++) { Printf("Thread %d locks mutex %llu while holding mutex %llu:\n", rep->loop[i].thr_ctx, rep->loop[i].mtx_ctx1, rep->loop[i].mtx_ctx0); PrintStackTrace(thr, rep->loop[i].stk[1]); if (rep->loop[i].stk[0]) { Printf("Mutex %llu was acquired here:\n", rep->loop[i].mtx_ctx0); PrintStackTrace(thr, rep->loop[i].stk[0]); } } Printf("==============================\n"); } Callback::Callback(Thread *thr) : thr(thr) { lt = thr->dd_lt; pt = thr->dd_pt; } u32 Callback::Unwind() { return CurrentStackTrace(thr, 3); } void InitializeFlags(Flags *f, const char *env) { internal_memset(f, 0, sizeof(*f)); // Default values. f->second_deadlock_stack = false; - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - // Override some common flags defaults. - cf->allow_addr2line = true; + SetCommonFlagsDefaults(); + { + // Override some common flags defaults. + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.allow_addr2line = true; + OverrideCommonFlags(cf); + } // Override from command line. - ParseFlag(env, &f->second_deadlock_stack, "second_deadlock_stack", ""); - ParseCommonFlagsFromString(cf, env); + FlagParser parser; + RegisterFlag(&parser, "second_deadlock_stack", "", &f->second_deadlock_stack); + RegisterCommonFlags(&parser); + parser.ParseString(env); + SetVerbosity(common_flags()->verbosity); } void Initialize() { static u64 ctx_mem[sizeof(Context) / sizeof(u64) + 1]; ctx = new(ctx_mem) Context(); InitializeInterceptors(); InitializeFlags(flags(), GetEnv("DSAN_OPTIONS")); ctx->dd = DDetector::Create(flags()); } void ThreadInit(Thread *thr) { static atomic_uintptr_t id_gen; uptr id = atomic_fetch_add(&id_gen, 1, memory_order_relaxed); thr->dd_pt = ctx->dd->CreatePhysicalThread(); thr->dd_lt = ctx->dd->CreateLogicalThread(id); } void ThreadDestroy(Thread *thr) { ctx->dd->DestroyPhysicalThread(thr->dd_pt); ctx->dd->DestroyLogicalThread(thr->dd_lt); } void MutexBeforeLock(Thread *thr, uptr m, bool writelock) { if (thr->ignore_interceptors) return; Callback cb(thr); { MutexHashMap::Handle h(&ctx->mutex_map, m); if (h.created()) ctx->dd->MutexInit(&cb, &h->dd); ctx->dd->MutexBeforeLock(&cb, &h->dd, writelock); } ReportDeadlock(thr, ctx->dd->GetReport(&cb)); } void MutexAfterLock(Thread *thr, uptr m, bool writelock, bool trylock) { if (thr->ignore_interceptors) return; Callback cb(thr); { MutexHashMap::Handle h(&ctx->mutex_map, m); if (h.created()) ctx->dd->MutexInit(&cb, &h->dd); ctx->dd->MutexAfterLock(&cb, &h->dd, writelock, trylock); } ReportDeadlock(thr, ctx->dd->GetReport(&cb)); } void MutexBeforeUnlock(Thread *thr, uptr m, bool writelock) { if (thr->ignore_interceptors) return; Callback cb(thr); { MutexHashMap::Handle h(&ctx->mutex_map, m); ctx->dd->MutexBeforeUnlock(&cb, &h->dd, writelock); } ReportDeadlock(thr, ctx->dd->GetReport(&cb)); } void MutexDestroy(Thread *thr, uptr m) { if (thr->ignore_interceptors) return; Callback cb(thr); MutexHashMap::Handle h(&ctx->mutex_map, m, true); if (!h.exists()) return; ctx->dd->MutexDestroy(&cb, &h->dd); } } // namespace __dsan Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/go/build.bat =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/go/build.bat (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/go/build.bat (revision 279191) @@ -1,4 +1,4 @@ type tsan_go.cc ..\rtl\tsan_interface_atomic.cc ..\rtl\tsan_clock.cc ..\rtl\tsan_flags.cc ..\rtl\tsan_md5.cc ..\rtl\tsan_mutex.cc ..\rtl\tsan_report.cc ..\rtl\tsan_rtl.cc ..\rtl\tsan_rtl_mutex.cc ..\rtl\tsan_rtl_report.cc ..\rtl\tsan_rtl_thread.cc ..\rtl\tsan_stat.cc ..\rtl\tsan_suppressions.cc ..\rtl\tsan_sync.cc ..\rtl\tsan_stack_trace.cc ..\..\sanitizer_common\sanitizer_allocator.cc ..\..\sanitizer_common\sanitizer_common.cc ..\..\sanitizer_common\sanitizer_flags.cc ..\..\sanitizer_common\sanitizer_stacktrace.cc ..\..\sanitizer_common\sanitizer_libc.cc ..\..\sanitizer_common\sanitizer_printf.cc ..\..\sanitizer_common\sanitizer_suppressions.cc ..\..\sanitizer_common\sanitizer_thread_registry.cc ..\rtl\tsan_platform_windows.cc ..\..\sanitizer_common\sanitizer_win.cc ..\..\sanitizer_common\sanitizer_deadlock_detector1.cc ..\..\sanitizer_common\sanitizer_stackdepot.cc ..\..\sanitizer_common\sanitizer_persistent_allocator.cc > gotsan.cc -gcc -c -o race_windows_amd64.syso gotsan.cc -I..\rtl -I..\.. -I..\..\sanitizer_common -I..\..\..\include -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 -Wno-error=attributes -Wno-attributes -Wno-format -DTSAN_DEBUG=0 -O3 -fomit-frame-pointer +gcc -c -o race_windows_amd64.syso gotsan.cc -I..\rtl -I..\.. -I..\..\sanitizer_common -I..\..\..\include -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -Wno-error=attributes -Wno-attributes -Wno-format -DSANITIZER_DEBUG=0 -O3 -fomit-frame-pointer Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/go/buildgo.sh =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/go/buildgo.sh (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/go/buildgo.sh (revision 279191) @@ -1,103 +1,132 @@ +#!/bin/sh + set -e SRCS=" tsan_go.cc ../rtl/tsan_clock.cc ../rtl/tsan_flags.cc ../rtl/tsan_interface_atomic.cc ../rtl/tsan_md5.cc ../rtl/tsan_mutex.cc ../rtl/tsan_report.cc ../rtl/tsan_rtl.cc ../rtl/tsan_rtl_mutex.cc ../rtl/tsan_rtl_report.cc ../rtl/tsan_rtl_thread.cc ../rtl/tsan_stack_trace.cc ../rtl/tsan_stat.cc ../rtl/tsan_suppressions.cc ../rtl/tsan_sync.cc ../../sanitizer_common/sanitizer_allocator.cc ../../sanitizer_common/sanitizer_common.cc ../../sanitizer_common/sanitizer_deadlock_detector2.cc + ../../sanitizer_common/sanitizer_flag_parser.cc ../../sanitizer_common/sanitizer_flags.cc ../../sanitizer_common/sanitizer_libc.cc ../../sanitizer_common/sanitizer_persistent_allocator.cc ../../sanitizer_common/sanitizer_printf.cc ../../sanitizer_common/sanitizer_suppressions.cc ../../sanitizer_common/sanitizer_thread_registry.cc ../../sanitizer_common/sanitizer_stackdepot.cc ../../sanitizer_common/sanitizer_stacktrace.cc ../../sanitizer_common/sanitizer_symbolizer.cc " if [ "`uname -a | grep Linux`" != "" ]; then SUFFIX="linux_amd64" OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Wno-unused-const-variable -Werror -Wno-unknown-warning-option" OSLDFLAGS="-lpthread -fPIC -fpie" - SRCS+=" + SRCS=" + $SRCS ../rtl/tsan_platform_linux.cc ../../sanitizer_common/sanitizer_posix.cc ../../sanitizer_common/sanitizer_posix_libcdep.cc ../../sanitizer_common/sanitizer_procmaps_common.cc ../../sanitizer_common/sanitizer_procmaps_linux.cc ../../sanitizer_common/sanitizer_linux.cc + ../../sanitizer_common/sanitizer_linux_libcdep.cc ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc " elif [ "`uname -a | grep FreeBSD`" != "" ]; then SUFFIX="freebsd_amd64" OSCFLAGS="-fno-strict-aliasing -fPIC -Werror" OSLDFLAGS="-lpthread -fPIC -fpie" - SRCS+=" + SRCS=" + $SRCS ../rtl/tsan_platform_linux.cc ../../sanitizer_common/sanitizer_posix.cc ../../sanitizer_common/sanitizer_posix_libcdep.cc ../../sanitizer_common/sanitizer_procmaps_common.cc ../../sanitizer_common/sanitizer_procmaps_freebsd.cc ../../sanitizer_common/sanitizer_linux.cc + ../../sanitizer_common/sanitizer_linux_libcdep.cc ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc " elif [ "`uname -a | grep Darwin`" != "" ]; then SUFFIX="darwin_amd64" OSCFLAGS="-fPIC -Wno-unused-const-variable -Wno-unknown-warning-option" OSLDFLAGS="-lpthread -fPIC -fpie" - SRCS+=" + SRCS=" + $SRCS ../rtl/tsan_platform_mac.cc ../../sanitizer_common/sanitizer_mac.cc ../../sanitizer_common/sanitizer_posix.cc ../../sanitizer_common/sanitizer_posix_libcdep.cc ../../sanitizer_common/sanitizer_procmaps_mac.cc " elif [ "`uname -a | grep MINGW`" != "" ]; then SUFFIX="windows_amd64" OSCFLAGS="-Wno-error=attributes -Wno-attributes -Wno-unused-const-variable -Wno-unknown-warning-option" OSLDFLAGS="" - SRCS+=" + SRCS=" + $SRCS ../rtl/tsan_platform_windows.cc ../../sanitizer_common/sanitizer_win.cc " else echo Unknown platform exit 1 fi -SRCS+=$ADD_SRCS +CC=${CC:-gcc} +IN_TMPDIR=${IN_TMPDIR:-0} +SILENT=${SILENT:-0} -rm -f gotsan.cc +if [ $IN_TMPDIR != "0" ]; then + DIR=$(mktemp -qd /tmp/gotsan.XXXXXXXXXX) + cleanup() { + rm -rf $DIR + } + trap cleanup EXIT +else + DIR=. +fi + +SRCS="$SRCS $ADD_SRCS" + +rm -f $DIR/gotsan.cc for F in $SRCS; do - cat $F >> gotsan.cc + cat $F >> $DIR/gotsan.cc done -FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -std=c++11 -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2 $OSCFLAGS" -if [ "$DEBUG" == "" ]; then - FLAGS+=" -DTSAN_DEBUG=0 -O3 -msse3 -fomit-frame-pointer" +FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -std=c++11 -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2 $OSCFLAGS" +if [ "$DEBUG" = "" ]; then + FLAGS="$FLAGS -DSANITIZER_DEBUG=0 -O3 -msse3 -fomit-frame-pointer" else - FLAGS+=" -DTSAN_DEBUG=1 -g" + FLAGS="$FLAGS -DSANITIZER_DEBUG=1 -g" fi -CC=${CC:-gcc} +if [ "$SILENT" != "1" ]; then + echo $CC gotsan.cc -c -o $DIR/race_$SUFFIX.syso $FLAGS $CFLAGS +fi +$CC $DIR/gotsan.cc -c -o $DIR/race_$SUFFIX.syso $FLAGS $CFLAGS -echo $CC gotsan.cc -c -o race_$SUFFIX.syso $FLAGS $CFLAGS -$CC gotsan.cc -c -o race_$SUFFIX.syso $FLAGS $CFLAGS +$CC test.c $DIR/race_$SUFFIX.syso -m64 -o $DIR/test $OSLDFLAGS -$CC test.c race_$SUFFIX.syso -m64 -o test $OSLDFLAGS -GORACE="exitcode=0 atexit_sleep_ms=0" ./test +export GORACE="exitcode=0 atexit_sleep_ms=0" +if [ "$SILENT" != "1" ]; then + $DIR/test +else + $DIR/test 2>/dev/null +fi Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/go/tsan_go.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/go/tsan_go.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/go/tsan_go.cc (revision 279191) @@ -1,218 +1,211 @@ //===-- tsan_go.cc --------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // ThreadSanitizer runtime for Go language. // //===----------------------------------------------------------------------===// #include "tsan_rtl.h" #include "tsan_symbolize.h" #include "sanitizer_common/sanitizer_common.h" #include namespace __tsan { void InitializeInterceptors() { } void InitializeDynamicAnnotations() { } bool IsExpectedReport(uptr addr, uptr size) { return false; } -void *internal_start_thread(void(*func)(void*), void *arg) { - return 0; -} - -void internal_join_thread(void *th) { -} - ReportLocation *SymbolizeData(uptr addr) { return 0; } void *internal_alloc(MBlockType typ, uptr sz) { return InternalAlloc(sz); } void internal_free(void *p) { InternalFree(p); } struct SymbolizeContext { uptr pc; char *func; char *file; uptr line; uptr off; uptr res; }; // Callback into Go. static void (*symbolize_cb)(SymbolizeContext *ctx); SymbolizedStack *SymbolizeCode(uptr addr) { SymbolizedStack *s = SymbolizedStack::New(addr); SymbolizeContext ctx; internal_memset(&ctx, 0, sizeof(ctx)); ctx.pc = addr; symbolize_cb(&ctx); if (ctx.res) { AddressInfo &info = s->info; info.module_offset = ctx.off; info.function = internal_strdup(ctx.func ? ctx.func : "??"); info.file = internal_strdup(ctx.file ? ctx.file : "-"); info.line = ctx.line; info.column = 0; } return s; } extern "C" { static ThreadState *main_thr; static bool inited; static ThreadState *AllocGoroutine() { ThreadState *thr = (ThreadState*)internal_alloc(MBlockThreadContex, sizeof(ThreadState)); internal_memset(thr, 0, sizeof(*thr)); return thr; } void __tsan_init(ThreadState **thrp, void (*cb)(SymbolizeContext *cb)) { symbolize_cb = cb; ThreadState *thr = AllocGoroutine(); main_thr = *thrp = thr; Initialize(thr); inited = true; } void __tsan_fini() { // FIXME: Not necessary thread 0. ThreadState *thr = main_thr; int res = Finalize(thr); exit(res); } void __tsan_map_shadow(uptr addr, uptr size) { MapShadow(addr, size); } void __tsan_read(ThreadState *thr, void *addr, void *pc) { MemoryRead(thr, (uptr)pc, (uptr)addr, kSizeLog1); } void __tsan_read_pc(ThreadState *thr, void *addr, uptr callpc, uptr pc) { if (callpc != 0) FuncEntry(thr, callpc); MemoryRead(thr, (uptr)pc, (uptr)addr, kSizeLog1); if (callpc != 0) FuncExit(thr); } void __tsan_write(ThreadState *thr, void *addr, void *pc) { MemoryWrite(thr, (uptr)pc, (uptr)addr, kSizeLog1); } void __tsan_write_pc(ThreadState *thr, void *addr, uptr callpc, uptr pc) { if (callpc != 0) FuncEntry(thr, callpc); MemoryWrite(thr, (uptr)pc, (uptr)addr, kSizeLog1); if (callpc != 0) FuncExit(thr); } void __tsan_read_range(ThreadState *thr, void *addr, uptr size, uptr pc) { MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, false); } void __tsan_write_range(ThreadState *thr, void *addr, uptr size, uptr pc) { MemoryAccessRange(thr, (uptr)pc, (uptr)addr, size, true); } void __tsan_func_enter(ThreadState *thr, void *pc) { FuncEntry(thr, (uptr)pc); } void __tsan_func_exit(ThreadState *thr) { FuncExit(thr); } void __tsan_malloc(void *p, uptr sz) { if (!inited) return; MemoryResetRange(0, 0, (uptr)p, sz); } void __tsan_go_start(ThreadState *parent, ThreadState **pthr, void *pc) { ThreadState *thr = AllocGoroutine(); *pthr = thr; int goid = ThreadCreate(parent, (uptr)pc, 0, true); ThreadStart(thr, goid, 0); } void __tsan_go_end(ThreadState *thr) { ThreadFinish(thr); internal_free(thr); } void __tsan_acquire(ThreadState *thr, void *addr) { Acquire(thr, 0, (uptr)addr); } void __tsan_release(ThreadState *thr, void *addr) { ReleaseStore(thr, 0, (uptr)addr); } void __tsan_release_merge(ThreadState *thr, void *addr) { Release(thr, 0, (uptr)addr); } void __tsan_finalizer_goroutine(ThreadState *thr) { AcquireGlobal(thr, 0); } void __tsan_mutex_before_lock(ThreadState *thr, uptr addr, uptr write) { } void __tsan_mutex_after_lock(ThreadState *thr, uptr addr, uptr write) { if (write) MutexLock(thr, 0, addr); else MutexReadLock(thr, 0, addr); } void __tsan_mutex_before_unlock(ThreadState *thr, uptr addr, uptr write) { if (write) MutexUnlock(thr, 0, addr); else MutexReadUnlock(thr, 0, addr); } void __tsan_go_ignore_sync_begin(ThreadState *thr) { ThreadIgnoreSyncBegin(thr, 0); } void __tsan_go_ignore_sync_end(ThreadState *thr) { ThreadIgnoreSyncEnd(thr, 0); } } // extern "C" } // namespace __tsan namespace __sanitizer { void SymbolizerPrepareForSandboxing() { // Nothing to do here for Go. } } // namespace __sanitizer Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/Makefile.old =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/Makefile.old (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/Makefile.old (nonexistent) @@ -1,62 +0,0 @@ -CXXFLAGS = -std=c++11 -fPIE -g -Wall -Werror -fno-builtin -msse3 -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG) -CLANG=clang -ifeq ($(DEBUG), 0) - CXXFLAGS += -O3 -endif - -# For interception. FIXME: move interception one level higher. -INTERCEPTION=../../interception -COMMON=../../sanitizer_common -INCLUDES= -I../.. -I../../../include -EXTRA_CXXFLAGS=-fno-exceptions -fno-rtti -NO_SYSROOT=--sysroot=. -CXXFLAGS+=$(EXTRA_CXXFLAGS) -CXXFLAGS+=$(CFLAGS) -ifeq ($(DEBUG), 0) - CXXFLAGS+=-fomit-frame-pointer -ifeq ($(CXX), g++) - CXXFLAGS+=-Wframe-larger-than=512 -endif # CXX=g++ -endif # DEBUG=0 - -ifeq ($(CXX), $(CLANG)++) - # Global constructors are banned. - CXXFLAGS+=-Wglobal-constructors -endif - - - -all: libtsan.a - -LIBTSAN_HEADERS=$(wildcard *.h) \ - $(wildcard $(INTERCEPTION)/*.h) \ - $(wildcard $(COMMON)/*.h) -LIBTSAN_SRC=$(wildcard *.cc) -LIBTSAN_ASM_SRC=$(wildcard *.S) -INTERCEPTION_SRC=$(wildcard $(INTERCEPTION)/*.cc) -COMMON_SRC=$(wildcard $(COMMON)/*.cc) - -LIBTSAN_OBJ=$(patsubst %.cc,%.o,$(LIBTSAN_SRC)) \ - $(patsubst %.S,%.o,$(LIBTSAN_ASM_SRC)) \ - $(patsubst $(INTERCEPTION)/%.cc,%.o,$(INTERCEPTION_SRC)) \ - $(patsubst $(COMMON)/%.cc,%.o,$(COMMON_SRC)) - -%_linux.o: %_linux.cc Makefile.old $(LIBTSAN_HEADERS) - $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -%.o: %.cc Makefile.old $(LIBTSAN_HEADERS) - $(CXX) $(CXXFLAGS) $(INCLUDES) $(NO_SYSROOT) -c $< -%.o: $(INTERCEPTION)/%.cc Makefile.old $(LIBTSAN_HEADERS) - $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ -%.o: $(COMMON)/%.cc Makefile.old $(LIBTSAN_HEADERS) - $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ -%.o: %.S - $(CXX) $(INCLUDES) -o $@ -c $< - -libtsan.a: $(LIBTSAN_OBJ) - ar ru $@ $(LIBTSAN_OBJ) - -libtsan_dummy.a: tsan_dummy_rtl.o - ar ru $@ $< - -clean: - rm -f *.o *.a Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_clock.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_clock.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_clock.cc (revision 279191) @@ -1,429 +1,429 @@ //===-- tsan_clock.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "tsan_clock.h" #include "tsan_rtl.h" #include "sanitizer_common/sanitizer_placement_new.h" // SyncClock and ThreadClock implement vector clocks for sync variables // (mutexes, atomic variables, file descriptors, etc) and threads, respectively. // ThreadClock contains fixed-size vector clock for maximum number of threads. // SyncClock contains growable vector clock for currently necessary number of // threads. // Together they implement very simple model of operations, namely: // // void ThreadClock::acquire(const SyncClock *src) { // for (int i = 0; i < kMaxThreads; i++) // clock[i] = max(clock[i], src->clock[i]); // } // // void ThreadClock::release(SyncClock *dst) const { // for (int i = 0; i < kMaxThreads; i++) // dst->clock[i] = max(dst->clock[i], clock[i]); // } // // void ThreadClock::ReleaseStore(SyncClock *dst) const { // for (int i = 0; i < kMaxThreads; i++) // dst->clock[i] = clock[i]; // } // // void ThreadClock::acq_rel(SyncClock *dst) { // acquire(dst); // release(dst); // } // // Conformance to this model is extensively verified in tsan_clock_test.cc. // However, the implementation is significantly more complex. The complexity // allows to implement important classes of use cases in O(1) instead of O(N). // // The use cases are: // 1. Singleton/once atomic that has a single release-store operation followed // by zillions of acquire-loads (the acquire-load is O(1)). // 2. Thread-local mutex (both lock and unlock can be O(1)). // 3. Leaf mutex (unlock is O(1)). // 4. A mutex shared by 2 threads (both lock and unlock can be O(1)). // 5. An atomic with a single writer (writes can be O(1)). // The implementation dynamically adopts to workload. So if an atomic is in // read-only phase, these reads will be O(1); if it later switches to read/write // phase, the implementation will correctly handle that by switching to O(N). // // Thread-safety note: all const operations on SyncClock's are conducted under // a shared lock; all non-const operations on SyncClock's are conducted under // an exclusive lock; ThreadClock's are private to respective threads and so // do not need any protection. // // Description of ThreadClock state: // clk_ - fixed size vector clock. // nclk_ - effective size of the vector clock (the rest is zeros). // tid_ - index of the thread associated with he clock ("current thread"). // last_acquire_ - current thread time when it acquired something from // other threads. // // Description of SyncClock state: // clk_ - variable size vector clock, low kClkBits hold timestamp, // the remaining bits hold "acquired" flag (the actual value is thread's // reused counter); // if acquried == thr->reused_, then the respective thread has already // acquired this clock (except possibly dirty_tids_). // dirty_tids_ - holds up to two indeces in the vector clock that other threads // need to acquire regardless of "acquired" flag value; // release_store_tid_ - denotes that the clock state is a result of // release-store operation by the thread with release_store_tid_ index. // release_store_reused_ - reuse count of release_store_tid_. // We don't have ThreadState in these methods, so this is an ugly hack that // works only in C++. #ifndef SANITIZER_GO # define CPP_STAT_INC(typ) StatInc(cur_thread(), typ) #else # define CPP_STAT_INC(typ) (void)0 #endif namespace __tsan { const unsigned kInvalidTid = (unsigned)-1; ThreadClock::ThreadClock(unsigned tid, unsigned reused) : tid_(tid) , reused_(reused + 1) { // 0 has special meaning CHECK_LT(tid, kMaxTidInClock); CHECK_EQ(reused_, ((u64)reused_ << kClkBits) >> kClkBits); nclk_ = tid_ + 1; last_acquire_ = 0; internal_memset(clk_, 0, sizeof(clk_)); clk_[tid_].reused = reused_; } void ThreadClock::acquire(ClockCache *c, const SyncClock *src) { - DCHECK(nclk_ <= kMaxTid); - DCHECK(src->size_ <= kMaxTid); + DCHECK_LE(nclk_, kMaxTid); + DCHECK_LE(src->size_, kMaxTid); CPP_STAT_INC(StatClockAcquire); // Check if it's empty -> no need to do anything. const uptr nclk = src->size_; if (nclk == 0) { CPP_STAT_INC(StatClockAcquireEmpty); return; } // Check if we've already acquired src after the last release operation on src bool acquired = false; if (nclk > tid_) { CPP_STAT_INC(StatClockAcquireLarge); if (src->elem(tid_).reused == reused_) { CPP_STAT_INC(StatClockAcquireRepeat); for (unsigned i = 0; i < kDirtyTids; i++) { unsigned tid = src->dirty_tids_[i]; if (tid != kInvalidTid) { u64 epoch = src->elem(tid).epoch; if (clk_[tid].epoch < epoch) { clk_[tid].epoch = epoch; acquired = true; } } } if (acquired) { CPP_STAT_INC(StatClockAcquiredSomething); last_acquire_ = clk_[tid_].epoch; } return; } } // O(N) acquire. CPP_STAT_INC(StatClockAcquireFull); nclk_ = max(nclk_, nclk); for (uptr i = 0; i < nclk; i++) { u64 epoch = src->elem(i).epoch; if (clk_[i].epoch < epoch) { clk_[i].epoch = epoch; acquired = true; } } // Remember that this thread has acquired this clock. if (nclk > tid_) src->elem(tid_).reused = reused_; if (acquired) { CPP_STAT_INC(StatClockAcquiredSomething); last_acquire_ = clk_[tid_].epoch; } } void ThreadClock::release(ClockCache *c, SyncClock *dst) const { DCHECK_LE(nclk_, kMaxTid); DCHECK_LE(dst->size_, kMaxTid); if (dst->size_ == 0) { // ReleaseStore will correctly set release_store_tid_, // which can be important for future operations. ReleaseStore(c, dst); return; } CPP_STAT_INC(StatClockRelease); // Check if we need to resize dst. if (dst->size_ < nclk_) dst->Resize(c, nclk_); // Check if we had not acquired anything from other threads // since the last release on dst. If so, we need to update // only dst->elem(tid_). if (dst->elem(tid_).epoch > last_acquire_) { UpdateCurrentThread(dst); if (dst->release_store_tid_ != tid_ || dst->release_store_reused_ != reused_) dst->release_store_tid_ = kInvalidTid; return; } // O(N) release. CPP_STAT_INC(StatClockReleaseFull); // First, remember whether we've acquired dst. bool acquired = IsAlreadyAcquired(dst); if (acquired) CPP_STAT_INC(StatClockReleaseAcquired); // Update dst->clk_. for (uptr i = 0; i < nclk_; i++) { ClockElem &ce = dst->elem(i); ce.epoch = max(ce.epoch, clk_[i].epoch); ce.reused = 0; } // Clear 'acquired' flag in the remaining elements. if (nclk_ < dst->size_) CPP_STAT_INC(StatClockReleaseClearTail); for (uptr i = nclk_; i < dst->size_; i++) dst->elem(i).reused = 0; for (unsigned i = 0; i < kDirtyTids; i++) dst->dirty_tids_[i] = kInvalidTid; dst->release_store_tid_ = kInvalidTid; dst->release_store_reused_ = 0; // If we've acquired dst, remember this fact, // so that we don't need to acquire it on next acquire. if (acquired) dst->elem(tid_).reused = reused_; } void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) const { - DCHECK(nclk_ <= kMaxTid); - DCHECK(dst->size_ <= kMaxTid); + DCHECK_LE(nclk_, kMaxTid); + DCHECK_LE(dst->size_, kMaxTid); CPP_STAT_INC(StatClockStore); // Check if we need to resize dst. if (dst->size_ < nclk_) dst->Resize(c, nclk_); if (dst->release_store_tid_ == tid_ && dst->release_store_reused_ == reused_ && dst->elem(tid_).epoch > last_acquire_) { CPP_STAT_INC(StatClockStoreFast); UpdateCurrentThread(dst); return; } // O(N) release-store. CPP_STAT_INC(StatClockStoreFull); for (uptr i = 0; i < nclk_; i++) { ClockElem &ce = dst->elem(i); ce.epoch = clk_[i].epoch; ce.reused = 0; } // Clear the tail of dst->clk_. if (nclk_ < dst->size_) { for (uptr i = nclk_; i < dst->size_; i++) { ClockElem &ce = dst->elem(i); ce.epoch = 0; ce.reused = 0; } CPP_STAT_INC(StatClockStoreTail); } for (unsigned i = 0; i < kDirtyTids; i++) dst->dirty_tids_[i] = kInvalidTid; dst->release_store_tid_ = tid_; dst->release_store_reused_ = reused_; // Rememeber that we don't need to acquire it in future. dst->elem(tid_).reused = reused_; } void ThreadClock::acq_rel(ClockCache *c, SyncClock *dst) { CPP_STAT_INC(StatClockAcquireRelease); acquire(c, dst); ReleaseStore(c, dst); } // Updates only single element related to the current thread in dst->clk_. void ThreadClock::UpdateCurrentThread(SyncClock *dst) const { // Update the threads time, but preserve 'acquired' flag. dst->elem(tid_).epoch = clk_[tid_].epoch; for (unsigned i = 0; i < kDirtyTids; i++) { if (dst->dirty_tids_[i] == tid_) { CPP_STAT_INC(StatClockReleaseFast1); return; } if (dst->dirty_tids_[i] == kInvalidTid) { CPP_STAT_INC(StatClockReleaseFast2); dst->dirty_tids_[i] = tid_; return; } } // Reset all 'acquired' flags, O(N). CPP_STAT_INC(StatClockReleaseSlow); for (uptr i = 0; i < dst->size_; i++) dst->elem(i).reused = 0; for (unsigned i = 0; i < kDirtyTids; i++) dst->dirty_tids_[i] = kInvalidTid; } // Checks whether the current threads has already acquired src. bool ThreadClock::IsAlreadyAcquired(const SyncClock *src) const { if (src->elem(tid_).reused != reused_) return false; for (unsigned i = 0; i < kDirtyTids; i++) { unsigned tid = src->dirty_tids_[i]; if (tid != kInvalidTid) { if (clk_[tid].epoch < src->elem(tid).epoch) return false; } } return true; } void SyncClock::Resize(ClockCache *c, uptr nclk) { CPP_STAT_INC(StatClockReleaseResize); if (RoundUpTo(nclk, ClockBlock::kClockCount) <= RoundUpTo(size_, ClockBlock::kClockCount)) { // Growing within the same block. // Memory is already allocated, just increase the size. size_ = nclk; return; } if (nclk <= ClockBlock::kClockCount) { // Grow from 0 to one-level table. CHECK_EQ(size_, 0); CHECK_EQ(tab_, 0); CHECK_EQ(tab_idx_, 0); size_ = nclk; tab_idx_ = ctx->clock_alloc.Alloc(c); tab_ = ctx->clock_alloc.Map(tab_idx_); internal_memset(tab_, 0, sizeof(*tab_)); return; } // Growing two-level table. if (size_ == 0) { // Allocate first level table. tab_idx_ = ctx->clock_alloc.Alloc(c); tab_ = ctx->clock_alloc.Map(tab_idx_); internal_memset(tab_, 0, sizeof(*tab_)); } else if (size_ <= ClockBlock::kClockCount) { // Transform one-level table to two-level table. u32 old = tab_idx_; tab_idx_ = ctx->clock_alloc.Alloc(c); tab_ = ctx->clock_alloc.Map(tab_idx_); internal_memset(tab_, 0, sizeof(*tab_)); tab_->table[0] = old; } // At this point we have first level table allocated. // Add second level tables as necessary. for (uptr i = RoundUpTo(size_, ClockBlock::kClockCount); i < nclk; i += ClockBlock::kClockCount) { u32 idx = ctx->clock_alloc.Alloc(c); ClockBlock *cb = ctx->clock_alloc.Map(idx); internal_memset(cb, 0, sizeof(*cb)); CHECK_EQ(tab_->table[i/ClockBlock::kClockCount], 0); tab_->table[i/ClockBlock::kClockCount] = idx; } size_ = nclk; } // Sets a single element in the vector clock. // This function is called only from weird places like AcquireGlobal. void ThreadClock::set(unsigned tid, u64 v) { DCHECK_LT(tid, kMaxTid); DCHECK_GE(v, clk_[tid].epoch); clk_[tid].epoch = v; if (nclk_ <= tid) nclk_ = tid + 1; last_acquire_ = clk_[tid_].epoch; } void ThreadClock::DebugDump(int(*printf)(const char *s, ...)) { printf("clock=["); for (uptr i = 0; i < nclk_; i++) printf("%s%llu", i == 0 ? "" : ",", clk_[i].epoch); printf("] reused=["); for (uptr i = 0; i < nclk_; i++) printf("%s%llu", i == 0 ? "" : ",", clk_[i].reused); printf("] tid=%u/%u last_acq=%llu", tid_, reused_, last_acquire_); } SyncClock::SyncClock() : release_store_tid_(kInvalidTid) , release_store_reused_() , tab_() , tab_idx_() , size_() { for (uptr i = 0; i < kDirtyTids; i++) dirty_tids_[i] = kInvalidTid; } SyncClock::~SyncClock() { // Reset must be called before dtor. CHECK_EQ(size_, 0); CHECK_EQ(tab_, 0); CHECK_EQ(tab_idx_, 0); } void SyncClock::Reset(ClockCache *c) { if (size_ == 0) { // nothing } else if (size_ <= ClockBlock::kClockCount) { // One-level table. ctx->clock_alloc.Free(c, tab_idx_); } else { // Two-level table. for (uptr i = 0; i < size_; i += ClockBlock::kClockCount) ctx->clock_alloc.Free(c, tab_->table[i / ClockBlock::kClockCount]); ctx->clock_alloc.Free(c, tab_idx_); } tab_ = 0; tab_idx_ = 0; size_ = 0; release_store_tid_ = kInvalidTid; release_store_reused_ = 0; for (uptr i = 0; i < kDirtyTids; i++) dirty_tids_[i] = kInvalidTid; } ClockElem &SyncClock::elem(unsigned tid) const { DCHECK_LT(tid, size_); if (size_ <= ClockBlock::kClockCount) return tab_->clock[tid]; u32 idx = tab_->table[tid / ClockBlock::kClockCount]; ClockBlock *cb = ctx->clock_alloc.Map(idx); return cb->clock[tid % ClockBlock::kClockCount]; } void SyncClock::DebugDump(int(*printf)(const char *s, ...)) { printf("clock=["); for (uptr i = 0; i < size_; i++) printf("%s%llu", i == 0 ? "" : ",", elem(i).epoch); printf("] reused=["); for (uptr i = 0; i < size_; i++) printf("%s%llu", i == 0 ? "" : ",", elem(i).reused); printf("] release_store_tid=%d/%d dirty_tids=%d/%d", release_store_tid_, release_store_reused_, dirty_tids_[0], dirty_tids_[1]); } } // namespace __tsan Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_defs.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_defs.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_defs.h (revision 279191) @@ -1,188 +1,155 @@ //===-- tsan_defs.h ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #ifndef TSAN_DEFS_H #define TSAN_DEFS_H #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_libc.h" #include "tsan_stat.h" -#ifndef TSAN_DEBUG -#define TSAN_DEBUG 0 -#endif // TSAN_DEBUG - namespace __tsan { #ifdef SANITIZER_GO const bool kGoMode = true; const bool kCppMode = false; const char *const kTsanOptionsEnv = "GORACE"; // Go linker does not support weak symbols. #define CPP_WEAK #else const bool kGoMode = false; const bool kCppMode = true; const char *const kTsanOptionsEnv = "TSAN_OPTIONS"; #define CPP_WEAK WEAK #endif const int kTidBits = 13; const unsigned kMaxTid = 1 << kTidBits; const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit. const int kClkBits = 42; const unsigned kMaxTidReuse = (1 << (64 - kClkBits)) - 1; const uptr kShadowStackSize = 64 * 1024; -#ifdef TSAN_SHADOW_COUNT -# if TSAN_SHADOW_COUNT == 2 \ - || TSAN_SHADOW_COUNT == 4 || TSAN_SHADOW_COUNT == 8 -const uptr kShadowCnt = TSAN_SHADOW_COUNT; -# else -# error "TSAN_SHADOW_COUNT must be one of 2,4,8" -# endif -#else // Count of shadow values in a shadow cell. -#define TSAN_SHADOW_COUNT 4 const uptr kShadowCnt = 4; -#endif // That many user bytes are mapped onto a single shadow cell. const uptr kShadowCell = 8; // Size of a single shadow value (u64). const uptr kShadowSize = 8; // Shadow memory is kShadowMultiplier times larger than user memory. const uptr kShadowMultiplier = kShadowSize * kShadowCnt / kShadowCell; // That many user bytes are mapped onto a single meta shadow cell. // Must be less or equal to minimal memory allocator alignment. const uptr kMetaShadowCell = 8; // Size of a single meta shadow value (u32). const uptr kMetaShadowSize = 4; #if defined(TSAN_NO_HISTORY) && TSAN_NO_HISTORY const bool kCollectHistory = false; #else const bool kCollectHistory = true; #endif #if defined(TSAN_COLLECT_STATS) && TSAN_COLLECT_STATS const bool kCollectStats = true; #else const bool kCollectStats = false; #endif // The following "build consistency" machinery ensures that all source files // are built in the same configuration. Inconsistent builds lead to // hard to debug crashes. -#if TSAN_DEBUG +#if SANITIZER_DEBUG void build_consistency_debug(); #else void build_consistency_release(); #endif #if TSAN_COLLECT_STATS void build_consistency_stats(); #else void build_consistency_nostats(); #endif -#if TSAN_SHADOW_COUNT == 1 -void build_consistency_shadow1(); -#elif TSAN_SHADOW_COUNT == 2 -void build_consistency_shadow2(); -#elif TSAN_SHADOW_COUNT == 4 -void build_consistency_shadow4(); -#else -void build_consistency_shadow8(); -#endif - static inline void USED build_consistency() { -#if TSAN_DEBUG +#if SANITIZER_DEBUG build_consistency_debug(); #else build_consistency_release(); #endif #if TSAN_COLLECT_STATS build_consistency_stats(); #else build_consistency_nostats(); -#endif -#if TSAN_SHADOW_COUNT == 1 - build_consistency_shadow1(); -#elif TSAN_SHADOW_COUNT == 2 - build_consistency_shadow2(); -#elif TSAN_SHADOW_COUNT == 4 - build_consistency_shadow4(); -#else - build_consistency_shadow8(); #endif } template T min(T a, T b) { return a < b ? a : b; } template T max(T a, T b) { return a > b ? a : b; } template T RoundUp(T p, u64 align) { DCHECK_EQ(align & (align - 1), 0); return (T)(((u64)p + align - 1) & ~(align - 1)); } template T RoundDown(T p, u64 align) { DCHECK_EQ(align & (align - 1), 0); return (T)((u64)p & ~(align - 1)); } // Zeroizes high part, returns 'bits' lsb bits. template T GetLsb(T v, int bits) { return (T)((u64)v & ((1ull << bits) - 1)); } struct MD5Hash { u64 hash[2]; bool operator==(const MD5Hash &other) const; }; MD5Hash md5_hash(const void *data, uptr size); struct ThreadState; class ThreadContext; struct Context; struct ReportStack; class ReportDesc; class RegionAlloc; // Descriptor of user's memory block. struct MBlock { u64 siz; u32 stk; u16 tid; }; COMPILER_CHECK(sizeof(MBlock) == 16); } // namespace __tsan #endif // TSAN_DEFS_H Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.cc (revision 279191) @@ -1,133 +1,102 @@ //===-- tsan_flags.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" #include "tsan_flags.h" #include "tsan_rtl.h" #include "tsan_mman.h" namespace __tsan { Flags *flags() { return &ctx->flags; } // Can be overriden in frontend. #ifdef TSAN_EXTERNAL_HOOKS extern "C" const char* __tsan_default_options(); #else extern "C" SANITIZER_INTERFACE_ATTRIBUTE const char *WEAK __tsan_default_options() { return ""; } #endif -static void ParseFlags(Flags *f, const char *env) { - ParseFlag(env, &f->enable_annotations, "enable_annotations", ""); - ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks", ""); - ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses", ""); - ParseFlag(env, &f->report_bugs, "report_bugs", ""); - ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks", ""); - ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked", ""); - ParseFlag(env, &f->report_mutex_bugs, "report_mutex_bugs", ""); - ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe", ""); - ParseFlag(env, &f->report_atomic_races, "report_atomic_races", ""); - ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics", ""); - ParseFlag(env, &f->print_benign, "print_benign", ""); - ParseFlag(env, &f->exitcode, "exitcode", ""); - ParseFlag(env, &f->halt_on_error, "halt_on_error", ""); - ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms", ""); - ParseFlag(env, &f->profile_memory, "profile_memory", ""); - ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms", ""); - ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms", ""); - ParseFlag(env, &f->memory_limit_mb, "memory_limit_mb", ""); - ParseFlag(env, &f->stop_on_start, "stop_on_start", ""); - ParseFlag(env, &f->running_on_valgrind, "running_on_valgrind", ""); - ParseFlag(env, &f->history_size, "history_size", ""); - ParseFlag(env, &f->io_sync, "io_sync", ""); - ParseFlag(env, &f->die_after_fork, "die_after_fork", ""); - +void Flags::SetDefaults() { +#define TSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "tsan_flags.inc" +#undef TSAN_FLAG // DDFlags - ParseFlag(env, &f->second_deadlock_stack, "second_deadlock_stack", ""); + second_deadlock_stack = false; } +void RegisterTsanFlags(FlagParser *parser, Flags *f) { +#define TSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "tsan_flags.inc" +#undef TSAN_FLAG +} + void InitializeFlags(Flags *f, const char *env) { - internal_memset(f, 0, sizeof(*f)); + FlagParser parser; + RegisterTsanFlags(&parser, f); + RegisterCommonFlags(&parser); - // Default values. - f->enable_annotations = true; - f->suppress_equal_stacks = true; - f->suppress_equal_addresses = true; - f->report_bugs = true; - f->report_thread_leaks = true; - f->report_destroy_locked = true; - f->report_mutex_bugs = true; - f->report_signal_unsafe = true; - f->report_atomic_races = true; - f->force_seq_cst_atomics = false; - f->print_benign = false; - f->exitcode = 66; - f->halt_on_error = false; - f->atexit_sleep_ms = 1000; - f->profile_memory = ""; - f->flush_memory_ms = 0; - f->flush_symbolizer_ms = 5000; - f->memory_limit_mb = 0; - f->stop_on_start = false; - f->running_on_valgrind = false; - f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go. - f->io_sync = 1; - f->die_after_fork = true; + f->SetDefaults(); - // DDFlags - f->second_deadlock_stack = false; + SetCommonFlagsDefaults(); + { + // Override some common flags defaults. + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.allow_addr2line = true; + cf.detect_deadlocks = true; + cf.print_suppressions = false; + cf.stack_trace_format = " #%n %f %S %M"; + OverrideCommonFlags(cf); + } - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - // Override some common flags defaults. - cf->allow_addr2line = true; - cf->detect_deadlocks = true; - cf->print_suppressions = false; - cf->stack_trace_format = " #%n %f %S %M"; - // Let a frontend override. - ParseFlags(f, __tsan_default_options()); - ParseCommonFlagsFromString(cf, __tsan_default_options()); + parser.ParseString(__tsan_default_options()); // Override from command line. - ParseFlags(f, env); - ParseCommonFlagsFromString(cf, env); + parser.ParseString(env); // Sanity check. if (!f->report_bugs) { f->report_thread_leaks = false; f->report_destroy_locked = false; f->report_signal_unsafe = false; } - if (cf->help) PrintFlagDescriptions(); + SetVerbosity(common_flags()->verbosity); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) parser.PrintFlagDescriptions(); if (f->history_size < 0 || f->history_size > 7) { Printf("ThreadSanitizer: incorrect value for history_size" " (must be [0..7])\n"); Die(); } if (f->io_sync < 0 || f->io_sync > 2) { Printf("ThreadSanitizer: incorrect value for io_sync" " (must be [0..2])\n"); Die(); } } } // namespace __tsan Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.h (revision 279191) @@ -1,88 +1,35 @@ //===-- tsan_flags.h --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // NOTE: This file may be included into user code. //===----------------------------------------------------------------------===// #ifndef TSAN_FLAGS_H #define TSAN_FLAGS_H #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_deadlock_detector_interface.h" namespace __tsan { struct Flags : DDFlags { - // Enable dynamic annotations, otherwise they are no-ops. - bool enable_annotations; - // Suppress a race report if we've already output another race report - // with the same stack. - bool suppress_equal_stacks; - // Suppress a race report if we've already output another race report - // on the same address. - bool suppress_equal_addresses; - // Turns off bug reporting entirely (useful for benchmarking). - bool report_bugs; - // Report thread leaks at exit? - bool report_thread_leaks; - // Report destruction of a locked mutex? - bool report_destroy_locked; - // Report incorrect usages of mutexes and mutex annotations? - bool report_mutex_bugs; - // Report violations of async signal-safety - // (e.g. malloc() call from a signal handler). - bool report_signal_unsafe; - // Report races between atomic and plain memory accesses. - bool report_atomic_races; - // If set, all atomics are effectively sequentially consistent (seq_cst), - // regardless of what user actually specified. - bool force_seq_cst_atomics; - // Print matched "benign" races at exit. - bool print_benign; - // Override exit status if something was reported. - int exitcode; - // Exit after first reported error. - bool halt_on_error; - // Sleep in main thread before exiting for that many ms - // (useful to catch "at exit" races). - int atexit_sleep_ms; - // If set, periodically write memory profile to that file. - const char *profile_memory; - // Flush shadow memory every X ms. - int flush_memory_ms; - // Flush symbolizer caches every X ms. - int flush_symbolizer_ms; - // Resident memory limit in MB to aim at. - // If the process consumes more memory, then TSan will flush shadow memory. - int memory_limit_mb; - // Stops on start until __tsan_resume() is called (for debugging). - bool stop_on_start; - // Controls whether RunningOnValgrind() returns true or false. - bool running_on_valgrind; - // Per-thread history size, controls how many previous memory accesses - // are remembered per thread. Possible values are [0..7]. - // history_size=0 amounts to 32K memory accesses. Each next value doubles - // the amount of memory accesses, up to history_size=7 that amounts to - // 4M memory accesses. The default value is 2 (128K memory accesses). - int history_size; - // Controls level of synchronization implied by IO operations. - // 0 - no synchronization - // 1 - reasonable level of synchronization (write->read) - // 2 - global synchronization of all IO operations - int io_sync; - // Die after multi-threaded fork if the child creates new threads. - bool die_after_fork; +#define TSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "tsan_flags.inc" +#undef TSAN_FLAG + + void SetDefaults(); + void ParseFromString(const char *str); }; Flags *flags(); void InitializeFlags(Flags *flags, const char *env); } // namespace __tsan #endif // TSAN_FLAGS_H Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.inc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.inc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.inc (revision 279191) @@ -0,0 +1,78 @@ +//===-- tsan_flags.inc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// TSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_FLAG +# error "Define TSAN_FLAG prior to including this file!" +#endif + +// TSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +TSAN_FLAG(bool, enable_annotations, true, + "Enable dynamic annotations, otherwise they are no-ops.") +// Suppress a race report if we've already output another race report +// with the same stack. +TSAN_FLAG(bool, suppress_equal_stacks, true, + "Suppress a race report if we've already output another race report " + "with the same stack.") +TSAN_FLAG(bool, suppress_equal_addresses, true, + "Suppress a race report if we've already output another race report " + "on the same address.") + +TSAN_FLAG(bool, report_bugs, true, + "Turns off bug reporting entirely (useful for benchmarking).") +TSAN_FLAG(bool, report_thread_leaks, true, "Report thread leaks at exit?") +TSAN_FLAG(bool, report_destroy_locked, true, + "Report destruction of a locked mutex?") +TSAN_FLAG(bool, report_mutex_bugs, true, + "Report incorrect usages of mutexes and mutex annotations?") +TSAN_FLAG(bool, report_signal_unsafe, true, + "Report violations of async signal-safety " + "(e.g. malloc() call from a signal handler).") +TSAN_FLAG(bool, report_atomic_races, true, + "Report races between atomic and plain memory accesses.") +TSAN_FLAG( + bool, force_seq_cst_atomics, false, + "If set, all atomics are effectively sequentially consistent (seq_cst), " + "regardless of what user actually specified.") +TSAN_FLAG(bool, print_benign, false, "Print matched \"benign\" races at exit.") +TSAN_FLAG(int, exitcode, 66, "Override exit status if something was reported.") +TSAN_FLAG(bool, halt_on_error, false, "Exit after first reported error.") +TSAN_FLAG(int, atexit_sleep_ms, 1000, + "Sleep in main thread before exiting for that many ms " + "(useful to catch \"at exit\" races).") +TSAN_FLAG(const char *, profile_memory, "", + "If set, periodically write memory profile to that file.") +TSAN_FLAG(int, flush_memory_ms, 0, "Flush shadow memory every X ms.") +TSAN_FLAG(int, flush_symbolizer_ms, 5000, "Flush symbolizer caches every X ms.") +TSAN_FLAG( + int, memory_limit_mb, 0, + "Resident memory limit in MB to aim at." + "If the process consumes more memory, then TSan will flush shadow memory.") +TSAN_FLAG(bool, stop_on_start, false, + "Stops on start until __tsan_resume() is called (for debugging).") +TSAN_FLAG(bool, running_on_valgrind, false, + "Controls whether RunningOnValgrind() returns true or false.") +TSAN_FLAG( + int, history_size, kGoMode ? 1 : 2, // There are a lot of goroutines in Go. + "Per-thread history size, controls how many previous memory accesses " + "are remembered per thread. Possible values are [0..7]. " + "history_size=0 amounts to 32K memory accesses. Each next value doubles " + "the amount of memory accesses, up to history_size=7 that amounts to " + "4M memory accesses. The default value is 2 (128K memory accesses).") +TSAN_FLAG(int, io_sync, 1, + "Controls level of synchronization implied by IO operations. " + "0 - no synchronization " + "1 - reasonable level of synchronization (write->read)" + "2 - global synchronization of all IO operations.") +TSAN_FLAG(bool, die_after_fork, true, + "Die after multi-threaded fork if the child creates new threads.") Property changes on: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_flags.inc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc (revision 279191) @@ -1,2587 +1,2598 @@ //===-- tsan_interceptors.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // // FIXME: move as many interceptors as possible into // sanitizer_common/sanitizer_common_interceptors.inc //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_linux.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "interception/interception.h" #include "tsan_interface.h" #include "tsan_platform.h" #include "tsan_suppressions.h" #include "tsan_rtl.h" #include "tsan_mman.h" #include "tsan_fd.h" using namespace __tsan; // NOLINT #if SANITIZER_FREEBSD #define __errno_location __error #define __libc_malloc __malloc #define __libc_realloc __realloc #define __libc_calloc __calloc #define __libc_free __free #define stdout __stdoutp #define stderr __stderrp #endif const int kSigCount = 65; struct my_siginfo_t { // The size is determined by looking at sizeof of real siginfo_t on linux. u64 opaque[128 / sizeof(u64)]; }; struct ucontext_t { // The size is determined by looking at sizeof of real ucontext_t on linux. u64 opaque[936 / sizeof(u64) + 1]; }; extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *) extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); extern "C" int pthread_setspecific(unsigned key, const void *v); DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *) extern "C" int pthread_yield(); extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset); // REAL(sigfillset) defined in common interceptors. DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set) DECLARE_REAL(int, fflush, __sanitizer_FILE *fp) extern "C" void *pthread_self(); extern "C" void _exit(int status); extern "C" int *__errno_location(); extern "C" int fileno_unlocked(void *stream); extern "C" void *__libc_malloc(uptr size); extern "C" void *__libc_calloc(uptr size, uptr n); extern "C" void *__libc_realloc(void *ptr, uptr size); extern "C" void __libc_free(void *ptr); +extern "C" int dirfd(void *dirp); #if !SANITIZER_FREEBSD extern "C" int mallopt(int param, int value); #endif extern __sanitizer_FILE *stdout, *stderr; const int PTHREAD_MUTEX_RECURSIVE = 1; const int PTHREAD_MUTEX_RECURSIVE_NP = 1; const int EINVAL = 22; const int EBUSY = 16; const int EOWNERDEAD = 130; const int EPOLL_CTL_ADD = 1; const int SIGILL = 4; const int SIGABRT = 6; const int SIGFPE = 8; const int SIGSEGV = 11; const int SIGPIPE = 13; const int SIGTERM = 15; const int SIGBUS = 7; const int SIGSYS = 31; void *const MAP_FAILED = (void*)-1; const int PTHREAD_BARRIER_SERIAL_THREAD = -1; const int MAP_FIXED = 0x10; typedef long long_t; // NOLINT // From /usr/include/unistd.h # define F_ULOCK 0 /* Unlock a previously locked region. */ # define F_LOCK 1 /* Lock a region for exclusive use. */ # define F_TLOCK 2 /* Test and lock a region for exclusive use. */ # define F_TEST 3 /* Test a region for other processes locks. */ -typedef void (*sighandler_t)(int sig); - #define errno (*__errno_location()) +typedef void (*sighandler_t)(int sig); +typedef void (*sigactionhandler_t)(int sig, my_siginfo_t *siginfo, void *uctx); + struct sigaction_t { union { sighandler_t sa_handler; - void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx); + sigactionhandler_t sa_sigaction; }; #if SANITIZER_FREEBSD int sa_flags; __sanitizer_sigset_t sa_mask; #else __sanitizer_sigset_t sa_mask; int sa_flags; void (*sa_restorer)(); #endif }; const sighandler_t SIG_DFL = (sighandler_t)0; const sighandler_t SIG_IGN = (sighandler_t)1; const sighandler_t SIG_ERR = (sighandler_t)-1; const int SA_SIGINFO = 4; const int SIG_SETMASK = 2; namespace std { struct nothrow_t {}; } // namespace std static sigaction_t sigactions[kSigCount]; namespace __tsan { struct SignalDesc { bool armed; bool sigaction; my_siginfo_t siginfo; ucontext_t ctx; }; struct SignalContext { int int_signal_send; atomic_uintptr_t in_blocking_func; atomic_uintptr_t have_pending_signals; SignalDesc pending_signals[kSigCount]; }; // The object is 64-byte aligned, because we want hot data to be located in // a single cache line if possible (it's accessed in every interceptor). static ALIGNED(64) char libignore_placeholder[sizeof(LibIgnore)]; static LibIgnore *libignore() { return reinterpret_cast(&libignore_placeholder[0]); } void InitializeLibIgnore() { libignore()->Init(*SuppressionContext::Get()); libignore()->OnLibraryLoaded(0); } } // namespace __tsan static SignalContext *SigCtx(ThreadState *thr) { SignalContext *ctx = (SignalContext*)thr->signal_ctx; if (ctx == 0 && !thr->is_dead) { ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext"); MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx)); thr->signal_ctx = ctx; } return ctx; } static unsigned g_thread_finalize_key; class ScopedInterceptor { public: ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc); ~ScopedInterceptor(); private: ThreadState *const thr_; const uptr pc_; bool in_ignored_lib_; }; ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc) : thr_(thr) , pc_(pc) , in_ignored_lib_(false) { if (!thr_->ignore_interceptors) { Initialize(thr); FuncEntry(thr, pc); } DPrintf("#%d: intercept %s()\n", thr_->tid, fname); if (!thr_->in_ignored_lib && libignore()->IsIgnored(pc)) { in_ignored_lib_ = true; thr_->in_ignored_lib = true; ThreadIgnoreBegin(thr_, pc_); } } ScopedInterceptor::~ScopedInterceptor() { if (in_ignored_lib_) { thr_->in_ignored_lib = false; ThreadIgnoreEnd(thr_, pc_); } if (!thr_->ignore_interceptors) { ProcessPendingSignals(thr_); FuncExit(thr_); CheckNoLocks(thr_); } } #define SCOPED_INTERCEPTOR_RAW(func, ...) \ ThreadState *thr = cur_thread(); \ const uptr caller_pc = GET_CALLER_PC(); \ ScopedInterceptor si(thr, #func, caller_pc); \ const uptr pc = StackTrace::GetCurrentPc(); \ (void)pc; \ /**/ #define SCOPED_TSAN_INTERCEPTOR(func, ...) \ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \ if (REAL(func) == 0) { \ Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ Die(); \ } \ if (thr->ignore_interceptors || thr->in_ignored_lib) \ return REAL(func)(__VA_ARGS__); \ /**/ #define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__) #define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func) #if SANITIZER_FREEBSD # define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func) #else # define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver) #endif #define BLOCK_REAL(name) (BlockingCall(thr), REAL(name)) struct BlockingCall { explicit BlockingCall(ThreadState *thr) : thr(thr) , ctx(SigCtx(thr)) { for (;;) { atomic_store(&ctx->in_blocking_func, 1, memory_order_relaxed); if (atomic_load(&ctx->have_pending_signals, memory_order_relaxed) == 0) break; atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed); ProcessPendingSignals(thr); } // When we are in a "blocking call", we process signals asynchronously // (right when they arrive). In this context we do not expect to be // executing any user/runtime code. The known interceptor sequence when // this is not true is: pthread_join -> munmap(stack). It's fine // to ignore munmap in this case -- we handle stack shadow separately. thr->ignore_interceptors++; } ~BlockingCall() { thr->ignore_interceptors--; atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed); } ThreadState *thr; SignalContext *ctx; }; TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) { SCOPED_TSAN_INTERCEPTOR(sleep, sec); unsigned res = BLOCK_REAL(sleep)(sec); AfterSleep(thr, pc); return res; } TSAN_INTERCEPTOR(int, usleep, long_t usec) { SCOPED_TSAN_INTERCEPTOR(usleep, usec); int res = BLOCK_REAL(usleep)(usec); AfterSleep(thr, pc); return res; } TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) { SCOPED_TSAN_INTERCEPTOR(nanosleep, req, rem); int res = BLOCK_REAL(nanosleep)(req, rem); AfterSleep(thr, pc); return res; } // The sole reason tsan wraps atexit callbacks is to establish synchronization // between callback setup and callback execution. struct AtExitCtx { void (*f)(); void *arg; }; static void at_exit_wrapper(void *arg) { ThreadState *thr = cur_thread(); uptr pc = 0; Acquire(thr, pc, (uptr)arg); AtExitCtx *ctx = (AtExitCtx*)arg; ((void(*)(void *arg))ctx->f)(ctx->arg); __libc_free(ctx); } static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(), void *arg, void *dso); TSAN_INTERCEPTOR(int, atexit, void (*f)()) { if (cur_thread()->in_symbolizer) return 0; // We want to setup the atexit callback even if we are in ignored lib // or after fork. SCOPED_INTERCEPTOR_RAW(atexit, f); return setup_at_exit_wrapper(thr, pc, (void(*)())f, 0, 0); } TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { if (cur_thread()->in_symbolizer) return 0; SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso); return setup_at_exit_wrapper(thr, pc, (void(*)())f, arg, dso); } static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(), void *arg, void *dso) { AtExitCtx *ctx = (AtExitCtx*)__libc_malloc(sizeof(AtExitCtx)); ctx->f = f; ctx->arg = arg; Release(thr, pc, (uptr)ctx); // Memory allocation in __cxa_atexit will race with free during exit, // because we do not see synchronization around atexit callback list. ThreadIgnoreBegin(thr, pc); int res = REAL(__cxa_atexit)(at_exit_wrapper, ctx, dso); ThreadIgnoreEnd(thr, pc); return res; } static void on_exit_wrapper(int status, void *arg) { ThreadState *thr = cur_thread(); uptr pc = 0; Acquire(thr, pc, (uptr)arg); AtExitCtx *ctx = (AtExitCtx*)arg; ((void(*)(int status, void *arg))ctx->f)(status, ctx->arg); __libc_free(ctx); } TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) { if (cur_thread()->in_symbolizer) return 0; SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg); AtExitCtx *ctx = (AtExitCtx*)__libc_malloc(sizeof(AtExitCtx)); ctx->f = (void(*)())f; ctx->arg = arg; Release(thr, pc, (uptr)ctx); // Memory allocation in __cxa_atexit will race with free during exit, // because we do not see synchronization around atexit callback list. ThreadIgnoreBegin(thr, pc); int res = REAL(on_exit)(on_exit_wrapper, ctx); ThreadIgnoreEnd(thr, pc); return res; } // Cleanup old bufs. static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) { for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) { JmpBuf *buf = &thr->jmp_bufs[i]; if (buf->sp <= sp) { uptr sz = thr->jmp_bufs.Size(); thr->jmp_bufs[i] = thr->jmp_bufs[sz - 1]; thr->jmp_bufs.PopBack(); i--; } } } static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) { if (thr->shadow_stack_pos == 0) // called from libc guts during bootstrap return; // Cleanup old bufs. JmpBufGarbageCollect(thr, sp); // Remember the buf. JmpBuf *buf = thr->jmp_bufs.PushBack(); buf->sp = sp; buf->mangled_sp = mangled_sp; buf->shadow_stack_pos = thr->shadow_stack_pos; SignalContext *sctx = SigCtx(thr); buf->int_signal_send = sctx ? sctx->int_signal_send : 0; buf->in_blocking_func = sctx ? atomic_load(&sctx->in_blocking_func, memory_order_relaxed) : false; buf->in_signal_handler = atomic_load(&thr->in_signal_handler, memory_order_relaxed); } static void LongJmp(ThreadState *thr, uptr *env) { #if SANITIZER_FREEBSD uptr mangled_sp = env[2]; #else uptr mangled_sp = env[6]; #endif // SANITIZER_FREEBSD // Find the saved buf by mangled_sp. for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) { JmpBuf *buf = &thr->jmp_bufs[i]; if (buf->mangled_sp == mangled_sp) { CHECK_GE(thr->shadow_stack_pos, buf->shadow_stack_pos); // Unwind the stack. while (thr->shadow_stack_pos > buf->shadow_stack_pos) FuncExit(thr); SignalContext *sctx = SigCtx(thr); if (sctx) { sctx->int_signal_send = buf->int_signal_send; atomic_store(&sctx->in_blocking_func, buf->in_blocking_func, memory_order_relaxed); } atomic_store(&thr->in_signal_handler, buf->in_signal_handler, memory_order_relaxed); JmpBufGarbageCollect(thr, buf->sp - 1); // do not collect buf->sp return; } } Printf("ThreadSanitizer: can't find longjmp buf\n"); CHECK(0); } // FIXME: put everything below into a common extern "C" block? extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) { SetJmp(cur_thread(), sp, mangled_sp); } // Not called. Merely to satisfy TSAN_INTERCEPT(). extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __interceptor_setjmp(void *env); extern "C" int __interceptor_setjmp(void *env) { CHECK(0); return 0; } // FIXME: any reason to have a separate declaration? extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __interceptor__setjmp(void *env); extern "C" int __interceptor__setjmp(void *env) { CHECK(0); return 0; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __interceptor_sigsetjmp(void *env); extern "C" int __interceptor_sigsetjmp(void *env) { CHECK(0); return 0; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __interceptor___sigsetjmp(void *env); extern "C" int __interceptor___sigsetjmp(void *env) { CHECK(0); return 0; } extern "C" int setjmp(void *env); extern "C" int _setjmp(void *env); extern "C" int sigsetjmp(void *env); extern "C" int __sigsetjmp(void *env); DEFINE_REAL(int, setjmp, void *env) DEFINE_REAL(int, _setjmp, void *env) DEFINE_REAL(int, sigsetjmp, void *env) DEFINE_REAL(int, __sigsetjmp, void *env) TSAN_INTERCEPTOR(void, longjmp, uptr *env, int val) { { SCOPED_TSAN_INTERCEPTOR(longjmp, env, val); } LongJmp(cur_thread(), env); REAL(longjmp)(env, val); } TSAN_INTERCEPTOR(void, siglongjmp, uptr *env, int val) { { SCOPED_TSAN_INTERCEPTOR(siglongjmp, env, val); } LongJmp(cur_thread(), env); REAL(siglongjmp)(env, val); } TSAN_INTERCEPTOR(void*, malloc, uptr size) { if (cur_thread()->in_symbolizer) return __libc_malloc(size); void *p = 0; { SCOPED_INTERCEPTOR_RAW(malloc, size); p = user_alloc(thr, pc, size); } invoke_malloc_hook(p, size); return p; } TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { SCOPED_TSAN_INTERCEPTOR(__libc_memalign, align, sz); return user_alloc(thr, pc, sz, align); } TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { if (cur_thread()->in_symbolizer) return __libc_calloc(size, n); - if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) - return AllocatorReturnNull(); void *p = 0; { SCOPED_INTERCEPTOR_RAW(calloc, size, n); - p = user_alloc(thr, pc, n * size); - if (p) - internal_memset(p, 0, n * size); + p = user_calloc(thr, pc, size, n); } invoke_malloc_hook(p, n * size); return p; } TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { if (cur_thread()->in_symbolizer) return __libc_realloc(p, size); if (p) invoke_free_hook(p); { SCOPED_INTERCEPTOR_RAW(realloc, p, size); p = user_realloc(thr, pc, p, size); } invoke_malloc_hook(p, size); return p; } TSAN_INTERCEPTOR(void, free, void *p) { if (p == 0) return; if (cur_thread()->in_symbolizer) return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(free, p); user_free(thr, pc, p); } TSAN_INTERCEPTOR(void, cfree, void *p) { if (p == 0) return; if (cur_thread()->in_symbolizer) return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(cfree, p); user_free(thr, pc, p); } TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p); return user_alloc_usable_size(p); } #define OPERATOR_NEW_BODY(mangled_name) \ if (cur_thread()->in_symbolizer) \ return __libc_malloc(size); \ void *p = 0; \ { \ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \ p = user_alloc(thr, pc, size); \ } \ invoke_malloc_hook(p, size); \ return p; SANITIZER_INTERFACE_ATTRIBUTE void *operator new(__sanitizer::uptr size); void *operator new(__sanitizer::uptr size) { OPERATOR_NEW_BODY(_Znwm); } SANITIZER_INTERFACE_ATTRIBUTE void *operator new[](__sanitizer::uptr size); void *operator new[](__sanitizer::uptr size) { OPERATOR_NEW_BODY(_Znam); } SANITIZER_INTERFACE_ATTRIBUTE void *operator new(__sanitizer::uptr size, std::nothrow_t const&); void *operator new(__sanitizer::uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t); } SANITIZER_INTERFACE_ATTRIBUTE void *operator new[](__sanitizer::uptr size, std::nothrow_t const&); void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t); } #define OPERATOR_DELETE_BODY(mangled_name) \ if (ptr == 0) return; \ if (cur_thread()->in_symbolizer) \ return __libc_free(ptr); \ invoke_free_hook(ptr); \ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ user_free(thr, pc, ptr); SANITIZER_INTERFACE_ATTRIBUTE void operator delete(void *ptr) throw(); void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY(_ZdlPv); } SANITIZER_INTERFACE_ATTRIBUTE void operator delete[](void *ptr) throw(); void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY(_ZdaPv); } SANITIZER_INTERFACE_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&); void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t); } SANITIZER_INTERFACE_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&); void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t); } TSAN_INTERCEPTOR(uptr, strlen, const char *s) { SCOPED_TSAN_INTERCEPTOR(strlen, s); uptr len = internal_strlen(s); MemoryAccessRange(thr, pc, (uptr)s, len + 1, false); return len; } TSAN_INTERCEPTOR(void*, memset, void *dst, int v, uptr size) { SCOPED_TSAN_INTERCEPTOR(memset, dst, v, size); MemoryAccessRange(thr, pc, (uptr)dst, size, true); return internal_memset(dst, v, size); } TSAN_INTERCEPTOR(void*, memcpy, void *dst, const void *src, uptr size) { SCOPED_TSAN_INTERCEPTOR(memcpy, dst, src, size); MemoryAccessRange(thr, pc, (uptr)dst, size, true); MemoryAccessRange(thr, pc, (uptr)src, size, false); return internal_memcpy(dst, src, size); } TSAN_INTERCEPTOR(int, memcmp, const void *s1, const void *s2, uptr n) { SCOPED_TSAN_INTERCEPTOR(memcmp, s1, s2, n); int res = 0; uptr len = 0; for (; len < n; len++) { if ((res = ((const unsigned char *)s1)[len] - ((const unsigned char *)s2)[len])) break; } MemoryAccessRange(thr, pc, (uptr)s1, len < n ? len + 1 : n, false); MemoryAccessRange(thr, pc, (uptr)s2, len < n ? len + 1 : n, false); return res; } TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) { SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n); MemoryAccessRange(thr, pc, (uptr)dst, n, true); MemoryAccessRange(thr, pc, (uptr)src, n, false); return REAL(memmove)(dst, src, n); } TSAN_INTERCEPTOR(char*, strchr, char *s, int c) { SCOPED_TSAN_INTERCEPTOR(strchr, s, c); char *res = REAL(strchr)(s, c); uptr len = res ? (char*)res - (char*)s + 1 : internal_strlen(s) + 1; MemoryAccessRange(thr, pc, (uptr)s, len, false); return res; } TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) { SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c); char *res = REAL(strchrnul)(s, c); uptr len = (char*)res - (char*)s + 1; MemoryAccessRange(thr, pc, (uptr)s, len, false); return res; } TSAN_INTERCEPTOR(char*, strrchr, char *s, int c) { SCOPED_TSAN_INTERCEPTOR(strrchr, s, c); MemoryAccessRange(thr, pc, (uptr)s, internal_strlen(s) + 1, false); return REAL(strrchr)(s, c); } TSAN_INTERCEPTOR(char*, strcpy, char *dst, const char *src) { // NOLINT SCOPED_TSAN_INTERCEPTOR(strcpy, dst, src); // NOLINT uptr srclen = internal_strlen(src); MemoryAccessRange(thr, pc, (uptr)dst, srclen + 1, true); MemoryAccessRange(thr, pc, (uptr)src, srclen + 1, false); return REAL(strcpy)(dst, src); // NOLINT } TSAN_INTERCEPTOR(char*, strncpy, char *dst, char *src, uptr n) { SCOPED_TSAN_INTERCEPTOR(strncpy, dst, src, n); uptr srclen = internal_strnlen(src, n); MemoryAccessRange(thr, pc, (uptr)dst, n, true); MemoryAccessRange(thr, pc, (uptr)src, min(srclen + 1, n), false); return REAL(strncpy)(dst, src, n); } TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) { SCOPED_TSAN_INTERCEPTOR(strstr, s1, s2); const char *res = REAL(strstr)(s1, s2); uptr len1 = internal_strlen(s1); uptr len2 = internal_strlen(s2); MemoryAccessRange(thr, pc, (uptr)s1, len1 + 1, false); MemoryAccessRange(thr, pc, (uptr)s2, len2 + 1, false); return res; } TSAN_INTERCEPTOR(char*, strdup, const char *str) { SCOPED_TSAN_INTERCEPTOR(strdup, str); // strdup will call malloc, so no instrumentation is required here. return REAL(strdup)(str); } static bool fix_mmap_addr(void **addr, long_t sz, int flags) { if (*addr) { if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) { if (flags & MAP_FIXED) { errno = EINVAL; return false; } else { *addr = 0; } } } return true; } TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot, int flags, int fd, unsigned off) { SCOPED_TSAN_INTERCEPTOR(mmap, addr, sz, prot, flags, fd, off); if (!fix_mmap_addr(&addr, sz, flags)) return MAP_FAILED; void *res = REAL(mmap)(addr, sz, prot, flags, fd, off); if (res != MAP_FAILED) { if (fd > 0) FdAccess(thr, pc, fd); MemoryRangeImitateWrite(thr, pc, (uptr)res, sz); } return res; } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot, int flags, int fd, u64 off) { SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off); if (!fix_mmap_addr(&addr, sz, flags)) return MAP_FAILED; void *res = REAL(mmap64)(addr, sz, prot, flags, fd, off); if (res != MAP_FAILED) { if (fd > 0) FdAccess(thr, pc, fd); MemoryRangeImitateWrite(thr, pc, (uptr)res, sz); } return res; } #define TSAN_MAYBE_INTERCEPT_MMAP64 TSAN_INTERCEPT(mmap64) #else #define TSAN_MAYBE_INTERCEPT_MMAP64 #endif TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) { SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz); DontNeedShadowFor((uptr)addr, sz); int res = REAL(munmap)(addr, sz); return res; } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) { SCOPED_INTERCEPTOR_RAW(memalign, align, sz); return user_alloc(thr, pc, sz, align); } #define TSAN_MAYBE_INTERCEPT_MEMALIGN TSAN_INTERCEPT(memalign) #else #define TSAN_MAYBE_INTERCEPT_MEMALIGN #endif TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) { SCOPED_INTERCEPTOR_RAW(memalign, align, sz); return user_alloc(thr, pc, sz, align); } TSAN_INTERCEPTOR(void*, valloc, uptr sz) { SCOPED_INTERCEPTOR_RAW(valloc, sz); return user_alloc(thr, pc, sz, GetPageSizeCached()); } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) { SCOPED_INTERCEPTOR_RAW(pvalloc, sz); sz = RoundUp(sz, GetPageSizeCached()); return user_alloc(thr, pc, sz, GetPageSizeCached()); } #define TSAN_MAYBE_INTERCEPT_PVALLOC TSAN_INTERCEPT(pvalloc) #else #define TSAN_MAYBE_INTERCEPT_PVALLOC #endif TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) { SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz); *memptr = user_alloc(thr, pc, sz, align); return 0; } // Used in thread-safe function static initialization. extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) { SCOPED_INTERCEPTOR_RAW(__cxa_guard_acquire, g); for (;;) { u32 cmp = atomic_load(g, memory_order_acquire); if (cmp == 0) { if (atomic_compare_exchange_strong(g, &cmp, 1<<16, memory_order_relaxed)) return 1; } else if (cmp == 1) { Acquire(thr, pc, (uptr)g); return 0; } else { internal_sched_yield(); } } } extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_release(atomic_uint32_t *g) { SCOPED_INTERCEPTOR_RAW(__cxa_guard_release, g); Release(thr, pc, (uptr)g); atomic_store(g, 1, memory_order_release); } extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_abort(atomic_uint32_t *g) { SCOPED_INTERCEPTOR_RAW(__cxa_guard_abort, g); atomic_store(g, 0, memory_order_relaxed); } static void thread_finalize(void *v) { uptr iter = (uptr)v; if (iter > 1) { if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) { Printf("ThreadSanitizer: failed to set thread key\n"); Die(); } return; } { ThreadState *thr = cur_thread(); ThreadFinish(thr); SignalContext *sctx = thr->signal_ctx; if (sctx) { thr->signal_ctx = 0; UnmapOrDie(sctx, sizeof(*sctx)); } } } struct ThreadParam { void* (*callback)(void *arg); void *param; atomic_uintptr_t tid; }; extern "C" void *__tsan_thread_start_func(void *arg) { ThreadParam *p = (ThreadParam*)arg; void* (*callback)(void *arg) = p->callback; void *param = p->param; int tid = 0; { ThreadState *thr = cur_thread(); // Thread-local state is not initialized yet. ScopedIgnoreInterceptors ignore; ThreadIgnoreBegin(thr, 0); if (pthread_setspecific(g_thread_finalize_key, (void *)kPthreadDestructorIterations)) { Printf("ThreadSanitizer: failed to set thread key\n"); Die(); } ThreadIgnoreEnd(thr, 0); while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0) pthread_yield(); atomic_store(&p->tid, 0, memory_order_release); ThreadStart(thr, tid, GetTid()); } void *res = callback(param); // Prevent the callback from being tail called, // it mixes up stack traces. volatile int foo = 42; foo++; return res; } TSAN_INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param); if (ctx->after_multithreaded_fork) { if (flags()->die_after_fork) { Report("ThreadSanitizer: starting new threads after multi-threaded " "fork is not supported. Dying (set die_after_fork=0 to override)\n"); Die(); } else { VPrintf(1, "ThreadSanitizer: starting new threads after multi-threaded " "fork is not supported (pid %d). Continuing because of " "die_after_fork=0, but you are on your own\n", internal_getpid()); } } __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); attr = &myattr; } int detached = 0; REAL(pthread_attr_getdetachstate)(attr, &detached); AdjustStackSize(attr); ThreadParam p; p.callback = callback; p.param = param; atomic_store(&p.tid, 0, memory_order_relaxed); int res = -1; { // Otherwise we see false positives in pthread stack manipulation. ScopedIgnoreInterceptors ignore; ThreadIgnoreBegin(thr, pc); res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p); ThreadIgnoreEnd(thr, pc); } if (res == 0) { int tid = ThreadCreate(thr, pc, *(uptr*)th, detached); CHECK_NE(tid, 0); atomic_store(&p.tid, tid, memory_order_release); while (atomic_load(&p.tid, memory_order_acquire) != 0) pthread_yield(); } if (attr == &myattr) pthread_attr_destroy(&myattr); return res; } TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) { SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret); int tid = ThreadTid(thr, pc, (uptr)th); ThreadIgnoreBegin(thr, pc); int res = BLOCK_REAL(pthread_join)(th, ret); ThreadIgnoreEnd(thr, pc); if (res == 0) { ThreadJoin(thr, pc, tid); } return res; } +DEFINE_REAL_PTHREAD_FUNCTIONS + TSAN_INTERCEPTOR(int, pthread_detach, void *th) { SCOPED_TSAN_INTERCEPTOR(pthread_detach, th); int tid = ThreadTid(thr, pc, (uptr)th); int res = REAL(pthread_detach)(th); if (res == 0) { ThreadDetach(thr, pc, tid); } return res; } // Problem: // NPTL implementation of pthread_cond has 2 versions (2.2.5 and 2.3.2). // pthread_cond_t has different size in the different versions. // If call new REAL functions for old pthread_cond_t, they will corrupt memory // after pthread_cond_t (old cond is smaller). // If we call old REAL functions for new pthread_cond_t, we will lose some // functionality (e.g. old functions do not support waiting against // CLOCK_REALTIME). // Proper handling would require to have 2 versions of interceptors as well. // But this is messy, in particular requires linker scripts when sanitizer // runtime is linked into a shared library. // Instead we assume we don't have dynamic libraries built against old // pthread (2.2.5 is dated by 2002). And provide legacy_pthread_cond flag // that allows to work with old libraries (but this mode does not support // some features, e.g. pthread_condattr_getpshared). static void *init_cond(void *c, bool force = false) { // sizeof(pthread_cond_t) >= sizeof(uptr) in both versions. // So we allocate additional memory on the side large enough to hold // any pthread_cond_t object. Always call new REAL functions, but pass // the aux object to them. // Note: the code assumes that PTHREAD_COND_INITIALIZER initializes // first word of pthread_cond_t to zero. // It's all relevant only for linux. if (!common_flags()->legacy_pthread_cond) return c; atomic_uintptr_t *p = (atomic_uintptr_t*)c; uptr cond = atomic_load(p, memory_order_acquire); if (!force && cond != 0) return (void*)cond; void *newcond = WRAP(malloc)(pthread_cond_t_sz); internal_memset(newcond, 0, pthread_cond_t_sz); if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond, memory_order_acq_rel)) return newcond; WRAP(free)(newcond); return (void*)cond; } struct CondMutexUnlockCtx { ThreadState *thr; uptr pc; void *m; }; static void cond_mutex_unlock(CondMutexUnlockCtx *arg) { MutexLock(arg->thr, arg->pc, (uptr)arg->m); } INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { void *cond = init_cond(c, true); SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, cond, a); MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), true); return REAL(pthread_cond_init)(cond, a); } INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, cond, m); MutexUnlock(thr, pc, (uptr)m); MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false); CondMutexUnlockCtx arg = {thr, pc, m}; // This ensures that we handle mutex lock even in case of pthread_cancel. // See test/tsan/cond_cancel.cc. int res = call_pthread_cancel_with_cleanup( (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait), cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg); if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); MutexLock(thr, pc, (uptr)m); return res; } INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, cond, m, abstime); MutexUnlock(thr, pc, (uptr)m); MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false); CondMutexUnlockCtx arg = {thr, pc, m}; // This ensures that we handle mutex lock even in case of pthread_cancel. // See test/tsan/cond_cancel.cc. int res = call_pthread_cancel_with_cleanup( REAL(pthread_cond_timedwait), cond, m, abstime, (void(*)(void *arg))cond_mutex_unlock, &arg); if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); MutexLock(thr, pc, (uptr)m); return res; } INTERCEPTOR(int, pthread_cond_signal, void *c) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, cond); MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false); return REAL(pthread_cond_signal)(cond); } INTERCEPTOR(int, pthread_cond_broadcast, void *c) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, cond); MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false); return REAL(pthread_cond_broadcast)(cond); } INTERCEPTOR(int, pthread_cond_destroy, void *c) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, cond); MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), true); int res = REAL(pthread_cond_destroy)(cond); if (common_flags()->legacy_pthread_cond) { // Free our aux cond and zero the pointer to not leave dangling pointers. WRAP(free)(cond); atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed); } return res; } TSAN_INTERCEPTOR(int, pthread_mutex_init, void *m, void *a) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_init, m, a); int res = REAL(pthread_mutex_init)(m, a); if (res == 0) { bool recursive = false; if (a) { int type = 0; if (REAL(pthread_mutexattr_gettype)(a, &type) == 0) recursive = (type == PTHREAD_MUTEX_RECURSIVE || type == PTHREAD_MUTEX_RECURSIVE_NP); } MutexCreate(thr, pc, (uptr)m, false, recursive, false); } return res; } TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_destroy, m); int res = REAL(pthread_mutex_destroy)(m); if (res == 0 || res == EBUSY) { MutexDestroy(thr, pc, (uptr)m); } return res; } TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m); int res = REAL(pthread_mutex_trylock)(m); if (res == EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); if (res == 0 || res == EOWNERDEAD) MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true); return res; } TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime); int res = REAL(pthread_mutex_timedlock)(m, abstime); if (res == 0) { MutexLock(thr, pc, (uptr)m); } return res; } TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared); int res = REAL(pthread_spin_init)(m, pshared); if (res == 0) { MutexCreate(thr, pc, (uptr)m, false, false, false); } return res; } TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_destroy, m); int res = REAL(pthread_spin_destroy)(m); if (res == 0) { MutexDestroy(thr, pc, (uptr)m); } return res; } TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m); int res = REAL(pthread_spin_lock)(m); if (res == 0) { MutexLock(thr, pc, (uptr)m); } return res; } TSAN_INTERCEPTOR(int, pthread_spin_trylock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m); int res = REAL(pthread_spin_trylock)(m); if (res == 0) { MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true); } return res; } TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_unlock, m); MutexUnlock(thr, pc, (uptr)m); int res = REAL(pthread_spin_unlock)(m); return res; } TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a); int res = REAL(pthread_rwlock_init)(m, a); if (res == 0) { MutexCreate(thr, pc, (uptr)m, true, false, false); } return res; } TSAN_INTERCEPTOR(int, pthread_rwlock_destroy, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_destroy, m); int res = REAL(pthread_rwlock_destroy)(m); if (res == 0) { MutexDestroy(thr, pc, (uptr)m); } return res; } TSAN_INTERCEPTOR(int, pthread_rwlock_rdlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_rdlock, m); int res = REAL(pthread_rwlock_rdlock)(m); if (res == 0) { MutexReadLock(thr, pc, (uptr)m); } return res; } TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_tryrdlock, m); int res = REAL(pthread_rwlock_tryrdlock)(m); if (res == 0) { MutexReadLock(thr, pc, (uptr)m, /*try_lock=*/true); } return res; } TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime); int res = REAL(pthread_rwlock_timedrdlock)(m, abstime); if (res == 0) { MutexReadLock(thr, pc, (uptr)m); } return res; } TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m); int res = REAL(pthread_rwlock_wrlock)(m); if (res == 0) { MutexLock(thr, pc, (uptr)m); } return res; } TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_trywrlock, m); int res = REAL(pthread_rwlock_trywrlock)(m); if (res == 0) { MutexLock(thr, pc, (uptr)m, /*rec=*/1, /*try_lock=*/true); } return res; } TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime); int res = REAL(pthread_rwlock_timedwrlock)(m, abstime); if (res == 0) { MutexLock(thr, pc, (uptr)m); } return res; } TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m); MutexReadOrWriteUnlock(thr, pc, (uptr)m); int res = REAL(pthread_rwlock_unlock)(m); return res; } TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) { SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count); MemoryWrite(thr, pc, (uptr)b, kSizeLog1); int res = REAL(pthread_barrier_init)(b, a, count); return res; } TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) { SCOPED_TSAN_INTERCEPTOR(pthread_barrier_destroy, b); MemoryWrite(thr, pc, (uptr)b, kSizeLog1); int res = REAL(pthread_barrier_destroy)(b); return res; } TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) { SCOPED_TSAN_INTERCEPTOR(pthread_barrier_wait, b); Release(thr, pc, (uptr)b); MemoryRead(thr, pc, (uptr)b, kSizeLog1); int res = REAL(pthread_barrier_wait)(b); MemoryRead(thr, pc, (uptr)b, kSizeLog1); if (res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD) { Acquire(thr, pc, (uptr)b); } return res; } TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { SCOPED_INTERCEPTOR_RAW(pthread_once, o, f); if (o == 0 || f == 0) return EINVAL; atomic_uint32_t *a = static_cast(o); u32 v = atomic_load(a, memory_order_acquire); if (v == 0 && atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) { (*f)(); if (!thr->in_ignored_lib) Release(thr, pc, (uptr)o); atomic_store(a, 2, memory_order_release); } else { while (v != 2) { pthread_yield(); v = atomic_load(a, memory_order_acquire); } if (!thr->in_ignored_lib) Acquire(thr, pc, (uptr)o); } return 0; } TSAN_INTERCEPTOR(int, sem_init, void *s, int pshared, unsigned value) { SCOPED_TSAN_INTERCEPTOR(sem_init, s, pshared, value); int res = REAL(sem_init)(s, pshared, value); return res; } TSAN_INTERCEPTOR(int, sem_destroy, void *s) { SCOPED_TSAN_INTERCEPTOR(sem_destroy, s); int res = REAL(sem_destroy)(s); return res; } TSAN_INTERCEPTOR(int, sem_wait, void *s) { SCOPED_TSAN_INTERCEPTOR(sem_wait, s); int res = BLOCK_REAL(sem_wait)(s); if (res == 0) { Acquire(thr, pc, (uptr)s); } return res; } TSAN_INTERCEPTOR(int, sem_trywait, void *s) { SCOPED_TSAN_INTERCEPTOR(sem_trywait, s); int res = BLOCK_REAL(sem_trywait)(s); if (res == 0) { Acquire(thr, pc, (uptr)s); } return res; } TSAN_INTERCEPTOR(int, sem_timedwait, void *s, void *abstime) { SCOPED_TSAN_INTERCEPTOR(sem_timedwait, s, abstime); int res = BLOCK_REAL(sem_timedwait)(s, abstime); if (res == 0) { Acquire(thr, pc, (uptr)s); } return res; } TSAN_INTERCEPTOR(int, sem_post, void *s) { SCOPED_TSAN_INTERCEPTOR(sem_post, s); Release(thr, pc, (uptr)s); int res = REAL(sem_post)(s); return res; } TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) { SCOPED_TSAN_INTERCEPTOR(sem_getvalue, s, sval); int res = REAL(sem_getvalue)(s, sval); if (res == 0) { Acquire(thr, pc, (uptr)s); } return res; } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf); return REAL(__xstat)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___XSTAT TSAN_INTERCEPT(__xstat) #else #define TSAN_MAYBE_INTERCEPT___XSTAT #endif TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) { #if SANITIZER_FREEBSD SCOPED_TSAN_INTERCEPTOR(stat, path, buf); return REAL(stat)(path, buf); #else SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf); return REAL(__xstat)(0, path, buf); #endif } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf); return REAL(__xstat64)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___XSTAT64 TSAN_INTERCEPT(__xstat64) #else #define TSAN_MAYBE_INTERCEPT___XSTAT64 #endif #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf); return REAL(__xstat64)(0, path, buf); } #define TSAN_MAYBE_INTERCEPT_STAT64 TSAN_INTERCEPT(stat64) #else #define TSAN_MAYBE_INTERCEPT_STAT64 #endif #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf); return REAL(__lxstat)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___LXSTAT TSAN_INTERCEPT(__lxstat) #else #define TSAN_MAYBE_INTERCEPT___LXSTAT #endif TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) { #if SANITIZER_FREEBSD SCOPED_TSAN_INTERCEPTOR(lstat, path, buf); return REAL(lstat)(path, buf); #else SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf); return REAL(__lxstat)(0, path, buf); #endif } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf); return REAL(__lxstat64)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___LXSTAT64 TSAN_INTERCEPT(__lxstat64) #else #define TSAN_MAYBE_INTERCEPT___LXSTAT64 #endif #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf); return REAL(__lxstat64)(0, path, buf); } #define TSAN_MAYBE_INTERCEPT_LSTAT64 TSAN_INTERCEPT(lstat64) #else #define TSAN_MAYBE_INTERCEPT_LSTAT64 #endif #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) { SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf); if (fd > 0) FdAccess(thr, pc, fd); return REAL(__fxstat)(version, fd, buf); } #define TSAN_MAYBE_INTERCEPT___FXSTAT TSAN_INTERCEPT(__fxstat) #else #define TSAN_MAYBE_INTERCEPT___FXSTAT #endif TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) { #if SANITIZER_FREEBSD SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf); if (fd > 0) FdAccess(thr, pc, fd); return REAL(fstat)(fd, buf); #else SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf); if (fd > 0) FdAccess(thr, pc, fd); return REAL(__fxstat)(0, fd, buf); #endif } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) { SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf); if (fd > 0) FdAccess(thr, pc, fd); return REAL(__fxstat64)(version, fd, buf); } #define TSAN_MAYBE_INTERCEPT___FXSTAT64 TSAN_INTERCEPT(__fxstat64) #else #define TSAN_MAYBE_INTERCEPT___FXSTAT64 #endif #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) { SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf); if (fd > 0) FdAccess(thr, pc, fd); return REAL(__fxstat64)(0, fd, buf); } #define TSAN_MAYBE_INTERCEPT_FSTAT64 TSAN_INTERCEPT(fstat64) #else #define TSAN_MAYBE_INTERCEPT_FSTAT64 #endif TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) { SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode); int fd = REAL(open)(name, flags, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); return fd; } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) { SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode); int fd = REAL(open64)(name, flags, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); return fd; } #define TSAN_MAYBE_INTERCEPT_OPEN64 TSAN_INTERCEPT(open64) #else #define TSAN_MAYBE_INTERCEPT_OPEN64 #endif TSAN_INTERCEPTOR(int, creat, const char *name, int mode) { SCOPED_TSAN_INTERCEPTOR(creat, name, mode); int fd = REAL(creat)(name, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); return fd; } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) { SCOPED_TSAN_INTERCEPTOR(creat64, name, mode); int fd = REAL(creat64)(name, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); return fd; } #define TSAN_MAYBE_INTERCEPT_CREAT64 TSAN_INTERCEPT(creat64) #else #define TSAN_MAYBE_INTERCEPT_CREAT64 #endif TSAN_INTERCEPTOR(int, dup, int oldfd) { SCOPED_TSAN_INTERCEPTOR(dup, oldfd); int newfd = REAL(dup)(oldfd); if (oldfd >= 0 && newfd >= 0 && newfd != oldfd) FdDup(thr, pc, oldfd, newfd); return newfd; } TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) { SCOPED_TSAN_INTERCEPTOR(dup2, oldfd, newfd); int newfd2 = REAL(dup2)(oldfd, newfd); if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd) FdDup(thr, pc, oldfd, newfd2); return newfd2; } TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) { SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags); int newfd2 = REAL(dup3)(oldfd, newfd, flags); if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd) FdDup(thr, pc, oldfd, newfd2); return newfd2; } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) { SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags); int fd = REAL(eventfd)(initval, flags); if (fd >= 0) FdEventCreate(thr, pc, fd); return fd; } #define TSAN_MAYBE_INTERCEPT_EVENTFD TSAN_INTERCEPT(eventfd) #else #define TSAN_MAYBE_INTERCEPT_EVENTFD #endif #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) { SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags); if (fd >= 0) FdClose(thr, pc, fd); fd = REAL(signalfd)(fd, mask, flags); if (fd >= 0) FdSignalCreate(thr, pc, fd); return fd; } #define TSAN_MAYBE_INTERCEPT_SIGNALFD TSAN_INTERCEPT(signalfd) #else #define TSAN_MAYBE_INTERCEPT_SIGNALFD #endif #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, inotify_init, int fake) { SCOPED_TSAN_INTERCEPTOR(inotify_init, fake); int fd = REAL(inotify_init)(fake); if (fd >= 0) FdInotifyCreate(thr, pc, fd); return fd; } #define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT TSAN_INTERCEPT(inotify_init) #else #define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT #endif #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, inotify_init1, int flags) { SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags); int fd = REAL(inotify_init1)(flags); if (fd >= 0) FdInotifyCreate(thr, pc, fd); return fd; } #define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1 TSAN_INTERCEPT(inotify_init1) #else #define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1 #endif TSAN_INTERCEPTOR(int, socket, int domain, int type, int protocol) { SCOPED_TSAN_INTERCEPTOR(socket, domain, type, protocol); int fd = REAL(socket)(domain, type, protocol); if (fd >= 0) FdSocketCreate(thr, pc, fd); return fd; } TSAN_INTERCEPTOR(int, socketpair, int domain, int type, int protocol, int *fd) { SCOPED_TSAN_INTERCEPTOR(socketpair, domain, type, protocol, fd); int res = REAL(socketpair)(domain, type, protocol, fd); if (res == 0 && fd[0] >= 0 && fd[1] >= 0) FdPipeCreate(thr, pc, fd[0], fd[1]); return res; } TSAN_INTERCEPTOR(int, connect, int fd, void *addr, unsigned addrlen) { SCOPED_TSAN_INTERCEPTOR(connect, fd, addr, addrlen); FdSocketConnecting(thr, pc, fd); int res = REAL(connect)(fd, addr, addrlen); if (res == 0 && fd >= 0) FdSocketConnect(thr, pc, fd); return res; } TSAN_INTERCEPTOR(int, bind, int fd, void *addr, unsigned addrlen) { SCOPED_TSAN_INTERCEPTOR(bind, fd, addr, addrlen); int res = REAL(bind)(fd, addr, addrlen); if (fd > 0 && res == 0) FdAccess(thr, pc, fd); return res; } TSAN_INTERCEPTOR(int, listen, int fd, int backlog) { SCOPED_TSAN_INTERCEPTOR(listen, fd, backlog); int res = REAL(listen)(fd, backlog); if (fd > 0 && res == 0) FdAccess(thr, pc, fd); return res; } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, epoll_create, int size) { SCOPED_TSAN_INTERCEPTOR(epoll_create, size); int fd = REAL(epoll_create)(size); if (fd >= 0) FdPollCreate(thr, pc, fd); return fd; } #define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE TSAN_INTERCEPT(epoll_create) #else #define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE #endif #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, epoll_create1, int flags) { SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags); int fd = REAL(epoll_create1)(flags); if (fd >= 0) FdPollCreate(thr, pc, fd); return fd; } #define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1 TSAN_INTERCEPT(epoll_create1) #else #define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1 #endif TSAN_INTERCEPTOR(int, close, int fd) { SCOPED_TSAN_INTERCEPTOR(close, fd); if (fd >= 0) FdClose(thr, pc, fd); return REAL(close)(fd); } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __close, int fd) { SCOPED_TSAN_INTERCEPTOR(__close, fd); if (fd >= 0) FdClose(thr, pc, fd); return REAL(__close)(fd); } #define TSAN_MAYBE_INTERCEPT___CLOSE TSAN_INTERCEPT(__close) #else #define TSAN_MAYBE_INTERCEPT___CLOSE #endif // glibc guts #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) { SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr); int fds[64]; int cnt = ExtractResolvFDs(state, fds, ARRAY_SIZE(fds)); for (int i = 0; i < cnt; i++) { if (fds[i] > 0) FdClose(thr, pc, fds[i]); } REAL(__res_iclose)(state, free_addr); } #define TSAN_MAYBE_INTERCEPT___RES_ICLOSE TSAN_INTERCEPT(__res_iclose) #else #define TSAN_MAYBE_INTERCEPT___RES_ICLOSE #endif TSAN_INTERCEPTOR(int, pipe, int *pipefd) { SCOPED_TSAN_INTERCEPTOR(pipe, pipefd); int res = REAL(pipe)(pipefd); if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0) FdPipeCreate(thr, pc, pipefd[0], pipefd[1]); return res; } TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) { SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags); int res = REAL(pipe2)(pipefd, flags); if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0) FdPipeCreate(thr, pc, pipefd[0], pipefd[1]); return res; } TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags); if (fd >= 0) { FdAccess(thr, pc, fd); FdRelease(thr, pc, fd); } int res = REAL(send)(fd, buf, len, flags); return res; } TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) { SCOPED_TSAN_INTERCEPTOR(sendmsg, fd, msg, flags); if (fd >= 0) { FdAccess(thr, pc, fd); FdRelease(thr, pc, fd); } int res = REAL(sendmsg)(fd, msg, flags); return res; } TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(recv, fd, buf, len, flags); if (fd >= 0) FdAccess(thr, pc, fd); int res = REAL(recv)(fd, buf, len, flags); if (res >= 0 && fd >= 0) { FdAcquire(thr, pc, fd); } return res; } TSAN_INTERCEPTOR(int, unlink, char *path) { SCOPED_TSAN_INTERCEPTOR(unlink, path); Release(thr, pc, File2addr(path)); int res = REAL(unlink)(path); return res; } TSAN_INTERCEPTOR(void*, tmpfile, int fake) { SCOPED_TSAN_INTERCEPTOR(tmpfile, fake); void *res = REAL(tmpfile)(fake); if (res) { int fd = fileno_unlocked(res); if (fd >= 0) FdFileCreate(thr, pc, fd); } return res; } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(void*, tmpfile64, int fake) { SCOPED_TSAN_INTERCEPTOR(tmpfile64, fake); void *res = REAL(tmpfile64)(fake); if (res) { int fd = fileno_unlocked(res); if (fd >= 0) FdFileCreate(thr, pc, fd); } return res; } #define TSAN_MAYBE_INTERCEPT_TMPFILE64 TSAN_INTERCEPT(tmpfile64) #else #define TSAN_MAYBE_INTERCEPT_TMPFILE64 #endif TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f); MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true); } return REAL(fread)(ptr, size, nmemb, f); } TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f); MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false); } return REAL(fwrite)(p, size, nmemb, f); } static void FlushStreams() { // Flushing all the streams here may freeze the process if a child thread is // performing file stream operations at the same time. REAL(fflush)(stdout); REAL(fflush)(stderr); } TSAN_INTERCEPTOR(void, abort, int fake) { SCOPED_TSAN_INTERCEPTOR(abort, fake); FlushStreams(); REAL(abort)(fake); } TSAN_INTERCEPTOR(int, puts, const char *s) { SCOPED_TSAN_INTERCEPTOR(puts, s); MemoryAccessRange(thr, pc, (uptr)s, internal_strlen(s), false); return REAL(puts)(s); } TSAN_INTERCEPTOR(int, rmdir, char *path) { SCOPED_TSAN_INTERCEPTOR(rmdir, path); Release(thr, pc, Dir2addr(path)); int res = REAL(rmdir)(path); return res; } -TSAN_INTERCEPTOR(void*, opendir, char *path) { - SCOPED_TSAN_INTERCEPTOR(opendir, path); - void *res = REAL(opendir)(path); - if (res != 0) - Acquire(thr, pc, Dir2addr(path)); - return res; +TSAN_INTERCEPTOR(int, closedir, void *dirp) { + SCOPED_TSAN_INTERCEPTOR(closedir, dirp); + int fd = dirfd(dirp); + FdClose(thr, pc, fd); + return REAL(closedir)(dirp); } #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) { SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev); if (epfd >= 0) FdAccess(thr, pc, epfd); if (epfd >= 0 && fd >= 0) FdAccess(thr, pc, fd); if (op == EPOLL_CTL_ADD && epfd >= 0) FdRelease(thr, pc, epfd); int res = REAL(epoll_ctl)(epfd, op, fd, ev); return res; } #define TSAN_MAYBE_INTERCEPT_EPOLL_CTL TSAN_INTERCEPT(epoll_ctl) #else #define TSAN_MAYBE_INTERCEPT_EPOLL_CTL #endif #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) { SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout); if (epfd >= 0) FdAccess(thr, pc, epfd); int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout); if (res > 0 && epfd >= 0) FdAcquire(thr, pc, epfd); return res; } #define TSAN_MAYBE_INTERCEPT_EPOLL_WAIT TSAN_INTERCEPT(epoll_wait) #else #define TSAN_MAYBE_INTERCEPT_EPOLL_WAIT #endif namespace __tsan { static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire, bool sigact, int sig, my_siginfo_t *info, void *uctx) { if (acquire) Acquire(thr, 0, (uptr)&sigactions[sig]); // Ensure that the handler does not spoil errno. const int saved_errno = errno; errno = 99; - // Need to remember pc before the call, because the handler can reset it. - uptr pc = sigact ? + // This code races with sigaction. Be careful to not read sa_sigaction twice. + // Also need to remember pc for reporting before the call, + // because the handler can reset it. + volatile uptr pc = sigact ? (uptr)sigactions[sig].sa_sigaction : (uptr)sigactions[sig].sa_handler; - pc += 1; // return address is expected, OutputReport() will undo this - if (sigact) - sigactions[sig].sa_sigaction(sig, info, uctx); - else - sigactions[sig].sa_handler(sig); + if (pc != (uptr)SIG_DFL && pc != (uptr)SIG_IGN) { + if (sigact) + ((sigactionhandler_t)pc)(sig, info, uctx); + else + ((sighandler_t)pc)(sig); + } // We do not detect errno spoiling for SIGTERM, // because some SIGTERM handlers do spoil errno but reraise SIGTERM, // tsan reports false positive in such case. // It's difficult to properly detect this situation (reraise), // because in async signal processing case (when handler is called directly // from rtl_generic_sighandler) we have not yet received the reraised // signal; and it looks too fragile to intercept all ways to reraise a signal. if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) { VarSizeStackTrace stack; - ObtainCurrentStack(thr, pc, &stack); + // Add 1 to pc because return address is expected, + // OutputReport() will undo this. + ObtainCurrentStack(thr, pc + 1, &stack); ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeErrnoInSignal); if (!IsFiredSuppression(ctx, rep, stack)) { rep.AddStack(stack, true); OutputReport(thr, rep); } } errno = saved_errno; } void ProcessPendingSignals(ThreadState *thr) { SignalContext *sctx = SigCtx(thr); if (sctx == 0 || atomic_load(&sctx->have_pending_signals, memory_order_relaxed) == 0) return; atomic_store(&sctx->have_pending_signals, 0, memory_order_relaxed); atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed); // These are too big for stack. static THREADLOCAL __sanitizer_sigset_t emptyset, oldset; REAL(sigfillset)(&emptyset); pthread_sigmask(SIG_SETMASK, &emptyset, &oldset); for (int sig = 0; sig < kSigCount; sig++) { SignalDesc *signal = &sctx->pending_signals[sig]; if (signal->armed) { signal->armed = false; - if (sigactions[sig].sa_handler != SIG_DFL - && sigactions[sig].sa_handler != SIG_IGN) { - CallUserSignalHandler(thr, false, true, signal->sigaction, - sig, &signal->siginfo, &signal->ctx); - } + CallUserSignalHandler(thr, false, true, signal->sigaction, sig, + &signal->siginfo, &signal->ctx); } } pthread_sigmask(SIG_SETMASK, &oldset, 0); atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed); } } // namespace __tsan static bool is_sync_signal(SignalContext *sctx, int sig) { return sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS || // If we are sending signal to ourselves, we must process it now. (sctx && sig == sctx->int_signal_send); } void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig, my_siginfo_t *info, void *ctx) { ThreadState *thr = cur_thread(); SignalContext *sctx = SigCtx(thr); if (sig < 0 || sig >= kSigCount) { VPrintf(1, "ThreadSanitizer: ignoring signal %d\n", sig); return; } // Don't mess with synchronous signals. const bool sync = is_sync_signal(sctx, sig); if (sync || // If we are in blocking function, we can safely process it now // (but check if we are in a recursive interceptor, // i.e. pthread_join()->munmap()). (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed))) { atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed); if (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed)) { // We ignore interceptors in blocking functions, // temporary enbled them again while we are calling user function. int const i = thr->ignore_interceptors; thr->ignore_interceptors = 0; atomic_store(&sctx->in_blocking_func, 0, memory_order_relaxed); CallUserSignalHandler(thr, sync, true, sigact, sig, info, ctx); thr->ignore_interceptors = i; atomic_store(&sctx->in_blocking_func, 1, memory_order_relaxed); } else { // Be very conservative with when we do acquire in this case. // It's unsafe to do acquire in async handlers, because ThreadState // can be in inconsistent state. // SIGSYS looks relatively safe -- it's synchronous and can actually // need some global state. bool acq = (sig == SIGSYS); CallUserSignalHandler(thr, sync, acq, sigact, sig, info, ctx); } atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed); return; } if (sctx == 0) return; SignalDesc *signal = &sctx->pending_signals[sig]; if (signal->armed == false) { signal->armed = true; signal->sigaction = sigact; if (info) internal_memcpy(&signal->siginfo, info, sizeof(*info)); if (ctx) internal_memcpy(&signal->ctx, ctx, sizeof(signal->ctx)); atomic_store(&sctx->have_pending_signals, 1, memory_order_relaxed); } } static void rtl_sighandler(int sig) { rtl_generic_sighandler(false, sig, 0, 0); } static void rtl_sigaction(int sig, my_siginfo_t *info, void *ctx) { rtl_generic_sighandler(true, sig, info, ctx); } TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) { SCOPED_TSAN_INTERCEPTOR(sigaction, sig, act, old); if (old) internal_memcpy(old, &sigactions[sig], sizeof(*old)); if (act == 0) return 0; - internal_memcpy(&sigactions[sig], act, sizeof(*act)); + // Copy act into sigactions[sig]. + // Can't use struct copy, because compiler can emit call to memcpy. + // Can't use internal_memcpy, because it copies byte-by-byte, + // and signal handler reads the sa_handler concurrently. It it can read + // some bytes from old value and some bytes from new value. + // Use volatile to prevent insertion of memcpy. + sigactions[sig].sa_handler = *(volatile sighandler_t*)&act->sa_handler; + sigactions[sig].sa_flags = *(volatile int*)&act->sa_flags; + internal_memcpy(&sigactions[sig].sa_mask, &act->sa_mask, + sizeof(sigactions[sig].sa_mask)); +#if !SANITIZER_FREEBSD + sigactions[sig].sa_restorer = act->sa_restorer; +#endif sigaction_t newact; internal_memcpy(&newact, act, sizeof(newact)); REAL(sigfillset)(&newact.sa_mask); if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL) { if (newact.sa_flags & SA_SIGINFO) newact.sa_sigaction = rtl_sigaction; else newact.sa_handler = rtl_sighandler; } ReleaseStore(thr, pc, (uptr)&sigactions[sig]); int res = REAL(sigaction)(sig, &newact, 0); return res; } TSAN_INTERCEPTOR(sighandler_t, signal, int sig, sighandler_t h) { sigaction_t act; act.sa_handler = h; REAL(memset)(&act.sa_mask, -1, sizeof(act.sa_mask)); act.sa_flags = 0; sigaction_t old; int res = sigaction(sig, &act, &old); if (res) return SIG_ERR; return old.sa_handler; } TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) { SCOPED_TSAN_INTERCEPTOR(sigsuspend, mask); return REAL(sigsuspend)(mask); } TSAN_INTERCEPTOR(int, raise, int sig) { SCOPED_TSAN_INTERCEPTOR(raise, sig); SignalContext *sctx = SigCtx(thr); CHECK_NE(sctx, 0); int prev = sctx->int_signal_send; sctx->int_signal_send = sig; int res = REAL(raise)(sig); CHECK_EQ(sctx->int_signal_send, sig); sctx->int_signal_send = prev; return res; } TSAN_INTERCEPTOR(int, kill, int pid, int sig) { SCOPED_TSAN_INTERCEPTOR(kill, pid, sig); SignalContext *sctx = SigCtx(thr); CHECK_NE(sctx, 0); int prev = sctx->int_signal_send; if (pid == (int)internal_getpid()) { sctx->int_signal_send = sig; } int res = REAL(kill)(pid, sig); if (pid == (int)internal_getpid()) { CHECK_EQ(sctx->int_signal_send, sig); sctx->int_signal_send = prev; } return res; } TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) { SCOPED_TSAN_INTERCEPTOR(pthread_kill, tid, sig); SignalContext *sctx = SigCtx(thr); CHECK_NE(sctx, 0); int prev = sctx->int_signal_send; if (tid == pthread_self()) { sctx->int_signal_send = sig; } int res = REAL(pthread_kill)(tid, sig); if (tid == pthread_self()) { CHECK_EQ(sctx->int_signal_send, sig); sctx->int_signal_send = prev; } return res; } TSAN_INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { SCOPED_TSAN_INTERCEPTOR(gettimeofday, tv, tz); // It's intercepted merely to process pending signals. return REAL(gettimeofday)(tv, tz); } TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service, void *hints, void *rv) { SCOPED_TSAN_INTERCEPTOR(getaddrinfo, node, service, hints, rv); // We miss atomic synchronization in getaddrinfo, // and can report false race between malloc and free // inside of getaddrinfo. So ignore memory accesses. ThreadIgnoreBegin(thr, pc); int res = REAL(getaddrinfo)(node, service, hints, rv); ThreadIgnoreEnd(thr, pc); return res; } TSAN_INTERCEPTOR(int, fork, int fake) { if (cur_thread()->in_symbolizer) return REAL(fork)(fake); SCOPED_INTERCEPTOR_RAW(fork, fake); ForkBefore(thr, pc); int pid = REAL(fork)(fake); if (pid == 0) { // child ForkChildAfter(thr, pc); FdOnFork(thr, pc); } else if (pid > 0) { // parent ForkParentAfter(thr, pc); } else { // error ForkParentAfter(thr, pc); } return pid; } TSAN_INTERCEPTOR(int, vfork, int fake) { // Some programs (e.g. openjdk) call close for all file descriptors // in the child process. Under tsan it leads to false positives, because // address space is shared, so the parent process also thinks that // the descriptors are closed (while they are actually not). // This leads to false positives due to missed synchronization. // Strictly saying this is undefined behavior, because vfork child is not // allowed to call any functions other than exec/exit. But this is what // openjdk does, so we want to handle it. // We could disable interceptors in the child process. But it's not possible // to simply intercept and wrap vfork, because vfork child is not allowed // to return from the function that calls vfork, and that's exactly what // we would do. So this would require some assembly trickery as well. // Instead we simply turn vfork into fork. return WRAP(fork)(fake); } static int OnExit(ThreadState *thr) { int status = Finalize(thr); FlushStreams(); return status; } struct TsanInterceptorContext { ThreadState *thr; const uptr caller_pc; const uptr pc; }; static void HandleRecvmsg(ThreadState *thr, uptr pc, __sanitizer_msghdr *msg) { int fds[64]; int cnt = ExtractRecvmsgFDs(msg, fds, ARRAY_SIZE(fds)); for (int i = 0; i < cnt; i++) FdEventCreate(thr, pc, fds[i]); } #include "sanitizer_common/sanitizer_platform_interceptors.h" // Causes interceptor recursion (getaddrinfo() and fopen()) #undef SANITIZER_INTERCEPT_GETADDRINFO // There interceptors do not seem to be strictly necessary for tsan. // But we see cases where the interceptors consume 70% of execution time. // Memory blocks passed to fgetgrent_r are "written to" by tsan several times. // First, there is some recursion (getgrnam_r calls fgetgrent_r), and each // function "writes to" the buffer. Then, the same memory is "written to" // twice, first as buf and then as pwbufp (both of them refer to the same // addresses). #undef SANITIZER_INTERCEPT_GETPWENT #undef SANITIZER_INTERCEPT_GETPWENT_R #undef SANITIZER_INTERCEPT_FGETPWENT #undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS #undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS +// __tls_get_addr can be called with mis-aligned stack due to: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066 +// There are two potential issues: +// 1. Sanitizer code contains a MOVDQA spill (it does not seem to be the case +// right now). or 2. ProcessPendingSignal calls user handler which contains +// MOVDQA spill (this happens right now). +// Since the interceptor only initializes memory for msan, the simplest solution +// is to disable the interceptor in tsan (other sanitizers do not call +// signal handlers from COMMON_INTERCEPTOR_ENTER). +#undef SANITIZER_INTERCEPT_TLS_GET_ADDR #define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \ ((TsanInterceptorContext *)ctx)->pc, (uptr)ptr, size, \ true) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ MemoryAccessRange(((TsanInterceptorContext *) ctx)->thr, \ ((TsanInterceptorContext *) ctx)->pc, (uptr) ptr, size, \ false) #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__); \ TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ ctx = (void *)&_ctx; \ (void) ctx; #define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, func, ...) \ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \ TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ ctx = (void *)&_ctx; \ (void) ctx; #define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) \ Acquire(thr, pc, File2addr(path)); \ if (file) { \ int fd = fileno_unlocked(file); \ if (fd >= 0) FdFileCreate(thr, pc, fd); \ } #define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) \ if (file) { \ int fd = fileno_unlocked(file); \ if (fd >= 0) FdClose(thr, pc, fd); \ } -#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res) \ +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ libignore()->OnLibraryLoaded(filename) #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() \ libignore()->OnLibraryUnloaded() +#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ + Acquire(((TsanInterceptorContext *) ctx)->thr, pc, Dir2addr(path)) + #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd) #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ FdRelease(((TsanInterceptorContext *) ctx)->thr, pc, fd) #define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \ FdAccess(((TsanInterceptorContext *) ctx)->thr, pc, fd) #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ FdSocketAccept(((TsanInterceptorContext *) ctx)->thr, pc, fd, newfd) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name) #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ __tsan::ctx->thread_registry->SetThreadNameByUserId(thread, name) #define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) \ OnExit(((TsanInterceptorContext *) ctx)->thr) #define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \ MutexLock(((TsanInterceptorContext *)ctx)->thr, \ ((TsanInterceptorContext *)ctx)->pc, (uptr)m) #define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \ MutexUnlock(((TsanInterceptorContext *)ctx)->thr, \ ((TsanInterceptorContext *)ctx)->pc, (uptr)m) #define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \ MutexRepair(((TsanInterceptorContext *)ctx)->thr, \ ((TsanInterceptorContext *)ctx)->pc, (uptr)m) #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \ HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \ ((TsanInterceptorContext *)ctx)->pc, msg) #include "sanitizer_common/sanitizer_common_interceptors.inc" #define TSAN_SYSCALL() \ ThreadState *thr = cur_thread(); \ if (thr->ignore_interceptors) \ return; \ ScopedSyscall scoped_syscall(thr) \ /**/ struct ScopedSyscall { ThreadState *thr; explicit ScopedSyscall(ThreadState *thr) : thr(thr) { Initialize(thr); } ~ScopedSyscall() { ProcessPendingSignals(thr); } }; static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) { TSAN_SYSCALL(); MemoryAccessRange(thr, pc, p, s, write); } static void syscall_acquire(uptr pc, uptr addr) { TSAN_SYSCALL(); Acquire(thr, pc, addr); DPrintf("syscall_acquire(%p)\n", addr); } static void syscall_release(uptr pc, uptr addr) { TSAN_SYSCALL(); DPrintf("syscall_release(%p)\n", addr); Release(thr, pc, addr); } static void syscall_fd_close(uptr pc, int fd) { TSAN_SYSCALL(); FdClose(thr, pc, fd); } static USED void syscall_fd_acquire(uptr pc, int fd) { TSAN_SYSCALL(); FdAcquire(thr, pc, fd); DPrintf("syscall_fd_acquire(%p)\n", fd); } static USED void syscall_fd_release(uptr pc, int fd) { TSAN_SYSCALL(); DPrintf("syscall_fd_release(%p)\n", fd); FdRelease(thr, pc, fd); } static void syscall_pre_fork(uptr pc) { TSAN_SYSCALL(); ForkBefore(thr, pc); } static void syscall_post_fork(uptr pc, int pid) { TSAN_SYSCALL(); if (pid == 0) { // child ForkChildAfter(thr, pc); FdOnFork(thr, pc); } else if (pid > 0) { // parent ForkParentAfter(thr, pc); } else { // error ForkParentAfter(thr, pc); } } #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \ syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false) #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true) #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ do { \ (void)(p); \ (void)(s); \ } while (false) #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ do { \ (void)(p); \ (void)(s); \ } while (false) #define COMMON_SYSCALL_ACQUIRE(addr) \ syscall_acquire(GET_CALLER_PC(), (uptr)(addr)) #define COMMON_SYSCALL_RELEASE(addr) \ syscall_release(GET_CALLER_PC(), (uptr)(addr)) #define COMMON_SYSCALL_FD_CLOSE(fd) syscall_fd_close(GET_CALLER_PC(), fd) #define COMMON_SYSCALL_FD_ACQUIRE(fd) syscall_fd_acquire(GET_CALLER_PC(), fd) #define COMMON_SYSCALL_FD_RELEASE(fd) syscall_fd_release(GET_CALLER_PC(), fd) #define COMMON_SYSCALL_PRE_FORK() \ syscall_pre_fork(GET_CALLER_PC()) #define COMMON_SYSCALL_POST_FORK(res) \ syscall_post_fork(GET_CALLER_PC(), res) #include "sanitizer_common/sanitizer_common_syscalls.inc" namespace __tsan { static void finalize(void *arg) { ThreadState *thr = cur_thread(); int status = Finalize(thr); // Make sure the output is not lost. FlushStreams(); if (status) REAL(_exit)(status); } static void unreachable() { Report("FATAL: ThreadSanitizer: unreachable called\n"); Die(); } void InitializeInterceptors() { // We need to setup it early, because functions like dlsym() can call it. REAL(memset) = internal_memset; REAL(memcpy) = internal_memcpy; REAL(memcmp) = internal_memcmp; // Instruct libc malloc to consume less memory. #if !SANITIZER_FREEBSD mallopt(1, 0); // M_MXFAST mallopt(-3, 32*1024); // M_MMAP_THRESHOLD #endif InitializeCommonInterceptors(); // We can not use TSAN_INTERCEPT to get setjmp addr, // because it does &setjmp and setjmp is not present in some versions of libc. using __interception::GetRealFunctionAddress; GetRealFunctionAddress("setjmp", (uptr*)&REAL(setjmp), 0, 0); GetRealFunctionAddress("_setjmp", (uptr*)&REAL(_setjmp), 0, 0); GetRealFunctionAddress("sigsetjmp", (uptr*)&REAL(sigsetjmp), 0, 0); GetRealFunctionAddress("__sigsetjmp", (uptr*)&REAL(__sigsetjmp), 0, 0); TSAN_INTERCEPT(longjmp); TSAN_INTERCEPT(siglongjmp); TSAN_INTERCEPT(malloc); TSAN_INTERCEPT(__libc_memalign); TSAN_INTERCEPT(calloc); TSAN_INTERCEPT(realloc); TSAN_INTERCEPT(free); TSAN_INTERCEPT(cfree); TSAN_INTERCEPT(mmap); TSAN_MAYBE_INTERCEPT_MMAP64; TSAN_INTERCEPT(munmap); TSAN_MAYBE_INTERCEPT_MEMALIGN; TSAN_INTERCEPT(valloc); TSAN_MAYBE_INTERCEPT_PVALLOC; TSAN_INTERCEPT(posix_memalign); TSAN_INTERCEPT(strlen); TSAN_INTERCEPT(memset); TSAN_INTERCEPT(memcpy); TSAN_INTERCEPT(memmove); TSAN_INTERCEPT(memcmp); TSAN_INTERCEPT(strchr); TSAN_INTERCEPT(strchrnul); TSAN_INTERCEPT(strrchr); TSAN_INTERCEPT(strcpy); // NOLINT TSAN_INTERCEPT(strncpy); TSAN_INTERCEPT(strstr); TSAN_INTERCEPT(strdup); TSAN_INTERCEPT(pthread_create); TSAN_INTERCEPT(pthread_join); TSAN_INTERCEPT(pthread_detach); TSAN_INTERCEPT_VER(pthread_cond_init, "GLIBC_2.3.2"); TSAN_INTERCEPT_VER(pthread_cond_signal, "GLIBC_2.3.2"); TSAN_INTERCEPT_VER(pthread_cond_broadcast, "GLIBC_2.3.2"); TSAN_INTERCEPT_VER(pthread_cond_wait, "GLIBC_2.3.2"); TSAN_INTERCEPT_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); TSAN_INTERCEPT_VER(pthread_cond_destroy, "GLIBC_2.3.2"); TSAN_INTERCEPT(pthread_mutex_init); TSAN_INTERCEPT(pthread_mutex_destroy); TSAN_INTERCEPT(pthread_mutex_trylock); TSAN_INTERCEPT(pthread_mutex_timedlock); TSAN_INTERCEPT(pthread_spin_init); TSAN_INTERCEPT(pthread_spin_destroy); TSAN_INTERCEPT(pthread_spin_lock); TSAN_INTERCEPT(pthread_spin_trylock); TSAN_INTERCEPT(pthread_spin_unlock); TSAN_INTERCEPT(pthread_rwlock_init); TSAN_INTERCEPT(pthread_rwlock_destroy); TSAN_INTERCEPT(pthread_rwlock_rdlock); TSAN_INTERCEPT(pthread_rwlock_tryrdlock); TSAN_INTERCEPT(pthread_rwlock_timedrdlock); TSAN_INTERCEPT(pthread_rwlock_wrlock); TSAN_INTERCEPT(pthread_rwlock_trywrlock); TSAN_INTERCEPT(pthread_rwlock_timedwrlock); TSAN_INTERCEPT(pthread_rwlock_unlock); TSAN_INTERCEPT(pthread_barrier_init); TSAN_INTERCEPT(pthread_barrier_destroy); TSAN_INTERCEPT(pthread_barrier_wait); TSAN_INTERCEPT(pthread_once); TSAN_INTERCEPT(sem_init); TSAN_INTERCEPT(sem_destroy); TSAN_INTERCEPT(sem_wait); TSAN_INTERCEPT(sem_trywait); TSAN_INTERCEPT(sem_timedwait); TSAN_INTERCEPT(sem_post); TSAN_INTERCEPT(sem_getvalue); TSAN_INTERCEPT(stat); TSAN_MAYBE_INTERCEPT___XSTAT; TSAN_MAYBE_INTERCEPT_STAT64; TSAN_MAYBE_INTERCEPT___XSTAT64; TSAN_INTERCEPT(lstat); TSAN_MAYBE_INTERCEPT___LXSTAT; TSAN_MAYBE_INTERCEPT_LSTAT64; TSAN_MAYBE_INTERCEPT___LXSTAT64; TSAN_INTERCEPT(fstat); TSAN_MAYBE_INTERCEPT___FXSTAT; TSAN_MAYBE_INTERCEPT_FSTAT64; TSAN_MAYBE_INTERCEPT___FXSTAT64; TSAN_INTERCEPT(open); TSAN_MAYBE_INTERCEPT_OPEN64; TSAN_INTERCEPT(creat); TSAN_MAYBE_INTERCEPT_CREAT64; TSAN_INTERCEPT(dup); TSAN_INTERCEPT(dup2); TSAN_INTERCEPT(dup3); TSAN_MAYBE_INTERCEPT_EVENTFD; TSAN_MAYBE_INTERCEPT_SIGNALFD; TSAN_MAYBE_INTERCEPT_INOTIFY_INIT; TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1; TSAN_INTERCEPT(socket); TSAN_INTERCEPT(socketpair); TSAN_INTERCEPT(connect); TSAN_INTERCEPT(bind); TSAN_INTERCEPT(listen); TSAN_MAYBE_INTERCEPT_EPOLL_CREATE; TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1; TSAN_INTERCEPT(close); TSAN_MAYBE_INTERCEPT___CLOSE; TSAN_MAYBE_INTERCEPT___RES_ICLOSE; TSAN_INTERCEPT(pipe); TSAN_INTERCEPT(pipe2); TSAN_INTERCEPT(send); TSAN_INTERCEPT(sendmsg); TSAN_INTERCEPT(recv); TSAN_INTERCEPT(unlink); TSAN_INTERCEPT(tmpfile); TSAN_MAYBE_INTERCEPT_TMPFILE64; TSAN_INTERCEPT(fread); TSAN_INTERCEPT(fwrite); TSAN_INTERCEPT(abort); TSAN_INTERCEPT(puts); TSAN_INTERCEPT(rmdir); - TSAN_INTERCEPT(opendir); + TSAN_INTERCEPT(closedir); TSAN_MAYBE_INTERCEPT_EPOLL_CTL; TSAN_MAYBE_INTERCEPT_EPOLL_WAIT; TSAN_INTERCEPT(sigaction); TSAN_INTERCEPT(signal); TSAN_INTERCEPT(sigsuspend); TSAN_INTERCEPT(raise); TSAN_INTERCEPT(kill); TSAN_INTERCEPT(pthread_kill); TSAN_INTERCEPT(sleep); TSAN_INTERCEPT(usleep); TSAN_INTERCEPT(nanosleep); TSAN_INTERCEPT(gettimeofday); TSAN_INTERCEPT(getaddrinfo); TSAN_INTERCEPT(fork); TSAN_INTERCEPT(vfork); TSAN_INTERCEPT(on_exit); TSAN_INTERCEPT(__cxa_atexit); TSAN_INTERCEPT(_exit); // Need to setup it, because interceptors check that the function is resolved. // But atexit is emitted directly into the module, so can't be resolved. REAL(atexit) = (int(*)(void(*)()))unreachable; if (REAL(__cxa_atexit)(&finalize, 0, 0)) { Printf("ThreadSanitizer: failed to setup atexit callback\n"); Die(); } if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { Printf("ThreadSanitizer: failed to create thread key\n"); Die(); } FdInit(); -} - -void *internal_start_thread(void(*func)(void *arg), void *arg) { - // Start the thread with signals blocked, otherwise it can steal user signals. - __sanitizer_sigset_t set, old; - internal_sigfillset(&set); - internal_sigprocmask(SIG_SETMASK, &set, &old); - void *th; - REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg); - internal_sigprocmask(SIG_SETMASK, &old, 0); - return th; -} - -void internal_join_thread(void *th) { - REAL(pthread_join)(th, 0); } } // namespace __tsan Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface.cc (revision 279191) @@ -1,98 +1,121 @@ //===-- tsan_interface.cc -------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "tsan_interface.h" #include "tsan_interface_ann.h" #include "tsan_rtl.h" #include "sanitizer_common/sanitizer_internal_defs.h" #define CALLERPC ((uptr)__builtin_return_address(0)) using namespace __tsan; // NOLINT typedef u16 uint16_t; typedef u32 uint32_t; typedef u64 uint64_t; void __tsan_init() { Initialize(cur_thread()); } void __tsan_read16(void *addr) { MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); MemoryRead(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } void __tsan_write16(void *addr) { MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } -u16 __tsan_unaligned_read2(const uu16 *addr) { +// __tsan_unaligned_read/write calls are emitted by compiler. + +void __tsan_unaligned_read2(const void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, false, false); - return *addr; } -u32 __tsan_unaligned_read4(const uu32 *addr) { +void __tsan_unaligned_read4(const void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, false, false); - return *addr; } -u64 __tsan_unaligned_read8(const uu64 *addr) { +void __tsan_unaligned_read8(const void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, false, false); - return *addr; } -void __tsan_unaligned_write2(uu16 *addr, u16 v) { +void __tsan_unaligned_read16(const void *addr) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 16, false, false); +} + +void __tsan_unaligned_write2(void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, true, false); - *addr = v; } -void __tsan_unaligned_write4(uu32 *addr, u32 v) { +void __tsan_unaligned_write4(void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, true, false); - *addr = v; } -void __tsan_unaligned_write8(uu64 *addr, u64 v) { +void __tsan_unaligned_write8(void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, true, false); - *addr = v; } +void __tsan_unaligned_write16(void *addr) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 16, true, false); +} + +// __sanitizer_unaligned_load/store are for user instrumentation. + extern "C" { SANITIZER_INTERFACE_ATTRIBUTE -uint16_t __sanitizer_unaligned_load16(void *addr) - ALIAS("__tsan_unaligned_read2"); +u16 __sanitizer_unaligned_load16(const uu16 *addr) { + __tsan_unaligned_read2(addr); + return *addr; +} + SANITIZER_INTERFACE_ATTRIBUTE -uint32_t __sanitizer_unaligned_load32(void *addr) - ALIAS("__tsan_unaligned_read4"); +u32 __sanitizer_unaligned_load32(const uu32 *addr) { + __tsan_unaligned_read4(addr); + return *addr; +} + SANITIZER_INTERFACE_ATTRIBUTE -uint64_t __sanitizer_unaligned_load64(void *addr) - ALIAS("__tsan_unaligned_read8"); +u64 __sanitizer_unaligned_load64(const uu64 *addr) { + __tsan_unaligned_read8(addr); + return *addr; +} + SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store16(void *addr, uint16_t v) - ALIAS("__tsan_unaligned_write2"); +void __sanitizer_unaligned_store16(uu16 *addr, u16 v) { + __tsan_unaligned_write2(addr); + *addr = v; +} + SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store32(void *addr, uint32_t v) - ALIAS("__tsan_unaligned_write4"); +void __sanitizer_unaligned_store32(uu32 *addr, u32 v) { + __tsan_unaligned_write4(addr); + *addr = v; +} + SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store64(void *addr, uint64_t v) - ALIAS("__tsan_unaligned_write8"); +void __sanitizer_unaligned_store64(uu64 *addr, u64 v) { + __tsan_unaligned_write8(addr); + *addr = v; } +} // extern "C" void __tsan_acquire(void *addr) { Acquire(cur_thread(), CALLERPC, (uptr)addr); } void __tsan_release(void *addr) { Release(cur_thread(), CALLERPC, (uptr)addr); } Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface.h (revision 279191) @@ -1,67 +1,70 @@ //===-- tsan_interface.h ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // // The functions declared in this header will be inserted by the instrumentation // module. // This header can be included by the instrumented program or by TSan tests. //===----------------------------------------------------------------------===// #ifndef TSAN_INTERFACE_H #define TSAN_INTERFACE_H #include // This header should NOT include any other headers. // All functions in this header are extern "C" and start with __tsan_. #ifdef __cplusplus extern "C" { #endif // This function should be called at the very beginning of the process, // before any instrumented code is executed and before any call to malloc. SANITIZER_INTERFACE_ATTRIBUTE void __tsan_init(); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16(void *addr); -SANITIZER_INTERFACE_ATTRIBUTE u16 __tsan_unaligned_read2(const uu16 *addr); -SANITIZER_INTERFACE_ATTRIBUTE u32 __tsan_unaligned_read4(const uu32 *addr); -SANITIZER_INTERFACE_ATTRIBUTE u64 __tsan_unaligned_read8(const uu64 *addr); -SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(uu16 *addr, u16 v); -SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(uu32 *addr, u32 v); -SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(uu64 *addr, u64 v); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read2(const void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read4(const void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read8(const void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read16(const void *addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write16(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_update(void **vptr_p, void *new_val); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_entry(void *call_pc); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_func_exit(); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read_range(void *addr, unsigned long size); // NOLINT SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write_range(void *addr, unsigned long size); // NOLINT #ifdef __cplusplus } // extern "C" #endif #endif // TSAN_INTERFACE_H Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface_java.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface_java.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface_java.cc (revision 279191) @@ -1,221 +1,251 @@ //===-- tsan_interface_java.cc --------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "tsan_interface_java.h" #include "tsan_rtl.h" #include "tsan_mutex.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_procmaps.h" using namespace __tsan; // NOLINT const jptr kHeapAlignment = 8; namespace __tsan { struct JavaContext { const uptr heap_begin; const uptr heap_size; JavaContext(jptr heap_begin, jptr heap_size) : heap_begin(heap_begin) , heap_size(heap_size) { } }; class ScopedJavaFunc { public: ScopedJavaFunc(ThreadState *thr, uptr pc) : thr_(thr) { Initialize(thr_); FuncEntry(thr, pc); } ~ScopedJavaFunc() { FuncExit(thr_); // FIXME(dvyukov): process pending signals. } private: ThreadState *thr_; }; static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1]; static JavaContext *jctx; } // namespace __tsan #define SCOPED_JAVA_FUNC(func) \ ThreadState *thr = cur_thread(); \ const uptr caller_pc = GET_CALLER_PC(); \ const uptr pc = StackTrace::GetCurrentPc(); \ (void)pc; \ ScopedJavaFunc scoped(thr, caller_pc); \ /**/ void __tsan_java_init(jptr heap_begin, jptr heap_size) { SCOPED_JAVA_FUNC(__tsan_java_init); DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size); CHECK_EQ(jctx, 0); CHECK_GT(heap_begin, 0); CHECK_GT(heap_size, 0); CHECK_EQ(heap_begin % kHeapAlignment, 0); CHECK_EQ(heap_size % kHeapAlignment, 0); CHECK_LT(heap_begin, heap_begin + heap_size); jctx = new(jctx_buf) JavaContext(heap_begin, heap_size); } int __tsan_java_fini() { SCOPED_JAVA_FUNC(__tsan_java_fini); DPrintf("#%d: java_fini()\n", thr->tid); CHECK_NE(jctx, 0); // FIXME(dvyukov): this does not call atexit() callbacks. int status = Finalize(thr); DPrintf("#%d: java_fini() = %d\n", thr->tid, status); return status; } void __tsan_java_alloc(jptr ptr, jptr size) { SCOPED_JAVA_FUNC(__tsan_java_alloc); DPrintf("#%d: java_alloc(%p, %p)\n", thr->tid, ptr, size); CHECK_NE(jctx, 0); CHECK_NE(size, 0); CHECK_EQ(ptr % kHeapAlignment, 0); CHECK_EQ(size % kHeapAlignment, 0); CHECK_GE(ptr, jctx->heap_begin); CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size); OnUserAlloc(thr, pc, ptr, size, false); } void __tsan_java_free(jptr ptr, jptr size) { SCOPED_JAVA_FUNC(__tsan_java_free); DPrintf("#%d: java_free(%p, %p)\n", thr->tid, ptr, size); CHECK_NE(jctx, 0); CHECK_NE(size, 0); CHECK_EQ(ptr % kHeapAlignment, 0); CHECK_EQ(size % kHeapAlignment, 0); CHECK_GE(ptr, jctx->heap_begin); CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size); ctx->metamap.FreeRange(thr, pc, ptr, size); } void __tsan_java_move(jptr src, jptr dst, jptr size) { SCOPED_JAVA_FUNC(__tsan_java_move); DPrintf("#%d: java_move(%p, %p, %p)\n", thr->tid, src, dst, size); CHECK_NE(jctx, 0); CHECK_NE(size, 0); CHECK_EQ(src % kHeapAlignment, 0); CHECK_EQ(dst % kHeapAlignment, 0); CHECK_EQ(size % kHeapAlignment, 0); CHECK_GE(src, jctx->heap_begin); CHECK_LE(src + size, jctx->heap_begin + jctx->heap_size); CHECK_GE(dst, jctx->heap_begin); CHECK_LE(dst + size, jctx->heap_begin + jctx->heap_size); CHECK_NE(dst, src); CHECK_NE(size, 0); // Assuming it's not running concurrently with threads that do // memory accesses and mutex operations (stop-the-world phase). ctx->metamap.MoveMemory(src, dst, size); // Move shadow. u64 *s = (u64*)MemToShadow(src); u64 *d = (u64*)MemToShadow(dst); u64 *send = (u64*)MemToShadow(src + size); uptr inc = 1; if (dst > src) { s = (u64*)MemToShadow(src + size) - 1; d = (u64*)MemToShadow(dst + size) - 1; send = (u64*)MemToShadow(src) - 1; inc = -1; } for (; s != send; s += inc, d += inc) { *d = *s; *s = 0; } } void __tsan_java_finalize() { SCOPED_JAVA_FUNC(__tsan_java_finalize); DPrintf("#%d: java_mutex_finalize()\n", thr->tid); AcquireGlobal(thr, 0); } void __tsan_java_mutex_lock(jptr addr) { SCOPED_JAVA_FUNC(__tsan_java_mutex_lock); DPrintf("#%d: java_mutex_lock(%p)\n", thr->tid, addr); CHECK_NE(jctx, 0); CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); MutexCreate(thr, pc, addr, true, true, true); MutexLock(thr, pc, addr); } void __tsan_java_mutex_unlock(jptr addr) { SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock); DPrintf("#%d: java_mutex_unlock(%p)\n", thr->tid, addr); CHECK_NE(jctx, 0); CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); MutexUnlock(thr, pc, addr); } void __tsan_java_mutex_read_lock(jptr addr) { SCOPED_JAVA_FUNC(__tsan_java_mutex_read_lock); DPrintf("#%d: java_mutex_read_lock(%p)\n", thr->tid, addr); CHECK_NE(jctx, 0); CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); MutexCreate(thr, pc, addr, true, true, true); MutexReadLock(thr, pc, addr); } void __tsan_java_mutex_read_unlock(jptr addr) { SCOPED_JAVA_FUNC(__tsan_java_mutex_read_unlock); DPrintf("#%d: java_mutex_read_unlock(%p)\n", thr->tid, addr); CHECK_NE(jctx, 0); CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); MutexReadUnlock(thr, pc, addr); } void __tsan_java_mutex_lock_rec(jptr addr, int rec) { SCOPED_JAVA_FUNC(__tsan_java_mutex_lock_rec); DPrintf("#%d: java_mutex_lock_rec(%p, %d)\n", thr->tid, addr, rec); CHECK_NE(jctx, 0); CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); CHECK_GT(rec, 0); MutexCreate(thr, pc, addr, true, true, true); MutexLock(thr, pc, addr, rec); } int __tsan_java_mutex_unlock_rec(jptr addr) { SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock_rec); DPrintf("#%d: java_mutex_unlock_rec(%p)\n", thr->tid, addr); CHECK_NE(jctx, 0); CHECK_GE(addr, jctx->heap_begin); CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); return MutexUnlock(thr, pc, addr, true); } + +void __tsan_java_acquire(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_acquire); + DPrintf("#%d: java_acquire(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + Acquire(thr, caller_pc, addr); +} + +void __tsan_java_release(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_release); + DPrintf("#%d: java_release(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + Release(thr, caller_pc, addr); +} + +void __tsan_java_release_store(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_release); + DPrintf("#%d: java_release_store(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + ReleaseStore(thr, caller_pc, addr); +} Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface_java.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface_java.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_interface_java.h (revision 279191) @@ -1,88 +1,96 @@ //===-- tsan_interface_java.h -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // // Interface for verification of Java or mixed Java/C++ programs. // The interface is intended to be used from within a JVM and notify TSan // about such events like Java locks and GC memory compaction. // // For plain memory accesses and function entry/exit a JVM is intended to use // C++ interfaces: __tsan_readN/writeN and __tsan_func_enter/exit. // // For volatile memory accesses and atomic operations JVM is intended to use // standard atomics API: __tsan_atomicN_load/store/etc. // // For usage examples see lit_tests/java_*.cc //===----------------------------------------------------------------------===// #ifndef TSAN_INTERFACE_JAVA_H #define TSAN_INTERFACE_JAVA_H #ifndef INTERFACE_ATTRIBUTE # define INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) #endif #ifdef __cplusplus extern "C" { #endif typedef unsigned long jptr; // NOLINT // Must be called before any other callback from Java. void __tsan_java_init(jptr heap_begin, jptr heap_size) INTERFACE_ATTRIBUTE; // Must be called when the application exits. // Not necessary the last callback (concurrently running threads are OK). // Returns exit status or 0 if tsan does not want to override it. int __tsan_java_fini() INTERFACE_ATTRIBUTE; // Callback for memory allocations. // May be omitted for allocations that are not subject to data races // nor contain synchronization objects (e.g. String). void __tsan_java_alloc(jptr ptr, jptr size) INTERFACE_ATTRIBUTE; // Callback for memory free. // Can be aggregated for several objects (preferably). void __tsan_java_free(jptr ptr, jptr size) INTERFACE_ATTRIBUTE; // Callback for memory move by GC. // Can be aggregated for several objects (preferably). // The ranges can overlap. void __tsan_java_move(jptr src, jptr dst, jptr size) INTERFACE_ATTRIBUTE; // This function must be called on the finalizer thread // before executing a batch of finalizers. // It ensures necessary synchronization between // java object creation and finalization. void __tsan_java_finalize() INTERFACE_ATTRIBUTE; // Mutex lock. // Addr is any unique address associated with the mutex. // Can be called on recursive reentry. void __tsan_java_mutex_lock(jptr addr) INTERFACE_ATTRIBUTE; // Mutex unlock. void __tsan_java_mutex_unlock(jptr addr) INTERFACE_ATTRIBUTE; // Mutex read lock. void __tsan_java_mutex_read_lock(jptr addr) INTERFACE_ATTRIBUTE; // Mutex read unlock. void __tsan_java_mutex_read_unlock(jptr addr) INTERFACE_ATTRIBUTE; // Recursive mutex lock, intended for handling of Object.wait(). // The 'rec' value must be obtained from the previous // __tsan_java_mutex_unlock_rec(). void __tsan_java_mutex_lock_rec(jptr addr, int rec) INTERFACE_ATTRIBUTE; // Recursive mutex unlock, intended for handling of Object.wait(). // The return value says how many times this thread called lock() // w/o a pairing unlock() (i.e. how many recursive levels it unlocked). // It must be passed back to __tsan_java_mutex_lock_rec() to restore // the same recursion level. int __tsan_java_mutex_unlock_rec(jptr addr) INTERFACE_ATTRIBUTE; +// Raw acquire/release primitives. +// Can be used to establish happens-before edges on volatile/final fields, +// in atomic operations, etc. release_store is the same as release, but it +// breaks release sequence on addr (see C++ standard 1.10/7 for details). +void __tsan_java_acquire(jptr addr) INTERFACE_ATTRIBUTE; +void __tsan_java_release(jptr addr) INTERFACE_ATTRIBUTE; +void __tsan_java_release_store(jptr addr) INTERFACE_ATTRIBUTE; + #ifdef __cplusplus } // extern "C" #endif #undef INTERFACE_ATTRIBUTE #endif // #ifndef TSAN_INTERFACE_JAVA_H Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.cc (revision 279191) @@ -1,217 +1,226 @@ //===-- tsan_mman.cc ------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "tsan_mman.h" #include "tsan_rtl.h" #include "tsan_report.h" #include "tsan_flags.h" // May be overriden by front-end. extern "C" void WEAK __sanitizer_malloc_hook(void *ptr, uptr size) { (void)ptr; (void)size; } extern "C" void WEAK __sanitizer_free_hook(void *ptr) { (void)ptr; } namespace __tsan { struct MapUnmapCallback { void OnMap(uptr p, uptr size) const { } void OnUnmap(uptr p, uptr size) const { // We are about to unmap a chunk of user memory. // Mark the corresponding shadow memory as not needed. DontNeedShadowFor(p, size); } }; static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64); Allocator *allocator() { return reinterpret_cast(&allocator_placeholder); } void InitializeAllocator() { - allocator()->Init(); + allocator()->Init(common_flags()->allocator_may_return_null); } void AllocatorThreadStart(ThreadState *thr) { allocator()->InitCache(&thr->alloc_cache); internal_allocator()->InitCache(&thr->internal_alloc_cache); } void AllocatorThreadFinish(ThreadState *thr) { allocator()->DestroyCache(&thr->alloc_cache); internal_allocator()->DestroyCache(&thr->internal_alloc_cache); } void AllocatorPrintStats() { allocator()->PrintStats(); } static void SignalUnsafeCall(ThreadState *thr, uptr pc) { if (atomic_load(&thr->in_signal_handler, memory_order_relaxed) == 0 || !flags()->report_signal_unsafe) return; VarSizeStackTrace stack; ObtainCurrentStack(thr, pc, &stack); ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeSignalUnsafe); if (!IsFiredSuppression(ctx, rep, stack)) { rep.AddStack(stack, true); OutputReport(thr, rep); } } void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) { if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) - return AllocatorReturnNull(); + return allocator()->ReturnNullOrDie(); void *p = allocator()->Allocate(&thr->alloc_cache, sz, align); if (p == 0) return 0; if (ctx && ctx->initialized) OnUserAlloc(thr, pc, (uptr)p, sz, true); if (signal) SignalUnsafeCall(thr, pc); + return p; +} + +void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) { + if (CallocShouldReturnNullDueToOverflow(size, n)) + return allocator()->ReturnNullOrDie(); + void *p = user_alloc(thr, pc, n * size); + if (p) + internal_memset(p, 0, n * size); return p; } void user_free(ThreadState *thr, uptr pc, void *p, bool signal) { if (ctx && ctx->initialized) OnUserFree(thr, pc, (uptr)p, true); allocator()->Deallocate(&thr->alloc_cache, p); if (signal) SignalUnsafeCall(thr, pc); } void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) { DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p); ctx->metamap.AllocBlock(thr, pc, p, sz); if (write && thr->ignore_reads_and_writes == 0) MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); else MemoryResetRange(thr, pc, (uptr)p, sz); } void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write) { CHECK_NE(p, (void*)0); uptr sz = ctx->metamap.FreeBlock(thr, pc, p); DPrintf("#%d: free(%p, %zu)\n", thr->tid, p, sz); if (write && thr->ignore_reads_and_writes == 0) MemoryRangeFreed(thr, pc, (uptr)p, sz); } void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) { void *p2 = 0; // FIXME: Handle "shrinking" more efficiently, // it seems that some software actually does this. if (sz) { p2 = user_alloc(thr, pc, sz); if (p2 == 0) return 0; if (p) { uptr oldsz = user_alloc_usable_size(p); internal_memcpy(p2, p, min(oldsz, sz)); } } if (p) user_free(thr, pc, p); return p2; } uptr user_alloc_usable_size(const void *p) { if (p == 0) return 0; MBlock *b = ctx->metamap.GetBlock((uptr)p); return b ? b->siz : 0; } void invoke_malloc_hook(void *ptr, uptr size) { ThreadState *thr = cur_thread(); if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors) return; __sanitizer_malloc_hook(ptr, size); } void invoke_free_hook(void *ptr) { ThreadState *thr = cur_thread(); if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors) return; __sanitizer_free_hook(ptr); } void *internal_alloc(MBlockType typ, uptr sz) { ThreadState *thr = cur_thread(); if (thr->nomalloc) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } return InternalAlloc(sz, &thr->internal_alloc_cache); } void internal_free(void *p) { ThreadState *thr = cur_thread(); if (thr->nomalloc) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } InternalFree(p, &thr->internal_alloc_cache); } } // namespace __tsan using namespace __tsan; extern "C" { uptr __sanitizer_get_current_allocated_bytes() { uptr stats[AllocatorStatCount]; allocator()->GetStats(stats); return stats[AllocatorStatAllocated]; } uptr __sanitizer_get_heap_size() { uptr stats[AllocatorStatCount]; allocator()->GetStats(stats); return stats[AllocatorStatMapped]; } uptr __sanitizer_get_free_bytes() { return 1; } uptr __sanitizer_get_unmapped_bytes() { return 1; } uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } int __sanitizer_get_ownership(const void *p) { return allocator()->GetBlockBegin(p) != 0; } uptr __sanitizer_get_allocated_size(const void *p) { return user_alloc_usable_size(p); } void __tsan_on_thread_idle() { ThreadState *thr = cur_thread(); allocator()->SwallowCache(&thr->alloc_cache); internal_allocator()->SwallowCache(&thr->internal_alloc_cache); ctx->metamap.OnThreadIdle(thr); } } // extern "C" Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mman.h (revision 279191) @@ -1,80 +1,81 @@ //===-- tsan_mman.h ---------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #ifndef TSAN_MMAN_H #define TSAN_MMAN_H #include "tsan_defs.h" namespace __tsan { const uptr kDefaultAlignment = 16; void InitializeAllocator(); void AllocatorThreadStart(ThreadState *thr); void AllocatorThreadFinish(ThreadState *thr); void AllocatorPrintStats(); // For user allocations. void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align = kDefaultAlignment, bool signal = true); +void *user_calloc(ThreadState *thr, uptr pc, uptr sz, uptr n); // Does not accept NULL. void user_free(ThreadState *thr, uptr pc, void *p, bool signal = true); void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz); void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align); uptr user_alloc_usable_size(const void *p); // Invoking malloc/free hooks that may be installed by the user. void invoke_malloc_hook(void *ptr, uptr size); void invoke_free_hook(void *ptr); enum MBlockType { MBlockScopedBuf, MBlockString, MBlockStackTrace, MBlockShadowStack, MBlockSync, MBlockClock, MBlockThreadContex, MBlockDeadInfo, MBlockRacyStacks, MBlockRacyAddresses, MBlockAtExit, MBlockFlag, MBlockReport, MBlockReportMop, MBlockReportThread, MBlockReportMutex, MBlockReportLoc, MBlockReportStack, MBlockSuppression, MBlockExpectRace, MBlockSignal, MBlockJmpBuf, // This must be the last. MBlockTypeCount }; // For internal data structures. void *internal_alloc(MBlockType typ, uptr sz); void internal_free(void *p); template void DestroyAndFree(T *&p) { p->~T(); internal_free(p); p = 0; } } // namespace __tsan #endif // TSAN_MMAN_H Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.cc (revision 279191) @@ -1,287 +1,287 @@ //===-- tsan_mutex.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_libc.h" #include "tsan_mutex.h" #include "tsan_platform.h" #include "tsan_rtl.h" namespace __tsan { // Simple reader-writer spin-mutex. Optimized for not-so-contended case. // Readers have preference, can possibly starvate writers. // The table fixes what mutexes can be locked under what mutexes. // E.g. if the row for MutexTypeThreads contains MutexTypeReport, // then Report mutex can be locked while under Threads mutex. // The leaf mutexes can be locked under any other mutexes. // Recursive locking is not supported. -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO const MutexType MutexTypeLeaf = (MutexType)-1; static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = { /*0 MutexTypeInvalid*/ {}, /*1 MutexTypeTrace*/ {MutexTypeLeaf}, /*2 MutexTypeThreads*/ {MutexTypeReport}, /*3 MutexTypeReport*/ {MutexTypeSyncVar, MutexTypeMBlock, MutexTypeJavaMBlock}, /*4 MutexTypeSyncVar*/ {MutexTypeDDetector}, /*5 MutexTypeSyncTab*/ {}, // unused /*6 MutexTypeSlab*/ {MutexTypeLeaf}, /*7 MutexTypeAnnotations*/ {}, /*8 MutexTypeAtExit*/ {MutexTypeSyncVar}, /*9 MutexTypeMBlock*/ {MutexTypeSyncVar}, /*10 MutexTypeJavaMBlock*/ {MutexTypeSyncVar}, /*11 MutexTypeDDetector*/ {}, }; static bool CanLockAdj[MutexTypeCount][MutexTypeCount]; #endif void InitializeMutex() { -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO // Build the "can lock" adjacency matrix. // If [i][j]==true, then one can lock mutex j while under mutex i. const int N = MutexTypeCount; int cnt[N] = {}; bool leaf[N] = {}; for (int i = 1; i < N; i++) { for (int j = 0; j < N; j++) { MutexType z = CanLockTab[i][j]; if (z == MutexTypeInvalid) continue; if (z == MutexTypeLeaf) { CHECK(!leaf[i]); leaf[i] = true; continue; } CHECK(!CanLockAdj[i][(int)z]); CanLockAdj[i][(int)z] = true; cnt[i]++; } } for (int i = 0; i < N; i++) { CHECK(!leaf[i] || cnt[i] == 0); } // Add leaf mutexes. for (int i = 0; i < N; i++) { if (!leaf[i]) continue; for (int j = 0; j < N; j++) { if (i == j || leaf[j] || j == MutexTypeInvalid) continue; CHECK(!CanLockAdj[j][i]); CanLockAdj[j][i] = true; } } // Build the transitive closure. bool CanLockAdj2[MutexTypeCount][MutexTypeCount]; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { CanLockAdj2[i][j] = CanLockAdj[i][j]; } } for (int k = 0; k < N; k++) { for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (CanLockAdj2[i][k] && CanLockAdj2[k][j]) { CanLockAdj2[i][j] = true; } } } } #if 0 Printf("Can lock graph:\n"); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { Printf("%d ", CanLockAdj[i][j]); } Printf("\n"); } Printf("Can lock graph closure:\n"); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { Printf("%d ", CanLockAdj2[i][j]); } Printf("\n"); } #endif // Verify that the graph is acyclic. for (int i = 0; i < N; i++) { if (CanLockAdj2[i][i]) { Printf("Mutex %d participates in a cycle\n", i); Die(); } } #endif } InternalDeadlockDetector::InternalDeadlockDetector() { // Rely on zero initialization because some mutexes can be locked before ctor. } -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO void InternalDeadlockDetector::Lock(MutexType t) { // Printf("LOCK %d @%zu\n", t, seq_ + 1); CHECK_GT(t, MutexTypeInvalid); CHECK_LT(t, MutexTypeCount); u64 max_seq = 0; u64 max_idx = MutexTypeInvalid; for (int i = 0; i != MutexTypeCount; i++) { if (locked_[i] == 0) continue; CHECK_NE(locked_[i], max_seq); if (max_seq < locked_[i]) { max_seq = locked_[i]; max_idx = i; } } locked_[t] = ++seq_; if (max_idx == MutexTypeInvalid) return; // Printf(" last %d @%zu\n", max_idx, max_seq); if (!CanLockAdj[max_idx][t]) { Printf("ThreadSanitizer: internal deadlock detected\n"); Printf("ThreadSanitizer: can't lock %d while under %zu\n", t, (uptr)max_idx); CHECK(0); } } void InternalDeadlockDetector::Unlock(MutexType t) { // Printf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]); CHECK(locked_[t]); locked_[t] = 0; } void InternalDeadlockDetector::CheckNoLocks() { for (int i = 0; i != MutexTypeCount; i++) { CHECK_EQ(locked_[i], 0); } } #endif void CheckNoLocks(ThreadState *thr) { -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO thr->internal_deadlock_detector.CheckNoLocks(); #endif } const uptr kUnlocked = 0; const uptr kWriteLock = 1; const uptr kReadLock = 2; class Backoff { public: Backoff() : iter_() { } bool Do() { if (iter_++ < kActiveSpinIters) proc_yield(kActiveSpinCnt); else internal_sched_yield(); return true; } u64 Contention() const { u64 active = iter_ % kActiveSpinIters; u64 passive = iter_ - active; return active + 10 * passive; } private: int iter_; static const int kActiveSpinIters = 10; static const int kActiveSpinCnt = 20; }; Mutex::Mutex(MutexType type, StatType stat_type) { CHECK_GT(type, MutexTypeInvalid); CHECK_LT(type, MutexTypeCount); -#if TSAN_DEBUG +#if SANITIZER_DEBUG type_ = type; #endif #if TSAN_COLLECT_STATS stat_type_ = stat_type; #endif atomic_store(&state_, kUnlocked, memory_order_relaxed); } Mutex::~Mutex() { CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked); } void Mutex::Lock() { -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO cur_thread()->internal_deadlock_detector.Lock(type_); #endif uptr cmp = kUnlocked; if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock, memory_order_acquire)) return; for (Backoff backoff; backoff.Do();) { if (atomic_load(&state_, memory_order_relaxed) == kUnlocked) { cmp = kUnlocked; if (atomic_compare_exchange_weak(&state_, &cmp, kWriteLock, memory_order_acquire)) { #if TSAN_COLLECT_STATS && !SANITIZER_GO StatInc(cur_thread(), stat_type_, backoff.Contention()); #endif return; } } } } void Mutex::Unlock() { uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release); (void)prev; DCHECK_NE(prev & kWriteLock, 0); -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO cur_thread()->internal_deadlock_detector.Unlock(type_); #endif } void Mutex::ReadLock() { -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO cur_thread()->internal_deadlock_detector.Lock(type_); #endif uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire); if ((prev & kWriteLock) == 0) return; for (Backoff backoff; backoff.Do();) { prev = atomic_load(&state_, memory_order_acquire); if ((prev & kWriteLock) == 0) { #if TSAN_COLLECT_STATS && !SANITIZER_GO StatInc(cur_thread(), stat_type_, backoff.Contention()); #endif return; } } } void Mutex::ReadUnlock() { uptr prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release); (void)prev; DCHECK_EQ(prev & kWriteLock, 0); DCHECK_GT(prev & ~kWriteLock, 0); -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO cur_thread()->internal_deadlock_detector.Unlock(type_); #endif } void Mutex::CheckLocked() { CHECK_NE(atomic_load(&state_, memory_order_relaxed), 0); } } // namespace __tsan Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_mutex.h (revision 279191) @@ -1,88 +1,88 @@ //===-- tsan_mutex.h --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #ifndef TSAN_MUTEX_H #define TSAN_MUTEX_H #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_mutex.h" #include "tsan_defs.h" namespace __tsan { enum MutexType { MutexTypeInvalid, MutexTypeTrace, MutexTypeThreads, MutexTypeReport, MutexTypeSyncVar, MutexTypeSyncTab, MutexTypeSlab, MutexTypeAnnotations, MutexTypeAtExit, MutexTypeMBlock, MutexTypeJavaMBlock, MutexTypeDDetector, // This must be the last. MutexTypeCount }; class Mutex { public: explicit Mutex(MutexType type, StatType stat_type); ~Mutex(); void Lock(); void Unlock(); void ReadLock(); void ReadUnlock(); void CheckLocked(); private: atomic_uintptr_t state_; -#if TSAN_DEBUG +#if SANITIZER_DEBUG MutexType type_; #endif #if TSAN_COLLECT_STATS StatType stat_type_; #endif Mutex(const Mutex&); void operator = (const Mutex&); }; typedef GenericScopedLock Lock; typedef GenericScopedReadLock ReadLock; class InternalDeadlockDetector { public: InternalDeadlockDetector(); void Lock(MutexType t); void Unlock(MutexType t); void CheckNoLocks(); private: u64 seq_; u64 locked_[MutexTypeCount]; }; void InitializeMutex(); // Checks that the current thread does not hold any runtime locks // (e.g. when returning from an interceptor). void CheckNoLocks(ThreadState *thr); } // namespace __tsan #endif // TSAN_MUTEX_H Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_platform.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_platform.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_platform.h (revision 279191) @@ -1,270 +1,267 @@ //===-- tsan_platform.h -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // // Platform-specific code. //===----------------------------------------------------------------------===// #ifndef TSAN_PLATFORM_H #define TSAN_PLATFORM_H #if !defined(__LP64__) && !defined(_WIN64) # error "Only 64-bit is supported" #endif #include "tsan_defs.h" #include "tsan_trace.h" namespace __tsan { #if !defined(SANITIZER_GO) /* C/C++ on linux and freebsd 0000 0000 1000 - 0100 0000 0000: main binary and/or MAP_32BIT mappings 0100 0000 0000 - 0200 0000 0000: - 0200 0000 0000 - 1000 0000 0000: shadow 1000 0000 0000 - 3000 0000 0000: - 3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects) 4000 0000 0000 - 6000 0000 0000: - 6000 0000 0000 - 6200 0000 0000: traces 6200 0000 0000 - 7d00 0000 0000: - 7d00 0000 0000 - 7e00 0000 0000: heap 7e00 0000 0000 - 7e80 0000 0000: - 7e80 0000 0000 - 8000 0000 0000: modules and main thread stack */ const uptr kMetaShadowBeg = 0x300000000000ull; const uptr kMetaShadowEnd = 0x400000000000ull; const uptr kTraceMemBeg = 0x600000000000ull; const uptr kTraceMemEnd = 0x620000000000ull; const uptr kShadowBeg = 0x020000000000ull; const uptr kShadowEnd = 0x100000000000ull; const uptr kHeapMemBeg = 0x7d0000000000ull; const uptr kHeapMemEnd = 0x7e0000000000ull; const uptr kLoAppMemBeg = 0x000000001000ull; const uptr kLoAppMemEnd = 0x010000000000ull; const uptr kHiAppMemBeg = 0x7e8000000000ull; const uptr kHiAppMemEnd = 0x800000000000ull; const uptr kAppMemMsk = 0x7c0000000000ull; const uptr kAppMemXor = 0x020000000000ull; ALWAYS_INLINE bool IsAppMem(uptr mem) { return (mem >= kHeapMemBeg && mem < kHeapMemEnd) || (mem >= kLoAppMemBeg && mem < kLoAppMemEnd) || (mem >= kHiAppMemBeg && mem < kHiAppMemEnd); } ALWAYS_INLINE bool IsShadowMem(uptr mem) { return mem >= kShadowBeg && mem <= kShadowEnd; } ALWAYS_INLINE bool IsMetaMem(uptr mem) { return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd; } ALWAYS_INLINE uptr MemToShadow(uptr x) { DCHECK(IsAppMem(x)); return (((x) & ~(kAppMemMsk | (kShadowCell - 1))) ^ kAppMemXor) * kShadowCnt; } ALWAYS_INLINE u32 *MemToMeta(uptr x) { DCHECK(IsAppMem(x)); return (u32*)(((((x) & ~(kAppMemMsk | (kMetaShadowCell - 1))) ^ kAppMemXor) / kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg); } ALWAYS_INLINE uptr ShadowToMem(uptr s) { CHECK(IsShadowMem(s)); if (s >= MemToShadow(kLoAppMemBeg) && s <= MemToShadow(kLoAppMemEnd - 1)) return (s / kShadowCnt) ^ kAppMemXor; else return ((s / kShadowCnt) ^ kAppMemXor) | kAppMemMsk; } static USED uptr UserRegions[] = { kLoAppMemBeg, kLoAppMemEnd, kHiAppMemBeg, kHiAppMemEnd, kHeapMemBeg, kHeapMemEnd, }; #elif defined(SANITIZER_GO) && !SANITIZER_WINDOWS /* Go on linux, darwin and freebsd 0000 0000 1000 - 0000 1000 0000: executable 0000 1000 0000 - 00c0 0000 0000: - 00c0 0000 0000 - 00e0 0000 0000: heap 00e0 0000 0000 - 2000 0000 0000: - 2000 0000 0000 - 2380 0000 0000: shadow 2380 0000 0000 - 3000 0000 0000: - 3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects) 4000 0000 0000 - 6000 0000 0000: - 6000 0000 0000 - 6200 0000 0000: traces 6200 0000 0000 - 8000 0000 0000: - */ const uptr kMetaShadowBeg = 0x300000000000ull; const uptr kMetaShadowEnd = 0x400000000000ull; const uptr kTraceMemBeg = 0x600000000000ull; const uptr kTraceMemEnd = 0x620000000000ull; const uptr kShadowBeg = 0x200000000000ull; const uptr kShadowEnd = 0x238000000000ull; const uptr kAppMemBeg = 0x000000001000ull; const uptr kAppMemEnd = 0x00e000000000ull; ALWAYS_INLINE bool IsAppMem(uptr mem) { return mem >= kAppMemBeg && mem < kAppMemEnd; } ALWAYS_INLINE bool IsShadowMem(uptr mem) { return mem >= kShadowBeg && mem <= kShadowEnd; } ALWAYS_INLINE bool IsMetaMem(uptr mem) { return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd; } ALWAYS_INLINE uptr MemToShadow(uptr x) { DCHECK(IsAppMem(x)); return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg; } ALWAYS_INLINE u32 *MemToMeta(uptr x) { DCHECK(IsAppMem(x)); return (u32*)(((x & ~(kMetaShadowCell - 1)) / \ kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg); } ALWAYS_INLINE uptr ShadowToMem(uptr s) { CHECK(IsShadowMem(s)); return (s & ~kShadowBeg) / kShadowCnt; } static USED uptr UserRegions[] = { kAppMemBeg, kAppMemEnd, }; #elif defined(SANITIZER_GO) && SANITIZER_WINDOWS /* Go on windows 0000 0000 1000 - 0000 1000 0000: executable 0000 1000 0000 - 00f8 0000 0000: - 00c0 0000 0000 - 00e0 0000 0000: heap 00e0 0000 0000 - 0100 0000 0000: - 0100 0000 0000 - 0380 0000 0000: shadow 0380 0000 0000 - 0560 0000 0000: - 0560 0000 0000 - 0760 0000 0000: traces 0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects) 07d0 0000 0000 - 8000 0000 0000: - */ const uptr kMetaShadowBeg = 0x076000000000ull; const uptr kMetaShadowEnd = 0x07d000000000ull; const uptr kTraceMemBeg = 0x056000000000ull; const uptr kTraceMemEnd = 0x076000000000ull; const uptr kShadowBeg = 0x010000000000ull; const uptr kShadowEnd = 0x038000000000ull; const uptr kAppMemBeg = 0x000000001000ull; const uptr kAppMemEnd = 0x00e000000000ull; ALWAYS_INLINE bool IsAppMem(uptr mem) { return mem >= kAppMemBeg && mem < kAppMemEnd; } ALWAYS_INLINE bool IsShadowMem(uptr mem) { return mem >= kShadowBeg && mem <= kShadowEnd; } ALWAYS_INLINE bool IsMetaMem(uptr mem) { return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd; } ALWAYS_INLINE uptr MemToShadow(uptr x) { DCHECK(IsAppMem(x)); return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg; } ALWAYS_INLINE u32 *MemToMeta(uptr x) { DCHECK(IsAppMem(x)); return (u32*)(((x & ~(kMetaShadowCell - 1)) / \ kMetaShadowCell * kMetaShadowSize) | kMetaShadowEnd); } ALWAYS_INLINE uptr ShadowToMem(uptr s) { CHECK(IsShadowMem(s)); // FIXME(dvyukov): this is most likely wrong as the mapping is not bijection. return (x & ~kShadowBeg) / kShadowCnt; } static USED uptr UserRegions[] = { kAppMemBeg, kAppMemEnd, }; #else # error "Unknown platform" #endif // The additional page is to catch shadow stack overflow as paging fault. // Windows wants 64K alignment for mmaps. const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace) + (64 << 10) + (64 << 10) - 1) & ~((64 << 10) - 1); uptr ALWAYS_INLINE GetThreadTrace(int tid) { uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize; DCHECK_LT(p, kTraceMemEnd); return p; } uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) { uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize + kTraceSize * sizeof(Event); DCHECK_LT(p, kTraceMemEnd); return p; } void InitializePlatform(); void FlushShadowMemory(); void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive); -void *internal_start_thread(void(*func)(void*), void *arg); -void internal_join_thread(void *th); - // Says whether the addr relates to a global var. // Guesses with high probability, may yield both false positives and negatives. bool IsGlobalVar(uptr addr); int ExtractResolvFDs(void *state, int *fds, int nfd); int ExtractRecvmsgFDs(void *msg, int *fds, int nfd); int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, void *abstime), void *c, void *m, void *abstime, void(*cleanup)(void *arg), void *arg); } // namespace __tsan #endif // TSAN_PLATFORM_H Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cc (revision 279191) @@ -1,404 +1,407 @@ //===-- tsan_platform_linux.cc --------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // // Linux- and FreeBSD-specific code. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_LINUX || SANITIZER_FREEBSD #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stoptheworld.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_flags.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if SANITIZER_LINUX #define __need_res_state #include #endif #ifdef sa_handler # undef sa_handler #endif #ifdef sa_sigaction # undef sa_sigaction #endif #if SANITIZER_FREEBSD extern "C" void *__libc_stack_end; void *__libc_stack_end = 0; #endif namespace __tsan { static uptr g_data_start; static uptr g_data_end; const uptr kPageSize = 4096; enum { MemTotal = 0, MemShadow = 1, MemMeta = 2, MemFile = 3, MemMmap = 4, MemTrace = 5, MemHeap = 6, MemOther = 7, MemCount = 8, }; void FillProfileCallback(uptr p, uptr rss, bool file, uptr *mem, uptr stats_size) { mem[MemTotal] += rss; if (p >= kShadowBeg && p < kShadowEnd) mem[MemShadow] += rss; else if (p >= kMetaShadowBeg && p < kMetaShadowEnd) mem[MemMeta] += rss; #ifndef SANITIZER_GO else if (p >= kHeapMemBeg && p < kHeapMemEnd) mem[MemHeap] += rss; else if (p >= kLoAppMemBeg && p < kLoAppMemEnd) mem[file ? MemFile : MemMmap] += rss; else if (p >= kHiAppMemBeg && p < kHiAppMemEnd) mem[file ? MemFile : MemMmap] += rss; #else else if (p >= kAppMemBeg && p < kAppMemEnd) mem[file ? MemFile : MemMmap] += rss; #endif else if (p >= kTraceMemBeg && p < kTraceMemEnd) mem[MemTrace] += rss; else mem[MemOther] += rss; } void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) { uptr mem[MemCount] = {}; __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); StackDepotStats *stacks = StackDepotGetStats(); internal_snprintf(buf, buf_size, "RSS %zd MB: shadow:%zd meta:%zd file:%zd mmap:%zd" " trace:%zd heap:%zd other:%zd stacks=%zd[%zd] nthr=%zd/%zd\n", mem[MemTotal] >> 20, mem[MemShadow] >> 20, mem[MemMeta] >> 20, mem[MemFile] >> 20, mem[MemMmap] >> 20, mem[MemTrace] >> 20, mem[MemHeap] >> 20, mem[MemOther] >> 20, stacks->allocated >> 20, stacks->n_uniq_ids, nlive, nthread); } #if SANITIZER_LINUX void FlushShadowMemoryCallback( const SuspendedThreadsList &suspended_threads_list, void *argument) { FlushUnneededShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); } #endif void FlushShadowMemory() { #if SANITIZER_LINUX StopTheWorld(FlushShadowMemoryCallback, 0); #endif } #ifndef SANITIZER_GO static void ProtectRange(uptr beg, uptr end) { CHECK_LE(beg, end); if (beg == end) return; if (beg != (uptr)Mprotect(beg, end - beg)) { Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end); Printf("FATAL: Make sure you are not using unlimited stack\n"); Die(); } } // Mark shadow for .rodata sections with the special kShadowRodata marker. // Accesses to .rodata can't race, so this saves time, memory and trace space. static void MapRodata() { // First create temp file. const char *tmpdir = GetEnv("TMPDIR"); if (tmpdir == 0) tmpdir = GetEnv("TEST_TMPDIR"); #ifdef P_tmpdir if (tmpdir == 0) tmpdir = P_tmpdir; #endif if (tmpdir == 0) return; char name[256]; internal_snprintf(name, sizeof(name), "%s/tsan.rodata.%d", tmpdir, (int)internal_getpid()); uptr openrv = internal_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); if (internal_iserror(openrv)) return; internal_unlink(name); // Unlink it now, so that we can reuse the buffer. fd_t fd = openrv; // Fill the file with kShadowRodata. const uptr kMarkerSize = 512 * 1024 / sizeof(u64); InternalScopedBuffer marker(kMarkerSize); // volatile to prevent insertion of memset for (volatile u64 *p = marker.data(); p < marker.data() + kMarkerSize; p++) *p = kShadowRodata; internal_write(fd, marker.data(), marker.size()); // Map the file into memory. uptr page = internal_mmap(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, fd, 0); if (internal_iserror(page)) { internal_close(fd); return; } // Map the file into shadow of .rodata sections. MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end, offset, prot; // Reusing the buffer 'name'. while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name), &prot)) { if (name[0] != 0 && name[0] != '[' && (prot & MemoryMappingLayout::kProtectionRead) && (prot & MemoryMappingLayout::kProtectionExecute) && !(prot & MemoryMappingLayout::kProtectionWrite) && IsAppMem(start)) { // Assume it's .rodata char *shadow_start = (char*)MemToShadow(start); char *shadow_end = (char*)MemToShadow(end); for (char *p = shadow_start; p < shadow_end; p += marker.size()) { internal_mmap(p, Min(marker.size(), shadow_end - p), PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0); } } } internal_close(fd); } void InitializeShadowMemory() { // Map memory shadow. uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg); if (shadow != kShadowBeg) { Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); Printf("FATAL: Make sure to compile with -fPIE and " "to link with -pie (%p, %p).\n", shadow, kShadowBeg); Die(); } // This memory range is used for thread stacks and large user mmaps. // Frequently a thread uses only a small part of stack and similarly // a program uses a small part of large mmap. On some programs // we see 20% memory usage reduction without huge pages for this range. -#ifdef MADV_NOHUGEPAGE - madvise((void*)MemToShadow(0x7f0000000000ULL), - 0x10000000000ULL * kShadowMultiplier, MADV_NOHUGEPAGE); -#endif + // FIXME: don't use constants here. + NoHugePagesInRegion(MemToShadow(0x7f0000000000ULL), + 0x10000000000ULL * kShadowMultiplier); + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); DPrintf("memory shadow: %zx-%zx (%zuGB)\n", kShadowBeg, kShadowEnd, (kShadowEnd - kShadowBeg) >> 30); // Map meta shadow. uptr meta_size = kMetaShadowEnd - kMetaShadowBeg; uptr meta = (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size); if (meta != kMetaShadowBeg) { Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); Printf("FATAL: Make sure to compile with -fPIE and " "to link with -pie (%p, %p).\n", meta, kMetaShadowBeg); Die(); } + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(meta, meta_size); DPrintf("meta shadow: %zx-%zx (%zuGB)\n", meta, meta + meta_size, meta_size >> 30); MapRodata(); } static void InitDataSeg() { MemoryMappingLayout proc_maps(true); uptr start, end, offset; char name[128]; #if SANITIZER_FREEBSD // On FreeBSD BSS is usually the last block allocated within the // low range and heap is the last block allocated within the range // 0x800000000-0x8ffffffff. while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name), /*protection*/ 0)) { DPrintf("%p-%p %p %s\n", start, end, offset, name); if ((start & 0xffff00000000ULL) == 0 && (end & 0xffff00000000ULL) == 0 && name[0] == '\0') { g_data_start = start; g_data_end = end; } } #else bool prev_is_data = false; while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name), /*protection*/ 0)) { DPrintf("%p-%p %p %s\n", start, end, offset, name); bool is_data = offset != 0 && name[0] != 0; // BSS may get merged with [heap] in /proc/self/maps. This is not very // reliable. bool is_bss = offset == 0 && (name[0] == 0 || internal_strcmp(name, "[heap]") == 0) && prev_is_data; if (g_data_start == 0 && is_data) g_data_start = start; if (is_bss) g_data_end = end; prev_is_data = is_data; } #endif DPrintf("guessed data_start=%p data_end=%p\n", g_data_start, g_data_end); CHECK_LT(g_data_start, g_data_end); CHECK_GE((uptr)&g_data_start, g_data_start); CHECK_LT((uptr)&g_data_start, g_data_end); } static void CheckAndProtect() { // Ensure that the binary is indeed compiled with -pie. MemoryMappingLayout proc_maps(true); uptr p, end; while (proc_maps.Next(&p, &end, 0, 0, 0, 0)) { if (IsAppMem(p)) continue; if (p >= kHeapMemEnd && p < kHeapMemEnd + PrimaryAllocator::AdditionalSize()) continue; if (p >= 0xf000000000000000ull) // vdso break; Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end); Die(); } ProtectRange(kLoAppMemEnd, kShadowBeg); ProtectRange(kShadowEnd, kMetaShadowBeg); ProtectRange(kMetaShadowEnd, kTraceMemBeg); // Memory for traces is mapped lazily in MapThreadTrace. // Protect the whole range for now, so that user does not map something here. ProtectRange(kTraceMemBeg, kTraceMemEnd); ProtectRange(kTraceMemEnd, kHeapMemBeg); ProtectRange(kHeapMemEnd + PrimaryAllocator::AdditionalSize(), kHiAppMemBeg); } #endif // #ifndef SANITIZER_GO void InitializePlatform() { DisableCoreDumperIfNecessary(); // Go maps shadow memory lazily and works fine with limited address space. // Unlimited stack is not a problem as well, because the executable // is not compiled with -pie. if (kCppMode) { bool reexec = false; // TSan doesn't play well with unlimited stack size (as stack // overlaps with shadow memory). If we detect unlimited stack size, // we re-exec the program with limited stack size as a best effort. if (StackSizeIsUnlimited()) { const uptr kMaxStackSize = 32 * 1024 * 1024; VReport(1, "Program is run with unlimited stack size, which wouldn't " "work with ThreadSanitizer.\n" "Re-execing with stack size limited to %zd bytes.\n", kMaxStackSize); SetStackSizeLimitInBytes(kMaxStackSize); reexec = true; } if (!AddressSpaceIsUnlimited()) { Report("WARNING: Program is run with limited virtual address space," " which wouldn't work with ThreadSanitizer.\n"); Report("Re-execing with unlimited virtual address space.\n"); SetAddressSpaceUnlimited(); reexec = true; } if (reexec) ReExec(); } #ifndef SANITIZER_GO CheckAndProtect(); InitTlsSize(); InitDataSeg(); #endif } bool IsGlobalVar(uptr addr) { return g_data_start && addr >= g_data_start && addr < g_data_end; } #ifndef SANITIZER_GO // Extract file descriptors passed to glibc internal __res_iclose function. // This is required to properly "close" the fds, because we do not see internal // closes within glibc. The code is a pure hack. int ExtractResolvFDs(void *state, int *fds, int nfd) { #if SANITIZER_LINUX int cnt = 0; __res_state *statp = (__res_state*)state; for (int i = 0; i < MAXNS && cnt < nfd; i++) { if (statp->_u._ext.nsaddrs[i] && statp->_u._ext.nssocks[i] != -1) fds[cnt++] = statp->_u._ext.nssocks[i]; } return cnt; #else return 0; #endif } // Extract file descriptors passed via UNIX domain sockets. // This is requried to properly handle "open" of these fds. // see 'man recvmsg' and 'man 3 cmsg'. int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) { int res = 0; msghdr *msg = (msghdr*)msgp; struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); for (; cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) continue; int n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(fds[0]); for (int i = 0; i < n; i++) { fds[res++] = ((int*)CMSG_DATA(cmsg))[i]; if (res == nfd) return res; } } return res; } int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, void *abstime), void *c, void *m, void *abstime, void(*cleanup)(void *arg), void *arg) { // pthread_cleanup_push/pop are hardcore macros mess. // We can't intercept nor call them w/o including pthread.h. int res; pthread_cleanup_push(cleanup, arg); res = fn(c, m, abstime); pthread_cleanup_pop(0); return res; } #endif } // namespace __tsan #endif // SANITIZER_LINUX || SANITIZER_FREEBSD Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_platform_mac.cc (revision 279191) @@ -1,92 +1,94 @@ //===-- tsan_platform_mac.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // // Mac-specific code. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_MAC #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_flags.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace __tsan { uptr GetShadowMemoryConsumption() { return 0; } void FlushShadowMemory() { } void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) { } #ifndef SANITIZER_GO void InitializeShadowMemory() { uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg); if (shadow != kShadowBeg) { Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); Printf("FATAL: Make sure to compile with -fPIE and " "to link with -pie.\n"); Die(); } + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); DPrintf("kShadow %zx-%zx (%zuGB)\n", kShadowBeg, kShadowEnd, (kShadowEnd - kShadowBeg) >> 30); DPrintf("kAppMem %zx-%zx (%zuGB)\n", kAppMemBeg, kAppMemEnd, (kAppMemEnd - kAppMemBeg) >> 30); } #endif void InitializePlatform() { DisableCoreDumperIfNecessary(); } #ifndef SANITIZER_GO int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, void *abstime), void *c, void *m, void *abstime, void(*cleanup)(void *arg), void *arg) { // pthread_cleanup_push/pop are hardcore macros mess. // We can't intercept nor call them w/o including pthread.h. int res; pthread_cleanup_push(cleanup, arg); res = fn(c, m, abstime); pthread_cleanup_pop(0); return res; } #endif } // namespace __tsan #endif // SANITIZER_MAC Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.cc (revision 279191) @@ -1,1020 +1,993 @@ //===-- tsan_rtl.cc -------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // // Main file (entry points) for the TSan run-time. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "tsan_defs.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_mman.h" #include "tsan_suppressions.h" #include "tsan_symbolize.h" #ifdef __SSE3__ // transitively includes , // and it's prohibited to include std headers into tsan runtime. // So we do this dirty trick. #define _MM_MALLOC_H_INCLUDED #define __MM_MALLOC_H #include typedef __m128i m128; #endif volatile int __tsan_resumed = 0; extern "C" void __tsan_resume() { __tsan_resumed = 1; } namespace __tsan { #ifndef SANITIZER_GO THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); #endif static char ctx_placeholder[sizeof(Context)] ALIGNED(64); Context *ctx; // Can be overriden by a front-end. #ifdef TSAN_EXTERNAL_HOOKS bool OnFinalize(bool failed); void OnInitialize(); #else SANITIZER_INTERFACE_ATTRIBUTE bool WEAK OnFinalize(bool failed) { return failed; } SANITIZER_INTERFACE_ATTRIBUTE void WEAK OnInitialize() {} #endif static char thread_registry_placeholder[sizeof(ThreadRegistry)]; static ThreadContextBase *CreateThreadContext(u32 tid) { // Map thread trace when context is created. MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event)); MapThreadTrace(GetThreadTraceHeader(tid), sizeof(Trace)); new(ThreadTrace(tid)) Trace(); void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext)); return new(mem) ThreadContext(tid); } #ifndef SANITIZER_GO static const u32 kThreadQuarantineSize = 16; #else static const u32 kThreadQuarantineSize = 64; #endif Context::Context() : initialized() , report_mtx(MutexTypeReport, StatMtxReport) , nreported() , nmissed_expected() , thread_registry(new(thread_registry_placeholder) ThreadRegistry( CreateThreadContext, kMaxTid, kThreadQuarantineSize, kMaxTidReuse)) , racy_stacks(MBlockRacyStacks) , racy_addresses(MBlockRacyAddresses) , fired_suppressions(8) { } // The objects are allocated in TLS, so one may rely on zero-initialization. ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, unsigned reuse_count, uptr stk_addr, uptr stk_size, uptr tls_addr, uptr tls_size) : fast_state(tid, epoch) // Do not touch these, rely on zero initialization, // they may be accessed before the ctor. // , ignore_reads_and_writes() // , ignore_interceptors() , clock(tid, reuse_count) #ifndef SANITIZER_GO , jmp_bufs(MBlockJmpBuf) #endif , tid(tid) , unique_id(unique_id) , stk_addr(stk_addr) , stk_size(stk_size) , tls_addr(tls_addr) , tls_size(tls_size) #ifndef SANITIZER_GO , last_sleep_clock(tid) #endif { } static void MemoryProfiler(Context *ctx, fd_t fd, int i) { uptr n_threads; uptr n_running_threads; ctx->thread_registry->GetNumberOfThreads(&n_threads, &n_running_threads); InternalScopedBuffer buf(4096); WriteMemoryProfile(buf.data(), buf.size(), n_threads, n_running_threads); internal_write(fd, buf.data(), internal_strlen(buf.data())); } static void BackgroundThread(void *arg) { #ifndef SANITIZER_GO // This is a non-initialized non-user thread, nothing to see here. // We don't use ScopedIgnoreInterceptors, because we want ignores to be // enabled even when the thread function exits (e.g. during pthread thread // shutdown code). cur_thread()->ignore_interceptors++; #endif const u64 kMs2Ns = 1000 * 1000; fd_t mprof_fd = kInvalidFd; if (flags()->profile_memory && flags()->profile_memory[0]) { if (internal_strcmp(flags()->profile_memory, "stdout") == 0) { mprof_fd = 1; } else if (internal_strcmp(flags()->profile_memory, "stderr") == 0) { mprof_fd = 2; } else { InternalScopedString filename(kMaxPathLength); filename.append("%s.%d", flags()->profile_memory, (int)internal_getpid()); uptr openrv = OpenFile(filename.data(), true); if (internal_iserror(openrv)) { Printf("ThreadSanitizer: failed to open memory profile file '%s'\n", &filename[0]); } else { mprof_fd = openrv; } } } u64 last_flush = NanoTime(); uptr last_rss = 0; for (int i = 0; atomic_load(&ctx->stop_background_thread, memory_order_relaxed) == 0; i++) { SleepForMillis(100); u64 now = NanoTime(); // Flush memory if requested. if (flags()->flush_memory_ms > 0) { if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) { VPrintf(1, "ThreadSanitizer: periodic memory flush\n"); FlushShadowMemory(); last_flush = NanoTime(); } } // GetRSS can be expensive on huge programs, so don't do it every 100ms. if (flags()->memory_limit_mb > 0) { uptr rss = GetRSS(); uptr limit = uptr(flags()->memory_limit_mb) << 20; VPrintf(1, "ThreadSanitizer: memory flush check" " RSS=%llu LAST=%llu LIMIT=%llu\n", (u64)rss >> 20, (u64)last_rss >> 20, (u64)limit >> 20); if (2 * rss > limit + last_rss) { VPrintf(1, "ThreadSanitizer: flushing memory due to RSS\n"); FlushShadowMemory(); rss = GetRSS(); VPrintf(1, "ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20); } last_rss = rss; } // Write memory profile if requested. if (mprof_fd != kInvalidFd) MemoryProfiler(ctx, mprof_fd, i); #ifndef SANITIZER_GO // Flush symbolizer cache if requested. if (flags()->flush_symbolizer_ms > 0) { u64 last = atomic_load(&ctx->last_symbolize_time_ns, memory_order_relaxed); if (last != 0 && last + flags()->flush_symbolizer_ms * kMs2Ns < now) { Lock l(&ctx->report_mtx); SpinMutexLock l2(&CommonSanitizerReportMutex); SymbolizeFlush(); atomic_store(&ctx->last_symbolize_time_ns, 0, memory_order_relaxed); } } #endif } } static void StartBackgroundThread() { ctx->background_thread = internal_start_thread(&BackgroundThread, 0); } #ifndef SANITIZER_GO static void StopBackgroundThread() { atomic_store(&ctx->stop_background_thread, 1, memory_order_relaxed); internal_join_thread(ctx->background_thread); ctx->background_thread = 0; } #endif void DontNeedShadowFor(uptr addr, uptr size) { uptr shadow_beg = MemToShadow(addr); uptr shadow_end = MemToShadow(addr + size); FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); } void MapShadow(uptr addr, uptr size) { // Global data is not 64K aligned, but there are no adjacent mappings, // so we can get away with unaligned mapping. // CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier); // Meta shadow is 2:1, so tread carefully. static bool data_mapped = false; static uptr mapped_meta_end = 0; uptr meta_begin = (uptr)MemToMeta(addr); uptr meta_end = (uptr)MemToMeta(addr + size); meta_begin = RoundDownTo(meta_begin, 64 << 10); meta_end = RoundUpTo(meta_end, 64 << 10); if (!data_mapped) { // First call maps data+bss. data_mapped = true; MmapFixedNoReserve(meta_begin, meta_end - meta_begin); } else { // Mapping continous heap. // Windows wants 64K alignment. meta_begin = RoundDownTo(meta_begin, 64 << 10); meta_end = RoundUpTo(meta_end, 64 << 10); if (meta_end <= mapped_meta_end) return; if (meta_begin < mapped_meta_end) meta_begin = mapped_meta_end; MmapFixedNoReserve(meta_begin, meta_end - meta_begin); mapped_meta_end = meta_end; } VPrintf(2, "mapped meta shadow for (%p-%p) at (%p-%p)\n", addr, addr+size, meta_begin, meta_end); } void MapThreadTrace(uptr addr, uptr size) { DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size); CHECK_GE(addr, kTraceMemBeg); CHECK_LE(addr + size, kTraceMemEnd); CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment uptr addr1 = (uptr)MmapFixedNoReserve(addr, size); if (addr1 != addr) { Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n", addr, size, addr1); Die(); } } static void CheckShadowMapping() { for (uptr i = 0; i < ARRAY_SIZE(UserRegions); i += 2) { const uptr beg = UserRegions[i]; const uptr end = UserRegions[i + 1]; VPrintf(3, "checking shadow region %p-%p\n", beg, end); for (uptr p0 = beg; p0 <= end; p0 += (end - beg) / 4) { for (int x = -1; x <= 1; x++) { const uptr p = p0 + x; if (p < beg || p >= end) continue; const uptr s = MemToShadow(p); VPrintf(3, " checking pointer %p -> %p\n", p, s); CHECK(IsAppMem(p)); CHECK(IsShadowMem(s)); CHECK_EQ(p & ~(kShadowCell - 1), ShadowToMem(s)); const uptr m = (uptr)MemToMeta(p); CHECK(IsMetaMem(m)); } } } } void Initialize(ThreadState *thr) { // Thread safe because done before all threads exist. static bool is_initialized = false; if (is_initialized) return; is_initialized = true; // We are not ready to handle interceptors yet. ScopedIgnoreInterceptors ignore; SanitizerToolName = "ThreadSanitizer"; // Install tool-specific callbacks in sanitizer_common. SetCheckFailedCallback(TsanCheckFailed); ctx = new(ctx_placeholder) Context; const char *options = GetEnv(kTsanOptionsEnv); InitializeFlags(&ctx->flags, options); #ifndef SANITIZER_GO InitializeAllocator(); #endif InitializeInterceptors(); CheckShadowMapping(); InitializePlatform(); InitializeMutex(); InitializeDynamicAnnotations(); #ifndef SANITIZER_GO InitializeShadowMemory(); #endif // Setup correct file descriptor for error reports. __sanitizer_set_report_path(common_flags()->log_path); InitializeSuppressions(); #ifndef SANITIZER_GO InitializeLibIgnore(); Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer); #endif StartBackgroundThread(); #ifndef SANITIZER_GO SetSandboxingCallback(StopBackgroundThread); #endif if (common_flags()->detect_deadlocks) ctx->dd = DDetector::Create(flags()); VPrintf(1, "***** Running under ThreadSanitizer v2 (pid %d) *****\n", (int)internal_getpid()); // Initialize thread 0. int tid = ThreadCreate(thr, 0, 0, true); CHECK_EQ(tid, 0); ThreadStart(thr, tid, internal_getpid()); ctx->initialized = true; if (flags()->stop_on_start) { Printf("ThreadSanitizer is suspended at startup (pid %d)." " Call __tsan_resume().\n", (int)internal_getpid()); while (__tsan_resumed == 0) {} } OnInitialize(); } int Finalize(ThreadState *thr) { bool failed = false; if (flags()->atexit_sleep_ms > 0 && ThreadCount(thr) > 1) SleepForMillis(flags()->atexit_sleep_ms); // Wait for pending reports. ctx->report_mtx.Lock(); CommonSanitizerReportMutex.Lock(); CommonSanitizerReportMutex.Unlock(); ctx->report_mtx.Unlock(); #ifndef SANITIZER_GO - if (common_flags()->verbosity) - AllocatorPrintStats(); + if (Verbosity()) AllocatorPrintStats(); #endif ThreadFinalize(thr); if (ctx->nreported) { failed = true; #ifndef SANITIZER_GO Printf("ThreadSanitizer: reported %d warnings\n", ctx->nreported); #else Printf("Found %d data race(s)\n", ctx->nreported); #endif } if (ctx->nmissed_expected) { failed = true; Printf("ThreadSanitizer: missed %d expected races\n", ctx->nmissed_expected); } if (common_flags()->print_suppressions) PrintMatchedSuppressions(); #ifndef SANITIZER_GO if (flags()->print_benign) PrintMatchedBenignRaces(); #endif failed = OnFinalize(failed); StatAggregate(ctx->stat, thr->stat); StatOutput(ctx->stat); return failed ? flags()->exitcode : 0; } #ifndef SANITIZER_GO void ForkBefore(ThreadState *thr, uptr pc) { ctx->thread_registry->Lock(); ctx->report_mtx.Lock(); } void ForkParentAfter(ThreadState *thr, uptr pc) { ctx->report_mtx.Unlock(); ctx->thread_registry->Unlock(); } void ForkChildAfter(ThreadState *thr, uptr pc) { ctx->report_mtx.Unlock(); ctx->thread_registry->Unlock(); uptr nthread = 0; ctx->thread_registry->GetNumberOfThreads(0, 0, &nthread /* alive threads */); VPrintf(1, "ThreadSanitizer: forked new process with pid %d," " parent had %d threads\n", (int)internal_getpid(), (int)nthread); if (nthread == 1) { internal_start_thread(&BackgroundThread, 0); } else { // We've just forked a multi-threaded process. We cannot reasonably function // after that (some mutexes may be locked before fork). So just enable // ignores for everything in the hope that we will exec soon. ctx->after_multithreaded_fork = true; thr->ignore_interceptors++; ThreadIgnoreBegin(thr, pc); ThreadIgnoreSyncBegin(thr, pc); } } #endif #ifdef SANITIZER_GO NOINLINE void GrowShadowStack(ThreadState *thr) { const int sz = thr->shadow_stack_end - thr->shadow_stack; const int newsz = 2 * sz; uptr *newstack = (uptr*)internal_alloc(MBlockShadowStack, newsz * sizeof(uptr)); internal_memcpy(newstack, thr->shadow_stack, sz * sizeof(uptr)); internal_free(thr->shadow_stack); thr->shadow_stack = newstack; thr->shadow_stack_pos = newstack + sz; thr->shadow_stack_end = newstack + newsz; } #endif u32 CurrentStackId(ThreadState *thr, uptr pc) { if (thr->shadow_stack_pos == 0) // May happen during bootstrap. return 0; if (pc != 0) { #ifndef SANITIZER_GO DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #else if (thr->shadow_stack_pos == thr->shadow_stack_end) GrowShadowStack(thr); #endif thr->shadow_stack_pos[0] = pc; thr->shadow_stack_pos++; } u32 id = StackDepotPut( StackTrace(thr->shadow_stack, thr->shadow_stack_pos - thr->shadow_stack)); if (pc != 0) thr->shadow_stack_pos--; return id; } void TraceSwitch(ThreadState *thr) { thr->nomalloc++; Trace *thr_trace = ThreadTrace(thr->tid); Lock l(&thr_trace->mtx); unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % TraceParts(); TraceHeader *hdr = &thr_trace->headers[trace]; hdr->epoch0 = thr->fast_state.epoch(); ObtainCurrentStack(thr, 0, &hdr->stack0); hdr->mset0 = thr->mset; thr->nomalloc--; } Trace *ThreadTrace(int tid) { return (Trace*)GetThreadTraceHeader(tid); } uptr TraceTopPC(ThreadState *thr) { Event *events = (Event*)GetThreadTrace(thr->tid); uptr pc = events[thr->fast_state.GetTracePos()]; return pc; } uptr TraceSize() { return (uptr)(1ull << (kTracePartSizeBits + flags()->history_size + 1)); } uptr TraceParts() { return TraceSize() / kTracePartSize; } #ifndef SANITIZER_GO extern "C" void __tsan_trace_switch() { TraceSwitch(cur_thread()); } extern "C" void __tsan_report_race() { ReportRace(cur_thread()); } #endif ALWAYS_INLINE Shadow LoadShadow(u64 *p) { u64 raw = atomic_load((atomic_uint64_t*)p, memory_order_relaxed); return Shadow(raw); } ALWAYS_INLINE void StoreShadow(u64 *sp, u64 s) { atomic_store((atomic_uint64_t*)sp, s, memory_order_relaxed); } ALWAYS_INLINE void StoreIfNotYetStored(u64 *sp, u64 *s) { StoreShadow(sp, *s); *s = 0; } ALWAYS_INLINE void HandleRace(ThreadState *thr, u64 *shadow_mem, Shadow cur, Shadow old) { thr->racy_state[0] = cur.raw(); thr->racy_state[1] = old.raw(); thr->racy_shadow_addr = shadow_mem; #ifndef SANITIZER_GO HACKY_CALL(__tsan_report_race); #else ReportRace(thr); #endif } static inline bool HappensBefore(Shadow old, ThreadState *thr) { return thr->clock.get(old.TidWithIgnore()) >= old.epoch(); } ALWAYS_INLINE void MemoryAccessImpl1(ThreadState *thr, uptr addr, int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, u64 *shadow_mem, Shadow cur) { StatInc(thr, StatMop); StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead); StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog)); // This potentially can live in an MMX/SSE scratch register. // The required intrinsics are: // __m128i _mm_move_epi64(__m128i*); // _mm_storel_epi64(u64*, __m128i); u64 store_word = cur.raw(); // scan all the shadow values and dispatch to 4 categories: // same, replace, candidate and race (see comments below). // we consider only 3 cases regarding access sizes: // equal, intersect and not intersect. initially I considered // larger and smaller as well, it allowed to replace some // 'candidates' with 'same' or 'replace', but I think // it's just not worth it (performance- and complexity-wise). Shadow old(0); - if (kShadowCnt == 1) { - int idx = 0; + + // It release mode we manually unroll the loop, + // because empirically gcc generates better code this way. + // However, we can't afford unrolling in debug mode, because the function + // consumes almost 4K of stack. Gtest gives only 4K of stack to death test + // threads, which is not enough for the unrolled loop. +#if SANITIZER_DEBUG + for (int idx = 0; idx < 4; idx++) { #include "tsan_update_shadow_word_inl.h" - } else if (kShadowCnt == 2) { - int idx = 0; + } +#else + int idx = 0; #include "tsan_update_shadow_word_inl.h" - idx = 1; + idx = 1; #include "tsan_update_shadow_word_inl.h" - } else if (kShadowCnt == 4) { - int idx = 0; + idx = 2; #include "tsan_update_shadow_word_inl.h" - idx = 1; + idx = 3; #include "tsan_update_shadow_word_inl.h" - idx = 2; -#include "tsan_update_shadow_word_inl.h" - idx = 3; -#include "tsan_update_shadow_word_inl.h" - } else if (kShadowCnt == 8) { - int idx = 0; -#include "tsan_update_shadow_word_inl.h" - idx = 1; -#include "tsan_update_shadow_word_inl.h" - idx = 2; -#include "tsan_update_shadow_word_inl.h" - idx = 3; -#include "tsan_update_shadow_word_inl.h" - idx = 4; -#include "tsan_update_shadow_word_inl.h" - idx = 5; -#include "tsan_update_shadow_word_inl.h" - idx = 6; -#include "tsan_update_shadow_word_inl.h" - idx = 7; -#include "tsan_update_shadow_word_inl.h" - } else { - CHECK(false); - } +#endif // we did not find any races and had already stored // the current access info, so we are done if (LIKELY(store_word == 0)) return; // choose a random candidate slot and replace it StoreShadow(shadow_mem + (cur.epoch() % kShadowCnt), store_word); StatInc(thr, StatShadowReplace); return; RACE: HandleRace(thr, shadow_mem, cur, old); return; } void UnalignedMemoryAccess(ThreadState *thr, uptr pc, uptr addr, int size, bool kAccessIsWrite, bool kIsAtomic) { while (size) { int size1 = 1; int kAccessSizeLog = kSizeLog1; if (size >= 8 && (addr & ~7) == ((addr + 7) & ~7)) { size1 = 8; kAccessSizeLog = kSizeLog8; } else if (size >= 4 && (addr & ~7) == ((addr + 3) & ~7)) { size1 = 4; kAccessSizeLog = kSizeLog4; } else if (size >= 2 && (addr & ~7) == ((addr + 1) & ~7)) { size1 = 2; kAccessSizeLog = kSizeLog2; } MemoryAccess(thr, pc, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic); addr += size1; size -= size1; } } ALWAYS_INLINE bool ContainsSameAccessSlow(u64 *s, u64 a, u64 sync_epoch, bool is_write) { Shadow cur(a); for (uptr i = 0; i < kShadowCnt; i++) { Shadow old(LoadShadow(&s[i])); if (Shadow::Addr0AndSizeAreEqual(cur, old) && old.TidWithIgnore() == cur.TidWithIgnore() && old.epoch() > sync_epoch && old.IsAtomic() == cur.IsAtomic() && old.IsRead() <= cur.IsRead()) return true; } return false; } -#if defined(__SSE3__) && TSAN_SHADOW_COUNT == 4 +#if defined(__SSE3__) #define SHUF(v0, v1, i0, i1, i2, i3) _mm_castps_si128(_mm_shuffle_ps( \ _mm_castsi128_ps(v0), _mm_castsi128_ps(v1), \ (i0)*1 + (i1)*4 + (i2)*16 + (i3)*64)) ALWAYS_INLINE bool ContainsSameAccessFast(u64 *s, u64 a, u64 sync_epoch, bool is_write) { // This is an optimized version of ContainsSameAccessSlow. // load current access into access[0:63] const m128 access = _mm_cvtsi64_si128(a); // duplicate high part of access in addr0: // addr0[0:31] = access[32:63] // addr0[32:63] = access[32:63] // addr0[64:95] = access[32:63] // addr0[96:127] = access[32:63] const m128 addr0 = SHUF(access, access, 1, 1, 1, 1); // load 4 shadow slots const m128 shadow0 = _mm_load_si128((__m128i*)s); const m128 shadow1 = _mm_load_si128((__m128i*)s + 1); // load high parts of 4 shadow slots into addr_vect: // addr_vect[0:31] = shadow0[32:63] // addr_vect[32:63] = shadow0[96:127] // addr_vect[64:95] = shadow1[32:63] // addr_vect[96:127] = shadow1[96:127] m128 addr_vect = SHUF(shadow0, shadow1, 1, 3, 1, 3); if (!is_write) { // set IsRead bit in addr_vect const m128 rw_mask1 = _mm_cvtsi64_si128(1<<15); const m128 rw_mask = SHUF(rw_mask1, rw_mask1, 0, 0, 0, 0); addr_vect = _mm_or_si128(addr_vect, rw_mask); } // addr0 == addr_vect? const m128 addr_res = _mm_cmpeq_epi32(addr0, addr_vect); // epoch1[0:63] = sync_epoch const m128 epoch1 = _mm_cvtsi64_si128(sync_epoch); // epoch[0:31] = sync_epoch[0:31] // epoch[32:63] = sync_epoch[0:31] // epoch[64:95] = sync_epoch[0:31] // epoch[96:127] = sync_epoch[0:31] const m128 epoch = SHUF(epoch1, epoch1, 0, 0, 0, 0); // load low parts of shadow cell epochs into epoch_vect: // epoch_vect[0:31] = shadow0[0:31] // epoch_vect[32:63] = shadow0[64:95] // epoch_vect[64:95] = shadow1[0:31] // epoch_vect[96:127] = shadow1[64:95] const m128 epoch_vect = SHUF(shadow0, shadow1, 0, 2, 0, 2); // epoch_vect >= sync_epoch? const m128 epoch_res = _mm_cmpgt_epi32(epoch_vect, epoch); // addr_res & epoch_res const m128 res = _mm_and_si128(addr_res, epoch_res); // mask[0] = res[7] // mask[1] = res[15] // ... // mask[15] = res[127] const int mask = _mm_movemask_epi8(res); return mask != 0; } #endif ALWAYS_INLINE bool ContainsSameAccess(u64 *s, u64 a, u64 sync_epoch, bool is_write) { -#if defined(__SSE3__) && TSAN_SHADOW_COUNT == 4 +#if defined(__SSE3__) bool res = ContainsSameAccessFast(s, a, sync_epoch, is_write); // NOTE: this check can fail if the shadow is concurrently mutated - // by other threads. - DCHECK_EQ(res, ContainsSameAccessSlow(s, a, sync_epoch, is_write)); + // by other threads. But it still can be useful if you modify + // ContainsSameAccessFast and want to ensure that it's not completely broken. + // DCHECK_EQ(res, ContainsSameAccessSlow(s, a, sync_epoch, is_write)); return res; #else return ContainsSameAccessSlow(s, a, sync_epoch, is_write); #endif } ALWAYS_INLINE USED void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic) { u64 *shadow_mem = (u64*)MemToShadow(addr); DPrintf2("#%d: MemoryAccess: @%p %p size=%d" " is_write=%d shadow_mem=%p {%zx, %zx, %zx, %zx}\n", (int)thr->fast_state.tid(), (void*)pc, (void*)addr, (int)(1 << kAccessSizeLog), kAccessIsWrite, shadow_mem, (uptr)shadow_mem[0], (uptr)shadow_mem[1], (uptr)shadow_mem[2], (uptr)shadow_mem[3]); -#if TSAN_DEBUG +#if SANITIZER_DEBUG if (!IsAppMem(addr)) { Printf("Access to non app mem %zx\n", addr); DCHECK(IsAppMem(addr)); } if (!IsShadowMem((uptr)shadow_mem)) { Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr); DCHECK(IsShadowMem((uptr)shadow_mem)); } #endif if (kCppMode && *shadow_mem == kShadowRodata) { // Access to .rodata section, no races here. // Measurements show that it can be 10-20% of all memory accesses. StatInc(thr, StatMop); StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead); StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog)); StatInc(thr, StatMopRodata); return; } FastState fast_state = thr->fast_state; if (fast_state.GetIgnoreBit()) { StatInc(thr, StatMop); StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead); StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog)); StatInc(thr, StatMopIgnored); return; } Shadow cur(fast_state); cur.SetAddr0AndSizeLog(addr & 7, kAccessSizeLog); cur.SetWrite(kAccessIsWrite); cur.SetAtomic(kIsAtomic); if (LIKELY(ContainsSameAccess(shadow_mem, cur.raw(), thr->fast_synch_epoch, kAccessIsWrite))) { StatInc(thr, StatMop); StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead); StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog)); StatInc(thr, StatMopSame); return; } if (kCollectHistory) { fast_state.IncrementEpoch(); thr->fast_state = fast_state; TraceAddEvent(thr, fast_state, EventTypeMop, pc); cur.IncrementEpoch(); } MemoryAccessImpl1(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic, shadow_mem, cur); } // Called by MemoryAccessRange in tsan_rtl_thread.cc ALWAYS_INLINE USED void MemoryAccessImpl(ThreadState *thr, uptr addr, int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, u64 *shadow_mem, Shadow cur) { if (LIKELY(ContainsSameAccess(shadow_mem, cur.raw(), thr->fast_synch_epoch, kAccessIsWrite))) { StatInc(thr, StatMop); StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead); StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog)); StatInc(thr, StatMopSame); return; } MemoryAccessImpl1(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic, shadow_mem, cur); } static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size, u64 val) { (void)thr; (void)pc; if (size == 0) return; // FIXME: fix me. uptr offset = addr % kShadowCell; if (offset) { offset = kShadowCell - offset; if (size <= offset) return; addr += offset; size -= offset; } DCHECK_EQ(addr % 8, 0); // If a user passes some insane arguments (memset(0)), // let it just crash as usual. if (!IsAppMem(addr) || !IsAppMem(addr + size - 1)) return; // Don't want to touch lots of shadow memory. // If a program maps 10MB stack, there is no need reset the whole range. size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1); // UnmapOrDie/MmapFixedNoReserve does not work on Windows, // so we do it only for C/C++. if (kGoMode || size < common_flags()->clear_shadow_mmap_threshold) { u64 *p = (u64*)MemToShadow(addr); CHECK(IsShadowMem((uptr)p)); CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1))); // FIXME: may overwrite a part outside the region for (uptr i = 0; i < size / kShadowCell * kShadowCnt;) { p[i++] = val; for (uptr j = 1; j < kShadowCnt; j++) p[i++] = 0; } } else { // The region is big, reset only beginning and end. const uptr kPageSize = 4096; u64 *begin = (u64*)MemToShadow(addr); u64 *end = begin + size / kShadowCell * kShadowCnt; u64 *p = begin; // Set at least first kPageSize/2 to page boundary. while ((p < begin + kPageSize / kShadowSize / 2) || ((uptr)p % kPageSize)) { *p++ = val; for (uptr j = 1; j < kShadowCnt; j++) *p++ = 0; } // Reset middle part. u64 *p1 = p; p = RoundDown(end, kPageSize); UnmapOrDie((void*)p1, (uptr)p - (uptr)p1); MmapFixedNoReserve((uptr)p1, (uptr)p - (uptr)p1); // Set the ending. while (p < end) { *p++ = val; for (uptr j = 1; j < kShadowCnt; j++) *p++ = 0; } } } void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) { MemoryRangeSet(thr, pc, addr, size, 0); } void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) { // Processing more than 1k (4k of shadow) is expensive, // can cause excessive memory consumption (user does not necessary touch // the whole range) and most likely unnecessary. if (size > 1024) size = 1024; CHECK_EQ(thr->is_freeing, false); thr->is_freeing = true; MemoryAccessRange(thr, pc, addr, size, true); thr->is_freeing = false; if (kCollectHistory) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc); } Shadow s(thr->fast_state); s.ClearIgnoreBit(); s.MarkAsFreed(); s.SetWrite(true); s.SetAddr0AndSizeLog(0, 3); MemoryRangeSet(thr, pc, addr, size, s.raw()); } void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) { if (kCollectHistory) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc); } Shadow s(thr->fast_state); s.ClearIgnoreBit(); s.SetWrite(true); s.SetAddr0AndSizeLog(0, 3); MemoryRangeSet(thr, pc, addr, size, s.raw()); } ALWAYS_INLINE USED void FuncEntry(ThreadState *thr, uptr pc) { StatInc(thr, StatFuncEnter); DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc); if (kCollectHistory) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeFuncEnter, pc); } // Shadow stack maintenance can be replaced with // stack unwinding during trace switch (which presumably must be faster). DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack); #ifndef SANITIZER_GO DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #else if (thr->shadow_stack_pos == thr->shadow_stack_end) GrowShadowStack(thr); #endif thr->shadow_stack_pos[0] = pc; thr->shadow_stack_pos++; } ALWAYS_INLINE USED void FuncExit(ThreadState *thr) { StatInc(thr, StatFuncExit); DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid()); if (kCollectHistory) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0); } DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack); #ifndef SANITIZER_GO DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #endif thr->shadow_stack_pos--; } void ThreadIgnoreBegin(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid); thr->ignore_reads_and_writes++; CHECK_GT(thr->ignore_reads_and_writes, 0); thr->fast_state.SetIgnoreBit(); #ifndef SANITIZER_GO if (!ctx->after_multithreaded_fork) thr->mop_ignore_set.Add(CurrentStackId(thr, pc)); #endif } void ThreadIgnoreEnd(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid); thr->ignore_reads_and_writes--; CHECK_GE(thr->ignore_reads_and_writes, 0); if (thr->ignore_reads_and_writes == 0) { thr->fast_state.ClearIgnoreBit(); #ifndef SANITIZER_GO thr->mop_ignore_set.Reset(); #endif } } void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid); thr->ignore_sync++; CHECK_GT(thr->ignore_sync, 0); #ifndef SANITIZER_GO if (!ctx->after_multithreaded_fork) thr->sync_ignore_set.Add(CurrentStackId(thr, pc)); #endif } void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid); thr->ignore_sync--; CHECK_GE(thr->ignore_sync, 0); #ifndef SANITIZER_GO if (thr->ignore_sync == 0) thr->sync_ignore_set.Reset(); #endif } bool MD5Hash::operator==(const MD5Hash &other) const { return hash[0] == other.hash[0] && hash[1] == other.hash[1]; } -#if TSAN_DEBUG +#if SANITIZER_DEBUG void build_consistency_debug() {} #else void build_consistency_release() {} #endif #if TSAN_COLLECT_STATS void build_consistency_stats() {} #else void build_consistency_nostats() {} -#endif - -#if TSAN_SHADOW_COUNT == 1 -void build_consistency_shadow1() {} -#elif TSAN_SHADOW_COUNT == 2 -void build_consistency_shadow2() {} -#elif TSAN_SHADOW_COUNT == 4 -void build_consistency_shadow4() {} -#else -void build_consistency_shadow8() {} #endif } // namespace __tsan #ifndef SANITIZER_GO // Must be included in this file to make sure everything is inlined. #include "tsan_interface_inl.h" #endif Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl.h (revision 279191) @@ -1,734 +1,734 @@ //===-- tsan_rtl.h ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // // Main internal TSan header file. // // Ground rules: // - C++ run-time should not be used (static CTORs, RTTI, exceptions, static // function-scope locals) // - All functions/classes/etc reside in namespace __tsan, except for those // declared in tsan_interface.h. // - Platform-specific files should be used instead of ifdefs (*). // - No system headers included in header files (*). // - Platform specific headres included only into platform-specific files (*). // // (*) Except when inlining is critical for performance. //===----------------------------------------------------------------------===// #ifndef TSAN_RTL_H #define TSAN_RTL_H #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_asm.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_deadlock_detector_interface.h" #include "sanitizer_common/sanitizer_libignore.h" #include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_thread_registry.h" #include "tsan_clock.h" #include "tsan_defs.h" #include "tsan_flags.h" #include "tsan_sync.h" #include "tsan_trace.h" #include "tsan_vector.h" #include "tsan_report.h" #include "tsan_platform.h" #include "tsan_mutexset.h" #include "tsan_ignoreset.h" #include "tsan_stack_trace.h" #if SANITIZER_WORDSIZE != 64 # error "ThreadSanitizer is supported only on 64-bit platforms" #endif namespace __tsan { #ifndef SANITIZER_GO struct MapUnmapCallback; typedef SizeClassAllocator64 PrimaryAllocator; typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator SecondaryAllocator; typedef CombinedAllocator Allocator; Allocator *allocator(); #endif void TsanCheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); const u64 kShadowRodata = (u64)-1; // .rodata shadow marker // FastState (from most significant bit): // ignore : 1 // tid : kTidBits // unused : - // history_size : 3 // epoch : kClkBits class FastState { public: FastState(u64 tid, u64 epoch) { x_ = tid << kTidShift; x_ |= epoch; DCHECK_EQ(tid, this->tid()); DCHECK_EQ(epoch, this->epoch()); DCHECK_EQ(GetIgnoreBit(), false); } explicit FastState(u64 x) : x_(x) { } u64 raw() const { return x_; } u64 tid() const { u64 res = (x_ & ~kIgnoreBit) >> kTidShift; return res; } u64 TidWithIgnore() const { u64 res = x_ >> kTidShift; return res; } u64 epoch() const { u64 res = x_ & ((1ull << kClkBits) - 1); return res; } void IncrementEpoch() { u64 old_epoch = epoch(); x_ += 1; DCHECK_EQ(old_epoch + 1, epoch()); (void)old_epoch; } void SetIgnoreBit() { x_ |= kIgnoreBit; } void ClearIgnoreBit() { x_ &= ~kIgnoreBit; } bool GetIgnoreBit() const { return (s64)x_ < 0; } void SetHistorySize(int hs) { CHECK_GE(hs, 0); CHECK_LE(hs, 7); x_ = (x_ & ~(kHistoryMask << kHistoryShift)) | (u64(hs) << kHistoryShift); } ALWAYS_INLINE int GetHistorySize() const { return (int)((x_ >> kHistoryShift) & kHistoryMask); } void ClearHistorySize() { SetHistorySize(0); } ALWAYS_INLINE u64 GetTracePos() const { const int hs = GetHistorySize(); // When hs == 0, the trace consists of 2 parts. const u64 mask = (1ull << (kTracePartSizeBits + hs + 1)) - 1; return epoch() & mask; } private: friend class Shadow; static const int kTidShift = 64 - kTidBits - 1; static const u64 kIgnoreBit = 1ull << 63; static const u64 kFreedBit = 1ull << 63; static const u64 kHistoryShift = kClkBits; static const u64 kHistoryMask = 7; u64 x_; }; // Shadow (from most significant bit): // freed : 1 // tid : kTidBits // is_atomic : 1 // is_read : 1 // size_log : 2 // addr0 : 3 // epoch : kClkBits class Shadow : public FastState { public: explicit Shadow(u64 x) : FastState(x) { } explicit Shadow(const FastState &s) : FastState(s.x_) { ClearHistorySize(); } void SetAddr0AndSizeLog(u64 addr0, unsigned kAccessSizeLog) { DCHECK_EQ((x_ >> kClkBits) & 31, 0); DCHECK_LE(addr0, 7); DCHECK_LE(kAccessSizeLog, 3); x_ |= ((kAccessSizeLog << 3) | addr0) << kClkBits; DCHECK_EQ(kAccessSizeLog, size_log()); DCHECK_EQ(addr0, this->addr0()); } void SetWrite(unsigned kAccessIsWrite) { DCHECK_EQ(x_ & kReadBit, 0); if (!kAccessIsWrite) x_ |= kReadBit; DCHECK_EQ(kAccessIsWrite, IsWrite()); } void SetAtomic(bool kIsAtomic) { DCHECK(!IsAtomic()); if (kIsAtomic) x_ |= kAtomicBit; DCHECK_EQ(IsAtomic(), kIsAtomic); } bool IsAtomic() const { return x_ & kAtomicBit; } bool IsZero() const { return x_ == 0; } static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) { u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift; DCHECK_EQ(shifted_xor == 0, s1.TidWithIgnore() == s2.TidWithIgnore()); return shifted_xor == 0; } static ALWAYS_INLINE bool Addr0AndSizeAreEqual(const Shadow s1, const Shadow s2) { u64 masked_xor = ((s1.x_ ^ s2.x_) >> kClkBits) & 31; return masked_xor == 0; } static ALWAYS_INLINE bool TwoRangesIntersect(Shadow s1, Shadow s2, unsigned kS2AccessSize) { bool res = false; u64 diff = s1.addr0() - s2.addr0(); if ((s64)diff < 0) { // s1.addr0 < s2.addr0 // NOLINT // if (s1.addr0() + size1) > s2.addr0()) return true; if (s1.size() > -diff) res = true; } else { // if (s2.addr0() + kS2AccessSize > s1.addr0()) return true; if (kS2AccessSize > diff) res = true; } DCHECK_EQ(res, TwoRangesIntersectSlow(s1, s2)); DCHECK_EQ(res, TwoRangesIntersectSlow(s2, s1)); return res; } u64 ALWAYS_INLINE addr0() const { return (x_ >> kClkBits) & 7; } u64 ALWAYS_INLINE size() const { return 1ull << size_log(); } bool ALWAYS_INLINE IsWrite() const { return !IsRead(); } bool ALWAYS_INLINE IsRead() const { return x_ & kReadBit; } // The idea behind the freed bit is as follows. // When the memory is freed (or otherwise unaccessible) we write to the shadow // values with tid/epoch related to the free and the freed bit set. // During memory accesses processing the freed bit is considered // as msb of tid. So any access races with shadow with freed bit set // (it is as if write from a thread with which we never synchronized before). // This allows us to detect accesses to freed memory w/o additional // overheads in memory access processing and at the same time restore // tid/epoch of free. void MarkAsFreed() { x_ |= kFreedBit; } bool IsFreed() const { return x_ & kFreedBit; } bool GetFreedAndReset() { bool res = x_ & kFreedBit; x_ &= ~kFreedBit; return res; } bool ALWAYS_INLINE IsBothReadsOrAtomic(bool kIsWrite, bool kIsAtomic) const { bool v = x_ & ((u64(kIsWrite ^ 1) << kReadShift) | (u64(kIsAtomic) << kAtomicShift)); DCHECK_EQ(v, (!IsWrite() && !kIsWrite) || (IsAtomic() && kIsAtomic)); return v; } bool ALWAYS_INLINE IsRWNotWeaker(bool kIsWrite, bool kIsAtomic) const { bool v = ((x_ >> kReadShift) & 3) <= u64((kIsWrite ^ 1) | (kIsAtomic << 1)); DCHECK_EQ(v, (IsAtomic() < kIsAtomic) || (IsAtomic() == kIsAtomic && !IsWrite() <= !kIsWrite)); return v; } bool ALWAYS_INLINE IsRWWeakerOrEqual(bool kIsWrite, bool kIsAtomic) const { bool v = ((x_ >> kReadShift) & 3) >= u64((kIsWrite ^ 1) | (kIsAtomic << 1)); DCHECK_EQ(v, (IsAtomic() > kIsAtomic) || (IsAtomic() == kIsAtomic && !IsWrite() >= !kIsWrite)); return v; } private: static const u64 kReadShift = 5 + kClkBits; static const u64 kReadBit = 1ull << kReadShift; static const u64 kAtomicShift = 6 + kClkBits; static const u64 kAtomicBit = 1ull << kAtomicShift; u64 size_log() const { return (x_ >> (3 + kClkBits)) & 3; } static bool TwoRangesIntersectSlow(const Shadow s1, const Shadow s2) { if (s1.addr0() == s2.addr0()) return true; if (s1.addr0() < s2.addr0() && s1.addr0() + s1.size() > s2.addr0()) return true; if (s2.addr0() < s1.addr0() && s2.addr0() + s2.size() > s1.addr0()) return true; return false; } }; struct SignalContext; struct JmpBuf { uptr sp; uptr mangled_sp; int int_signal_send; bool in_blocking_func; uptr in_signal_handler; uptr *shadow_stack_pos; }; // This struct is stored in TLS. struct ThreadState { FastState fast_state; // Synch epoch represents the threads's epoch before the last synchronization // action. It allows to reduce number of shadow state updates. // For example, fast_synch_epoch=100, last write to addr X was at epoch=150, // if we are processing write to X from the same thread at epoch=200, // we do nothing, because both writes happen in the same 'synch epoch'. // That is, if another memory access does not race with the former write, // it does not race with the latter as well. // QUESTION: can we can squeeze this into ThreadState::Fast? // E.g. ThreadState::Fast is a 44-bit, 32 are taken by synch_epoch and 12 are // taken by epoch between synchs. // This way we can save one load from tls. u64 fast_synch_epoch; // This is a slow path flag. On fast path, fast_state.GetIgnoreBit() is read. // We do not distinguish beteween ignoring reads and writes // for better performance. int ignore_reads_and_writes; int ignore_sync; // Go does not support ignores. #ifndef SANITIZER_GO IgnoreSet mop_ignore_set; IgnoreSet sync_ignore_set; #endif // C/C++ uses fixed size shadow stack embed into Trace. // Go uses malloc-allocated shadow stack with dynamic size. uptr *shadow_stack; uptr *shadow_stack_end; uptr *shadow_stack_pos; u64 *racy_shadow_addr; u64 racy_state[2]; MutexSet mset; ThreadClock clock; #ifndef SANITIZER_GO AllocatorCache alloc_cache; InternalAllocatorCache internal_alloc_cache; Vector jmp_bufs; int ignore_interceptors; #endif u64 stat[StatCnt]; const int tid; const int unique_id; bool in_symbolizer; bool in_ignored_lib; bool is_dead; bool is_freeing; bool is_vptr_access; const uptr stk_addr; const uptr stk_size; const uptr tls_addr; const uptr tls_size; ThreadContext *tctx; InternalDeadlockDetector internal_deadlock_detector; DDPhysicalThread *dd_pt; DDLogicalThread *dd_lt; atomic_uintptr_t in_signal_handler; SignalContext *signal_ctx; DenseSlabAllocCache block_cache; DenseSlabAllocCache sync_cache; DenseSlabAllocCache clock_cache; #ifndef SANITIZER_GO u32 last_sleep_stack_id; ThreadClock last_sleep_clock; #endif // Set in regions of runtime that must be signal-safe and fork-safe. // If set, malloc must not be called. int nomalloc; explicit ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, unsigned reuse_count, uptr stk_addr, uptr stk_size, uptr tls_addr, uptr tls_size); }; #ifndef SANITIZER_GO __attribute__((tls_model("initial-exec"))) extern THREADLOCAL char cur_thread_placeholder[]; INLINE ThreadState *cur_thread() { return reinterpret_cast(&cur_thread_placeholder); } #endif class ThreadContext : public ThreadContextBase { public: explicit ThreadContext(int tid); ~ThreadContext(); ThreadState *thr; u32 creation_stack_id; SyncClock sync; // Epoch at which the thread had started. // If we see an event from the thread stamped by an older epoch, // the event is from a dead thread that shared tid with this thread. u64 epoch0; u64 epoch1; // Override superclass callbacks. void OnDead(); void OnJoined(void *arg); void OnFinished(); void OnStarted(void *arg); void OnCreated(void *arg); void OnReset(); void OnDetached(void *arg); }; struct RacyStacks { MD5Hash hash[2]; bool operator==(const RacyStacks &other) const { if (hash[0] == other.hash[0] && hash[1] == other.hash[1]) return true; if (hash[0] == other.hash[1] && hash[1] == other.hash[0]) return true; return false; } }; struct RacyAddress { uptr addr_min; uptr addr_max; }; struct FiredSuppression { ReportType type; uptr pc; Suppression *supp; }; struct Context { Context(); bool initialized; bool after_multithreaded_fork; MetaMap metamap; Mutex report_mtx; int nreported; int nmissed_expected; atomic_uint64_t last_symbolize_time_ns; void *background_thread; atomic_uint32_t stop_background_thread; ThreadRegistry *thread_registry; Vector racy_stacks; Vector racy_addresses; // Number of fired suppressions may be large enough. InternalMmapVector fired_suppressions; DDetector *dd; ClockAlloc clock_alloc; Flags flags; u64 stat[StatCnt]; u64 int_alloc_cnt[MBlockTypeCount]; u64 int_alloc_siz[MBlockTypeCount]; }; extern Context *ctx; // The one and the only global runtime context. struct ScopedIgnoreInterceptors { ScopedIgnoreInterceptors() { #ifndef SANITIZER_GO cur_thread()->ignore_interceptors++; #endif } ~ScopedIgnoreInterceptors() { #ifndef SANITIZER_GO cur_thread()->ignore_interceptors--; #endif } }; class ScopedReport { public: explicit ScopedReport(ReportType typ); ~ScopedReport(); void AddMemoryAccess(uptr addr, Shadow s, StackTrace stack, const MutexSet *mset); void AddStack(StackTrace stack, bool suppressable = false); void AddThread(const ThreadContext *tctx, bool suppressable = false); void AddThread(int unique_tid, bool suppressable = false); void AddUniqueTid(int unique_tid); void AddMutex(const SyncVar *s); u64 AddMutex(u64 id); void AddLocation(uptr addr, uptr size); void AddSleep(u32 stack_id); void SetCount(int count); const ReportDesc *GetReport() const; private: ReportDesc *rep_; // Symbolizer makes lots of intercepted calls. If we try to process them, // at best it will cause deadlocks on internal mutexes. ScopedIgnoreInterceptors ignore_interceptors_; void AddDeadMutex(u64 id); ScopedReport(const ScopedReport&); void operator = (const ScopedReport&); }; void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, MutexSet *mset); template void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) { uptr size = thr->shadow_stack_pos - thr->shadow_stack; uptr start = 0; if (size + !!toppc > kStackTraceMax) { start = size + !!toppc - kStackTraceMax; size = kStackTraceMax - !!toppc; } stack->Init(&thr->shadow_stack[start], size, toppc); } void StatAggregate(u64 *dst, u64 *src); void StatOutput(u64 *stat); void ALWAYS_INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) { if (kCollectStats) thr->stat[typ] += n; } void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) { if (kCollectStats) thr->stat[typ] = n; } void MapShadow(uptr addr, uptr size); void MapThreadTrace(uptr addr, uptr size); void DontNeedShadowFor(uptr addr, uptr size); void InitializeShadowMemory(); void InitializeInterceptors(); void InitializeLibIgnore(); void InitializeDynamicAnnotations(); void ForkBefore(ThreadState *thr, uptr pc); void ForkParentAfter(ThreadState *thr, uptr pc); void ForkChildAfter(ThreadState *thr, uptr pc); void ReportRace(ThreadState *thr); bool OutputReport(ThreadState *thr, const ScopedReport &srep); bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, StackTrace trace); bool IsExpectedReport(uptr addr, uptr size); void PrintMatchedBenignRaces(); #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1 # define DPrintf Printf #else # define DPrintf(...) #endif #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 2 # define DPrintf2 Printf #else # define DPrintf2(...) #endif u32 CurrentStackId(ThreadState *thr, uptr pc); ReportStack *SymbolizeStackId(u32 stack_id); void PrintCurrentStack(ThreadState *thr, uptr pc); void PrintCurrentStackSlow(uptr pc); // uses libunwind void Initialize(ThreadState *thr); int Finalize(ThreadState *thr); void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write); void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write); void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic); void MemoryAccessImpl(ThreadState *thr, uptr addr, int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic, u64 *shadow_mem, Shadow cur); void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, uptr size, bool is_write); void MemoryAccessRangeStep(ThreadState *thr, uptr pc, uptr addr, uptr size, uptr step, bool is_write); void UnalignedMemoryAccess(ThreadState *thr, uptr pc, uptr addr, int size, bool kAccessIsWrite, bool kIsAtomic); const int kSizeLog1 = 0; const int kSizeLog2 = 1; const int kSizeLog4 = 2; const int kSizeLog8 = 3; void ALWAYS_INLINE MemoryRead(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, false, false); } void ALWAYS_INLINE MemoryWrite(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, true, false); } void ALWAYS_INLINE MemoryReadAtomic(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, false, true); } void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, uptr addr, int kAccessSizeLog) { MemoryAccess(thr, pc, addr, kAccessSizeLog, true, true); } void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); void ThreadIgnoreBegin(ThreadState *thr, uptr pc); void ThreadIgnoreEnd(ThreadState *thr, uptr pc); void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc); void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc); void FuncEntry(ThreadState *thr, uptr pc); void FuncExit(ThreadState *thr); int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached); void ThreadStart(ThreadState *thr, int tid, uptr os_id); void ThreadFinish(ThreadState *thr); int ThreadTid(ThreadState *thr, uptr pc, uptr uid); void ThreadJoin(ThreadState *thr, uptr pc, int tid); void ThreadDetach(ThreadState *thr, uptr pc, int tid); void ThreadFinalize(ThreadState *thr); void ThreadSetName(ThreadState *thr, const char *name); int ThreadCount(ThreadState *thr); void ProcessPendingSignals(ThreadState *thr); void MutexCreate(ThreadState *thr, uptr pc, uptr addr, bool rw, bool recursive, bool linker_init); void MutexDestroy(ThreadState *thr, uptr pc, uptr addr); void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec = 1, bool try_lock = false); int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false); void MutexReadLock(ThreadState *thr, uptr pc, uptr addr, bool try_lock = false); void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD void Acquire(ThreadState *thr, uptr pc, uptr addr); // AcquireGlobal synchronizes the current thread with all other threads. // In terms of happens-before relation, it draws a HB edge from all threads // (where they happen to execute right now) to the current thread. We use it to // handle Go finalizers. Namely, finalizer goroutine executes AcquireGlobal // right before executing finalizers. This provides a coarse, but simple // approximation of the actual required synchronization. void AcquireGlobal(ThreadState *thr, uptr pc); void Release(ThreadState *thr, uptr pc, uptr addr); void ReleaseStore(ThreadState *thr, uptr pc, uptr addr); void AfterSleep(ThreadState *thr, uptr pc); void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c); void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c); void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); // The hacky call uses custom calling convention and an assembly thunk. // It is considerably faster that a normal call for the caller // if it is not executed (it is intended for slow paths from hot functions). // The trick is that the call preserves all registers and the compiler // does not treat it as a call. // If it does not work for you, use normal call. -#if TSAN_DEBUG == 0 +#if !SANITIZER_DEBUG && defined(__x86_64__) // The caller may not create the stack frame for itself at all, // so we create a reserve stack frame for it (1024b must be enough). #define HACKY_CALL(f) \ __asm__ __volatile__("sub $1024, %%rsp;" \ CFI_INL_ADJUST_CFA_OFFSET(1024) \ ".hidden " #f "_thunk;" \ "call " #f "_thunk;" \ "add $1024, %%rsp;" \ CFI_INL_ADJUST_CFA_OFFSET(-1024) \ ::: "memory", "cc"); #else #define HACKY_CALL(f) f() #endif void TraceSwitch(ThreadState *thr); uptr TraceTopPC(ThreadState *thr); uptr TraceSize(); uptr TraceParts(); Trace *ThreadTrace(int tid); extern "C" void __tsan_trace_switch(); void ALWAYS_INLINE TraceAddEvent(ThreadState *thr, FastState fs, EventType typ, u64 addr) { if (!kCollectHistory) return; DCHECK_GE((int)typ, 0); DCHECK_LE((int)typ, 7); DCHECK_EQ(GetLsb(addr, 61), addr); StatInc(thr, StatEvents); u64 pos = fs.GetTracePos(); if (UNLIKELY((pos % kTracePartSize) == 0)) { #ifndef SANITIZER_GO HACKY_CALL(__tsan_trace_switch); #else TraceSwitch(thr); #endif } Event *trace = (Event*)GetThreadTrace(fs.tid()); Event *evp = &trace[pos]; Event ev = (u64)addr | ((u64)typ << 61); *evp = ev; } } // namespace __tsan #endif // TSAN_RTL_H Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cc (revision 279191) @@ -1,692 +1,693 @@ //===-- tsan_rtl_report.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_suppressions.h" #include "tsan_symbolize.h" #include "tsan_report.h" #include "tsan_sync.h" #include "tsan_mman.h" #include "tsan_flags.h" #include "tsan_fd.h" namespace __tsan { using namespace __sanitizer; // NOLINT static ReportStack *SymbolizeStack(StackTrace trace); void TsanCheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { // There is high probability that interceptors will check-fail as well, // on the other hand there is no sense in processing interceptors // since we are going to die soon. ScopedIgnoreInterceptors ignore; Printf("FATAL: ThreadSanitizer CHECK failed: " "%s:%d \"%s\" (0x%zx, 0x%zx)\n", file, line, cond, (uptr)v1, (uptr)v2); PrintCurrentStackSlow(StackTrace::GetCurrentPc()); Die(); } // Can be overriden by an application/test to intercept reports. #ifdef TSAN_EXTERNAL_HOOKS bool OnReport(const ReportDesc *rep, bool suppressed); #else SANITIZER_INTERFACE_ATTRIBUTE bool WEAK OnReport(const ReportDesc *rep, bool suppressed) { (void)rep; return suppressed; } #endif static void StackStripMain(SymbolizedStack *frames) { SymbolizedStack *last_frame = nullptr; SymbolizedStack *last_frame2 = nullptr; for (SymbolizedStack *cur = frames; cur; cur = cur->next) { last_frame2 = last_frame; last_frame = cur; } if (last_frame2 == 0) return; #ifndef SANITIZER_GO const char *last = last_frame->info.function; const char *last2 = last_frame2->info.function; // Strip frame above 'main' if (last2 && 0 == internal_strcmp(last2, "main")) { last_frame->ClearAll(); last_frame2->next = nullptr; // Strip our internal thread start routine. } else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) { last_frame->ClearAll(); last_frame2->next = nullptr; // Strip global ctors init. } else if (last && 0 == internal_strcmp(last, "__do_global_ctors_aux")) { last_frame->ClearAll(); last_frame2->next = nullptr; // If both are 0, then we probably just failed to symbolize. } else if (last || last2) { // Ensure that we recovered stack completely. Trimmed stack // can actually happen if we do not instrument some code, // so it's only a debug print. However we must try hard to not miss it // due to our fault. - DPrintf("Bottom stack frame of stack %zx is missed\n", stack->info.address); + DPrintf("Bottom stack frame is missed\n"); } #else // The last frame always point into runtime (gosched0, goexit0, runtime.main). last_frame->ClearAll(); last_frame2->next = nullptr; #endif } ReportStack *SymbolizeStackId(u32 stack_id) { if (stack_id == 0) return 0; StackTrace stack = StackDepotGet(stack_id); if (stack.trace == nullptr) return nullptr; return SymbolizeStack(stack); } static ReportStack *SymbolizeStack(StackTrace trace) { if (trace.size == 0) return 0; SymbolizedStack *top = nullptr; for (uptr si = 0; si < trace.size; si++) { const uptr pc = trace.trace[si]; uptr pc1 = pc; #ifndef SANITIZER_GO // We obtain the return address, but we're interested in the previous // instruction. if ((pc & kExternalPCBit) == 0) pc1 = StackTrace::GetPreviousInstructionPc(pc); #else // FIXME(dvyukov): Go sometimes uses address of a function as top pc. if (si != trace.size - 1) pc1 -= 1; #endif SymbolizedStack *ent = SymbolizeCode(pc1); CHECK_NE(ent, 0); SymbolizedStack *last = ent; while (last->next) { last->info.address = pc; // restore original pc for report last = last->next; } last->info.address = pc; // restore original pc for report last->next = top; top = ent; } StackStripMain(top); ReportStack *stack = ReportStack::New(); stack->frames = top; return stack; } ScopedReport::ScopedReport(ReportType typ) { ctx->thread_registry->CheckLocked(); void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc)); rep_ = new(mem) ReportDesc; rep_->typ = typ; ctx->report_mtx.Lock(); CommonSanitizerReportMutex.Lock(); } ScopedReport::~ScopedReport() { CommonSanitizerReportMutex.Unlock(); ctx->report_mtx.Unlock(); DestroyAndFree(rep_); } void ScopedReport::AddStack(StackTrace stack, bool suppressable) { ReportStack **rs = rep_->stacks.PushBack(); *rs = SymbolizeStack(stack); (*rs)->suppressable = suppressable; } void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, StackTrace stack, const MutexSet *mset) { void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop)); ReportMop *mop = new(mem) ReportMop; rep_->mops.PushBack(mop); mop->tid = s.tid(); mop->addr = addr + s.addr0(); mop->size = s.size(); mop->write = s.IsWrite(); mop->atomic = s.IsAtomic(); mop->stack = SymbolizeStack(stack); if (mop->stack) mop->stack->suppressable = true; for (uptr i = 0; i < mset->Size(); i++) { MutexSet::Desc d = mset->Get(i); u64 mid = this->AddMutex(d.id); ReportMopMutex mtx = {mid, d.write}; mop->mset.PushBack(mtx); } } void ScopedReport::AddUniqueTid(int unique_tid) { rep_->unique_tids.PushBack(unique_tid); } void ScopedReport::AddThread(const ThreadContext *tctx, bool suppressable) { for (uptr i = 0; i < rep_->threads.Size(); i++) { if ((u32)rep_->threads[i]->id == tctx->tid) return; } void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread)); ReportThread *rt = new(mem) ReportThread(); rep_->threads.PushBack(rt); rt->id = tctx->tid; rt->pid = tctx->os_id; rt->running = (tctx->status == ThreadStatusRunning); rt->name = internal_strdup(tctx->name); rt->parent_tid = tctx->parent_tid; rt->stack = 0; rt->stack = SymbolizeStackId(tctx->creation_stack_id); if (rt->stack) rt->stack->suppressable = suppressable; } #ifndef SANITIZER_GO static ThreadContext *FindThreadByUidLocked(int unique_id) { ctx->thread_registry->CheckLocked(); for (unsigned i = 0; i < kMaxTid; i++) { ThreadContext *tctx = static_cast( ctx->thread_registry->GetThreadLocked(i)); if (tctx && tctx->unique_id == (u32)unique_id) { return tctx; } } return 0; } static ThreadContext *FindThreadByTidLocked(int tid) { ctx->thread_registry->CheckLocked(); return static_cast( ctx->thread_registry->GetThreadLocked(tid)); } static bool IsInStackOrTls(ThreadContextBase *tctx_base, void *arg) { uptr addr = (uptr)arg; ThreadContext *tctx = static_cast(tctx_base); if (tctx->status != ThreadStatusRunning) return false; ThreadState *thr = tctx->thr; CHECK(thr); return ((addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) || (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size)); } ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) { ctx->thread_registry->CheckLocked(); ThreadContext *tctx = static_cast( ctx->thread_registry->FindThreadContextLocked(IsInStackOrTls, (void*)addr)); if (!tctx) return 0; ThreadState *thr = tctx->thr; CHECK(thr); *is_stack = (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size); return tctx; } #endif void ScopedReport::AddThread(int unique_tid, bool suppressable) { #ifndef SANITIZER_GO - AddThread(FindThreadByUidLocked(unique_tid), suppressable); + if (const ThreadContext *tctx = FindThreadByUidLocked(unique_tid)) + AddThread(tctx, suppressable); #endif } void ScopedReport::AddMutex(const SyncVar *s) { for (uptr i = 0; i < rep_->mutexes.Size(); i++) { if (rep_->mutexes[i]->id == s->uid) return; } void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex)); ReportMutex *rm = new(mem) ReportMutex(); rep_->mutexes.PushBack(rm); rm->id = s->uid; rm->addr = s->addr; rm->destroyed = false; rm->stack = SymbolizeStackId(s->creation_stack_id); } u64 ScopedReport::AddMutex(u64 id) { u64 uid = 0; u64 mid = id; uptr addr = SyncVar::SplitId(id, &uid); SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr); // Check that the mutex is still alive. // Another mutex can be created at the same address, // so check uid as well. if (s && s->CheckId(uid)) { mid = s->uid; AddMutex(s); } else { AddDeadMutex(id); } if (s) s->mtx.Unlock(); return mid; } void ScopedReport::AddDeadMutex(u64 id) { for (uptr i = 0; i < rep_->mutexes.Size(); i++) { if (rep_->mutexes[i]->id == id) return; } void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex)); ReportMutex *rm = new(mem) ReportMutex(); rep_->mutexes.PushBack(rm); rm->id = id; rm->addr = 0; rm->destroyed = true; rm->stack = 0; } void ScopedReport::AddLocation(uptr addr, uptr size) { if (addr == 0) return; #ifndef SANITIZER_GO int fd = -1; int creat_tid = -1; u32 creat_stack = 0; if (FdLocation(addr, &fd, &creat_tid, &creat_stack)) { ReportLocation *loc = ReportLocation::New(ReportLocationFD); loc->fd = fd; loc->tid = creat_tid; loc->stack = SymbolizeStackId(creat_stack); rep_->locs.PushBack(loc); ThreadContext *tctx = FindThreadByUidLocked(creat_tid); if (tctx) AddThread(tctx); return; } MBlock *b = 0; Allocator *a = allocator(); if (a->PointerIsMine((void*)addr)) { void *block_begin = a->GetBlockBegin((void*)addr); if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin); } if (b != 0) { ThreadContext *tctx = FindThreadByTidLocked(b->tid); ReportLocation *loc = ReportLocation::New(ReportLocationHeap); loc->heap_chunk_start = (uptr)allocator()->GetBlockBegin((void *)addr); loc->heap_chunk_size = b->siz; loc->tid = tctx ? tctx->tid : b->tid; loc->stack = SymbolizeStackId(b->stk); rep_->locs.PushBack(loc); if (tctx) AddThread(tctx); return; } bool is_stack = false; if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) { ReportLocation *loc = ReportLocation::New(is_stack ? ReportLocationStack : ReportLocationTLS); loc->tid = tctx->tid; rep_->locs.PushBack(loc); AddThread(tctx); } if (ReportLocation *loc = SymbolizeData(addr)) { loc->suppressable = true; rep_->locs.PushBack(loc); return; } #endif } #ifndef SANITIZER_GO void ScopedReport::AddSleep(u32 stack_id) { rep_->sleep = SymbolizeStackId(stack_id); } #endif void ScopedReport::SetCount(int count) { rep_->count = count; } const ReportDesc *ScopedReport::GetReport() const { return rep_; } void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, MutexSet *mset) { // This function restores stack trace and mutex set for the thread/epoch. // It does so by getting stack trace and mutex set at the beginning of // trace part, and then replaying the trace till the given epoch. ctx->thread_registry->CheckLocked(); ThreadContext *tctx = static_cast( ctx->thread_registry->GetThreadLocked(tid)); if (tctx == 0) return; if (tctx->status != ThreadStatusRunning && tctx->status != ThreadStatusFinished && tctx->status != ThreadStatusDead) return; Trace* trace = ThreadTrace(tctx->tid); Lock l(&trace->mtx); const int partidx = (epoch / kTracePartSize) % TraceParts(); TraceHeader* hdr = &trace->headers[partidx]; if (epoch < hdr->epoch0) return; const u64 epoch0 = RoundDown(epoch, TraceSize()); const u64 eend = epoch % TraceSize(); const u64 ebegin = RoundDown(eend, kTracePartSize); DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n", tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx); InternalScopedBuffer stack(kShadowStackSize); for (uptr i = 0; i < hdr->stack0.size; i++) { stack[i] = hdr->stack0.trace[i]; - DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]); + DPrintf2(" #%02zu: pc=%zx\n", i, stack[i]); } if (mset) *mset = hdr->mset0; uptr pos = hdr->stack0.size; Event *events = (Event*)GetThreadTrace(tid); for (uptr i = ebegin; i <= eend; i++) { Event ev = events[i]; EventType typ = (EventType)(ev >> 61); uptr pc = (uptr)(ev & ((1ull << 61) - 1)); DPrintf2(" %zu typ=%d pc=%zx\n", i, typ, pc); if (typ == EventTypeMop) { stack[pos] = pc; } else if (typ == EventTypeFuncEnter) { stack[pos++] = pc; } else if (typ == EventTypeFuncExit) { if (pos > 0) pos--; } if (mset) { if (typ == EventTypeLock) { mset->Add(pc, true, epoch0 + i); } else if (typ == EventTypeUnlock) { mset->Del(pc, true); } else if (typ == EventTypeRLock) { mset->Add(pc, false, epoch0 + i); } else if (typ == EventTypeRUnlock) { mset->Del(pc, false); } } for (uptr j = 0; j <= pos; j++) DPrintf2(" #%zu: %zx\n", j, stack[j]); } if (pos == 0 && stack[0] == 0) return; pos++; stk->Init(stack.data(), pos); } static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2], uptr addr_min, uptr addr_max) { bool equal_stack = false; RacyStacks hash; if (flags()->suppress_equal_stacks) { hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr)); hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr)); for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) { if (hash == ctx->racy_stacks[i]) { DPrintf("ThreadSanitizer: suppressing report as doubled (stack)\n"); equal_stack = true; break; } } } bool equal_address = false; RacyAddress ra0 = {addr_min, addr_max}; if (flags()->suppress_equal_addresses) { for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) { RacyAddress ra2 = ctx->racy_addresses[i]; uptr maxbeg = max(ra0.addr_min, ra2.addr_min); uptr minend = min(ra0.addr_max, ra2.addr_max); if (maxbeg < minend) { DPrintf("ThreadSanitizer: suppressing report as doubled (addr)\n"); equal_address = true; break; } } } if (equal_stack || equal_address) { if (!equal_stack) ctx->racy_stacks.PushBack(hash); if (!equal_address) ctx->racy_addresses.PushBack(ra0); return true; } return false; } static void AddRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2], uptr addr_min, uptr addr_max) { if (flags()->suppress_equal_stacks) { RacyStacks hash; hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr)); hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr)); ctx->racy_stacks.PushBack(hash); } if (flags()->suppress_equal_addresses) { RacyAddress ra0 = {addr_min, addr_max}; ctx->racy_addresses.PushBack(ra0); } } bool OutputReport(ThreadState *thr, const ScopedReport &srep) { atomic_store(&ctx->last_symbolize_time_ns, NanoTime(), memory_order_relaxed); const ReportDesc *rep = srep.GetReport(); Suppression *supp = 0; uptr suppress_pc = 0; for (uptr i = 0; suppress_pc == 0 && i < rep->mops.Size(); i++) suppress_pc = IsSuppressed(rep->typ, rep->mops[i]->stack, &supp); for (uptr i = 0; suppress_pc == 0 && i < rep->stacks.Size(); i++) suppress_pc = IsSuppressed(rep->typ, rep->stacks[i], &supp); for (uptr i = 0; suppress_pc == 0 && i < rep->threads.Size(); i++) suppress_pc = IsSuppressed(rep->typ, rep->threads[i]->stack, &supp); for (uptr i = 0; suppress_pc == 0 && i < rep->locs.Size(); i++) suppress_pc = IsSuppressed(rep->typ, rep->locs[i], &supp); if (suppress_pc != 0) { FiredSuppression s = {srep.GetReport()->typ, suppress_pc, supp}; ctx->fired_suppressions.push_back(s); } { bool old_is_freeing = thr->is_freeing; thr->is_freeing = false; bool suppressed = OnReport(rep, suppress_pc != 0); thr->is_freeing = old_is_freeing; if (suppressed) return false; } PrintReport(rep); ctx->nreported++; if (flags()->halt_on_error) internal__exit(flags()->exitcode); return true; } bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, StackTrace trace) { for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) continue; for (uptr j = 0; j < trace.size; j++) { FiredSuppression *s = &ctx->fired_suppressions[k]; if (trace.trace[j] == s->pc) { if (s->supp) s->supp->hit_count++; return true; } } } return false; } static bool IsFiredSuppression(Context *ctx, const ScopedReport &srep, uptr addr) { for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) { if (ctx->fired_suppressions[k].type != srep.GetReport()->typ) continue; FiredSuppression *s = &ctx->fired_suppressions[k]; if (addr == s->pc) { if (s->supp) s->supp->hit_count++; return true; } } return false; } static bool RaceBetweenAtomicAndFree(ThreadState *thr) { Shadow s0(thr->racy_state[0]); Shadow s1(thr->racy_state[1]); CHECK(!(s0.IsAtomic() && s1.IsAtomic())); if (!s0.IsAtomic() && !s1.IsAtomic()) return true; if (s0.IsAtomic() && s1.IsFreed()) return true; if (s1.IsAtomic() && thr->is_freeing) return true; return false; } void ReportRace(ThreadState *thr) { CheckNoLocks(thr); // Symbolizer makes lots of intercepted calls. If we try to process them, // at best it will cause deadlocks on internal mutexes. ScopedIgnoreInterceptors ignore; if (!flags()->report_bugs) return; if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr)) return; bool freed = false; { Shadow s(thr->racy_state[1]); freed = s.GetFreedAndReset(); thr->racy_state[1] = s.raw(); } uptr addr = ShadowToMem((uptr)thr->racy_shadow_addr); uptr addr_min = 0; uptr addr_max = 0; { uptr a0 = addr + Shadow(thr->racy_state[0]).addr0(); uptr a1 = addr + Shadow(thr->racy_state[1]).addr0(); uptr e0 = a0 + Shadow(thr->racy_state[0]).size(); uptr e1 = a1 + Shadow(thr->racy_state[1]).size(); addr_min = min(a0, a1); addr_max = max(e0, e1); if (IsExpectedReport(addr_min, addr_max - addr_min)) return; } ThreadRegistryLock l0(ctx->thread_registry); ReportType typ = ReportTypeRace; if (thr->is_vptr_access && freed) typ = ReportTypeVptrUseAfterFree; else if (thr->is_vptr_access) typ = ReportTypeVptrRace; else if (freed) typ = ReportTypeUseAfterFree; ScopedReport rep(typ); if (IsFiredSuppression(ctx, rep, addr)) return; const uptr kMop = 2; VarSizeStackTrace traces[kMop]; const uptr toppc = TraceTopPC(thr); ObtainCurrentStack(thr, toppc, &traces[0]); if (IsFiredSuppression(ctx, rep, traces[0])) return; InternalScopedBuffer mset2(1); new(mset2.data()) MutexSet(); Shadow s2(thr->racy_state[1]); RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data()); if (IsFiredSuppression(ctx, rep, traces[1])) return; if (HandleRacyStacks(thr, traces, addr_min, addr_max)) return; for (uptr i = 0; i < kMop; i++) { Shadow s(thr->racy_state[i]); rep.AddMemoryAccess(addr, s, traces[i], i == 0 ? &thr->mset : mset2.data()); } for (uptr i = 0; i < kMop; i++) { FastState s(thr->racy_state[i]); ThreadContext *tctx = static_cast( ctx->thread_registry->GetThreadLocked(s.tid())); if (s.epoch() < tctx->epoch0 || s.epoch() > tctx->epoch1) continue; rep.AddThread(tctx); } rep.AddLocation(addr_min, addr_max - addr_min); #ifndef SANITIZER_GO { // NOLINT Shadow s(thr->racy_state[1]); if (s.epoch() <= thr->last_sleep_clock.get(s.tid())) rep.AddSleep(thr->last_sleep_stack_id); } #endif if (!OutputReport(thr, rep)) return; AddRacyStacks(thr, traces, addr_min, addr_max); } void PrintCurrentStack(ThreadState *thr, uptr pc) { VarSizeStackTrace trace; ObtainCurrentStack(thr, pc, &trace); PrintStack(SymbolizeStack(trace)); } void PrintCurrentStackSlow(uptr pc) { #ifndef SANITIZER_GO BufferedStackTrace *ptrace = new(internal_alloc(MBlockStackTrace, sizeof(BufferedStackTrace))) BufferedStackTrace(); ptrace->Unwind(kStackTraceMax, pc, 0, 0, 0, 0, false); for (uptr i = 0; i < ptrace->size / 2; i++) { uptr tmp = ptrace->trace_buffer[i]; ptrace->trace_buffer[i] = ptrace->trace_buffer[ptrace->size - i - 1]; ptrace->trace_buffer[ptrace->size - i - 1] = tmp; } PrintStack(SymbolizeStack(*ptrace)); #endif } } // namespace __tsan using namespace __tsan; extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_print_stack_trace() { PrintCurrentStackSlow(StackTrace::GetCurrentPc()); } } // extern "C" Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cc (revision 279191) @@ -1,403 +1,404 @@ //===-- tsan_rtl_thread.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_placement_new.h" #include "tsan_rtl.h" #include "tsan_mman.h" #include "tsan_platform.h" #include "tsan_report.h" #include "tsan_sync.h" namespace __tsan { // ThreadContext implementation. ThreadContext::ThreadContext(int tid) : ThreadContextBase(tid) , thr() , sync() , epoch0() , epoch1() { } #ifndef SANITIZER_GO ThreadContext::~ThreadContext() { } #endif void ThreadContext::OnDead() { CHECK_EQ(sync.size(), 0); } void ThreadContext::OnJoined(void *arg) { ThreadState *caller_thr = static_cast(arg); AcquireImpl(caller_thr, 0, &sync); sync.Reset(&caller_thr->clock_cache); } struct OnCreatedArgs { ThreadState *thr; uptr pc; }; void ThreadContext::OnCreated(void *arg) { thr = 0; if (tid == 0) return; OnCreatedArgs *args = static_cast(arg); args->thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0); ReleaseImpl(args->thr, 0, &sync); creation_stack_id = CurrentStackId(args->thr, args->pc); if (reuse_count == 0) StatInc(args->thr, StatThreadMaxTid); } void ThreadContext::OnReset() { CHECK_EQ(sync.size(), 0); FlushUnneededShadowMemory(GetThreadTrace(tid), TraceSize() * sizeof(Event)); //!!! FlushUnneededShadowMemory(GetThreadTraceHeader(tid), sizeof(Trace)); } void ThreadContext::OnDetached(void *arg) { ThreadState *thr1 = static_cast(arg); sync.Reset(&thr1->clock_cache); } struct OnStartedArgs { ThreadState *thr; uptr stk_addr; uptr stk_size; uptr tls_addr; uptr tls_size; }; void ThreadContext::OnStarted(void *arg) { OnStartedArgs *args = static_cast(arg); thr = args->thr; // RoundUp so that one trace part does not contain events // from different threads. epoch0 = RoundUp(epoch1 + 1, kTracePartSize); epoch1 = (u64)-1; new(thr) ThreadState(ctx, tid, unique_id, epoch0, reuse_count, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size); #ifndef SANITIZER_GO thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0]; thr->shadow_stack_pos = thr->shadow_stack; thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize; #else // Setup dynamic shadow stack. const int kInitStackSize = 8; thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, kInitStackSize * sizeof(uptr)); thr->shadow_stack_pos = thr->shadow_stack; thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; #endif #ifndef SANITIZER_GO AllocatorThreadStart(thr); #endif if (common_flags()->detect_deadlocks) { thr->dd_pt = ctx->dd->CreatePhysicalThread(); thr->dd_lt = ctx->dd->CreateLogicalThread(unique_id); } + thr->fast_state.SetHistorySize(flags()->history_size); + // Commit switch to the new part of the trace. + // TraceAddEvent will reset stack0/mset0 in the new part for us. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + thr->fast_synch_epoch = epoch0; AcquireImpl(thr, 0, &sync); - thr->fast_state.SetHistorySize(flags()->history_size); - const uptr trace = (epoch0 / kTracePartSize) % TraceParts(); - Trace *thr_trace = ThreadTrace(thr->tid); - thr_trace->headers[trace].epoch0 = epoch0; StatInc(thr, StatSyncAcquire); sync.Reset(&thr->clock_cache); DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx " "tls_addr=%zx tls_size=%zx\n", tid, (uptr)epoch0, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size); } void ThreadContext::OnFinished() { if (!detached) { thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); ReleaseImpl(thr, 0, &sync); } epoch1 = thr->fast_state.epoch(); if (common_flags()->detect_deadlocks) { ctx->dd->DestroyPhysicalThread(thr->dd_pt); ctx->dd->DestroyLogicalThread(thr->dd_lt); } ctx->clock_alloc.FlushCache(&thr->clock_cache); ctx->metamap.OnThreadIdle(thr); #ifndef SANITIZER_GO AllocatorThreadFinish(thr); #endif thr->~ThreadState(); StatAggregate(ctx->stat, thr->stat); thr = 0; } #ifndef SANITIZER_GO struct ThreadLeak { ThreadContext *tctx; int count; }; static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) { Vector &leaks = *(Vector*)arg; ThreadContext *tctx = static_cast(tctx_base); if (tctx->detached || tctx->status != ThreadStatusFinished) return; for (uptr i = 0; i < leaks.Size(); i++) { if (leaks[i].tctx->creation_stack_id == tctx->creation_stack_id) { leaks[i].count++; return; } } ThreadLeak leak = {tctx, 1}; leaks.PushBack(leak); } #endif #ifndef SANITIZER_GO static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) { if (tctx->tid == 0) { Printf("ThreadSanitizer: main thread finished with ignores enabled\n"); } else { Printf("ThreadSanitizer: thread T%d %s finished with ignores enabled," " created at:\n", tctx->tid, tctx->name); PrintStack(SymbolizeStackId(tctx->creation_stack_id)); } Printf(" One of the following ignores was not ended" " (in order of probability)\n"); for (uptr i = 0; i < set->Size(); i++) { Printf(" Ignore was enabled at:\n"); PrintStack(SymbolizeStackId(set->At(i))); } Die(); } static void ThreadCheckIgnore(ThreadState *thr) { if (ctx->after_multithreaded_fork) return; if (thr->ignore_reads_and_writes) ReportIgnoresEnabled(thr->tctx, &thr->mop_ignore_set); if (thr->ignore_sync) ReportIgnoresEnabled(thr->tctx, &thr->sync_ignore_set); } #else static void ThreadCheckIgnore(ThreadState *thr) {} #endif void ThreadFinalize(ThreadState *thr) { ThreadCheckIgnore(thr); #ifndef SANITIZER_GO if (!flags()->report_thread_leaks) return; ThreadRegistryLock l(ctx->thread_registry); Vector leaks(MBlockScopedBuf); ctx->thread_registry->RunCallbackForEachThreadLocked( MaybeReportThreadLeak, &leaks); for (uptr i = 0; i < leaks.Size(); i++) { ScopedReport rep(ReportTypeThreadLeak); rep.AddThread(leaks[i].tctx, true); rep.SetCount(leaks[i].count); OutputReport(thr, rep); } #endif } int ThreadCount(ThreadState *thr) { uptr result; ctx->thread_registry->GetNumberOfThreads(0, 0, &result); return (int)result; } int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) { StatInc(thr, StatThreadCreate); OnCreatedArgs args = { thr, pc }; int tid = ctx->thread_registry->CreateThread(uid, detached, thr->tid, &args); DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", thr->tid, tid, uid); StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads()); return tid; } void ThreadStart(ThreadState *thr, int tid, uptr os_id) { uptr stk_addr = 0; uptr stk_size = 0; uptr tls_addr = 0; uptr tls_size = 0; GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size); if (tid) { if (stk_addr && stk_size) MemoryRangeImitateWrite(thr, /*pc=*/ 1, stk_addr, stk_size); if (tls_addr && tls_size) { // Check that the thr object is in tls; const uptr thr_beg = (uptr)thr; const uptr thr_end = (uptr)thr + sizeof(*thr); CHECK_GE(thr_beg, tls_addr); CHECK_LE(thr_beg, tls_addr + tls_size); CHECK_GE(thr_end, tls_addr); CHECK_LE(thr_end, tls_addr + tls_size); // Since the thr object is huge, skip it. MemoryRangeImitateWrite(thr, /*pc=*/ 2, tls_addr, thr_beg - tls_addr); MemoryRangeImitateWrite(thr, /*pc=*/ 2, thr_end, tls_addr + tls_size - thr_end); } } ThreadRegistry *tr = ctx->thread_registry; OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size }; tr->StartThread(tid, os_id, &args); tr->Lock(); thr->tctx = (ThreadContext*)tr->GetThreadLocked(tid); tr->Unlock(); #ifndef SANITIZER_GO if (ctx->after_multithreaded_fork) { thr->ignore_interceptors++; ThreadIgnoreBegin(thr, 0); ThreadIgnoreSyncBegin(thr, 0); } #endif } void ThreadFinish(ThreadState *thr) { ThreadCheckIgnore(thr); StatInc(thr, StatThreadFinish); if (thr->stk_addr && thr->stk_size) DontNeedShadowFor(thr->stk_addr, thr->stk_size); if (thr->tls_addr && thr->tls_size) DontNeedShadowFor(thr->tls_addr, thr->tls_size); thr->is_dead = true; ctx->thread_registry->FinishThread(thr->tid); } static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) { uptr uid = (uptr)arg; if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) { tctx->user_id = 0; return true; } return false; } int ThreadTid(ThreadState *thr, uptr pc, uptr uid) { int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid); DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res); return res; } void ThreadJoin(ThreadState *thr, uptr pc, int tid) { CHECK_GT(tid, 0); CHECK_LT(tid, kMaxTid); DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid); ctx->thread_registry->JoinThread(tid, thr); } void ThreadDetach(ThreadState *thr, uptr pc, int tid) { CHECK_GT(tid, 0); CHECK_LT(tid, kMaxTid); ctx->thread_registry->DetachThread(tid, thr); } void ThreadSetName(ThreadState *thr, const char *name) { ctx->thread_registry->SetThreadName(thr->tid, name); } void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, uptr size, bool is_write) { if (size == 0) return; u64 *shadow_mem = (u64*)MemToShadow(addr); DPrintf2("#%d: MemoryAccessRange: @%p %p size=%d is_write=%d\n", thr->tid, (void*)pc, (void*)addr, (int)size, is_write); -#if TSAN_DEBUG +#if SANITIZER_DEBUG if (!IsAppMem(addr)) { Printf("Access to non app mem %zx\n", addr); DCHECK(IsAppMem(addr)); } if (!IsAppMem(addr + size - 1)) { Printf("Access to non app mem %zx\n", addr + size - 1); DCHECK(IsAppMem(addr + size - 1)); } if (!IsShadowMem((uptr)shadow_mem)) { Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr); DCHECK(IsShadowMem((uptr)shadow_mem)); } if (!IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))) { Printf("Bad shadow addr %p (%zx)\n", shadow_mem + size * kShadowCnt / 8 - 1, addr + size - 1); DCHECK(IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))); } #endif StatInc(thr, StatMopRange); if (*shadow_mem == kShadowRodata) { // Access to .rodata section, no races here. // Measurements show that it can be 10-20% of all memory accesses. StatInc(thr, StatMopRangeRodata); return; } FastState fast_state = thr->fast_state; if (fast_state.GetIgnoreBit()) return; fast_state.IncrementEpoch(); thr->fast_state = fast_state; TraceAddEvent(thr, fast_state, EventTypeMop, pc); bool unaligned = (addr % kShadowCell) != 0; // Handle unaligned beginning, if any. for (; addr % kShadowCell && size; addr++, size--) { int const kAccessSizeLog = 0; Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog); MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); } if (unaligned) shadow_mem += kShadowCnt; // Handle middle part, if any. for (; size >= kShadowCell; addr += kShadowCell, size -= kShadowCell) { int const kAccessSizeLog = 3; Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(0, kAccessSizeLog); MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); shadow_mem += kShadowCnt; } // Handle ending, if any. for (; size; addr++, size--) { int const kAccessSizeLog = 0; Shadow cur(fast_state); cur.SetWrite(is_write); cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog); MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false, shadow_mem, cur); } } } // namespace __tsan Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/rtl/tsan_string.cc (revision 279191) @@ -1,86 +1,82 @@ //===-- tsan_string.cc ----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "tsan_test_util.h" #include "gtest/gtest.h" #include namespace __tsan { TEST(ThreadSanitizer, Memcpy) { char data0[7] = {1, 2, 3, 4, 5, 6, 7}; char data[7] = {42, 42, 42, 42, 42, 42, 42}; MainThread().Memcpy(data+1, data0+1, 5); EXPECT_EQ(data[0], 42); EXPECT_EQ(data[1], 2); EXPECT_EQ(data[2], 3); EXPECT_EQ(data[3], 4); EXPECT_EQ(data[4], 5); EXPECT_EQ(data[5], 6); EXPECT_EQ(data[6], 42); MainThread().Memset(data+1, 13, 5); EXPECT_EQ(data[0], 42); EXPECT_EQ(data[1], 13); EXPECT_EQ(data[2], 13); EXPECT_EQ(data[3], 13); EXPECT_EQ(data[4], 13); EXPECT_EQ(data[5], 13); EXPECT_EQ(data[6], 42); } TEST(ThreadSanitizer, MemcpyRace1) { char *data = new char[10]; char *data1 = new char[10]; char *data2 = new char[10]; ScopedThread t1, t2; t1.Memcpy(data, data1, 10); t2.Memcpy(data, data2, 10, true); } -// The test fails with TSAN_SHADOW_COUNT=2, -// because the old racy access is evicted. -#if defined(TSAN_SHADOW_COUNT) && TSAN_SHADOW_COUNT >= 4 TEST(ThreadSanitizer, MemcpyRace2) { char *data = new char[10]; char *data1 = new char[10]; char *data2 = new char[10]; ScopedThread t1, t2; t1.Memcpy(data+5, data1, 1); t2.Memcpy(data+3, data2, 4, true); } -#endif TEST(ThreadSanitizer, MemcpyRace3) { char *data = new char[10]; char *data1 = new char[10]; char *data2 = new char[10]; ScopedThread t1, t2; t1.Memcpy(data, data1, 10); t2.Memcpy(data1, data2, 10, true); } TEST(ThreadSanitizer, MemcpyStack) { char *data = new char[10]; char *data1 = new char[10]; ScopedThread t1, t2; t1.Memcpy(data, data1, 10); t2.Memcpy(data, data1, 10, true); } TEST(ThreadSanitizer, MemsetRace1) { char *data = new char[10]; ScopedThread t1, t2; t1.Memset(data, 1, 10); t2.Memset(data, 2, 10, true); } } // namespace __tsan Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/unit/tsan_clock_test.cc (revision 279191) @@ -1,432 +1,432 @@ //===-- tsan_clock_test.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "tsan_clock.h" #include "tsan_rtl.h" #include "gtest/gtest.h" #include namespace __tsan { ClockCache cache; TEST(Clock, VectorBasic) { ThreadClock clk(0); ASSERT_EQ(clk.size(), 1U); clk.tick(); ASSERT_EQ(clk.size(), 1U); ASSERT_EQ(clk.get(0), 1U); clk.set(3, clk.get(3) + 1); ASSERT_EQ(clk.size(), 4U); ASSERT_EQ(clk.get(0), 1U); ASSERT_EQ(clk.get(1), 0U); ASSERT_EQ(clk.get(2), 0U); ASSERT_EQ(clk.get(3), 1U); clk.set(3, clk.get(3) + 1); ASSERT_EQ(clk.get(3), 2U); } TEST(Clock, ChunkedBasic) { ThreadClock vector(0); SyncClock chunked; ASSERT_EQ(vector.size(), 1U); ASSERT_EQ(chunked.size(), 0U); vector.acquire(&cache, &chunked); ASSERT_EQ(vector.size(), 1U); ASSERT_EQ(chunked.size(), 0U); vector.release(&cache, &chunked); ASSERT_EQ(vector.size(), 1U); ASSERT_EQ(chunked.size(), 1U); vector.acq_rel(&cache, &chunked); ASSERT_EQ(vector.size(), 1U); ASSERT_EQ(chunked.size(), 1U); chunked.Reset(&cache); } TEST(Clock, AcquireRelease) { ThreadClock vector1(100); vector1.tick(); SyncClock chunked; vector1.release(&cache, &chunked); ASSERT_EQ(chunked.size(), 101U); ThreadClock vector2(0); vector2.acquire(&cache, &chunked); ASSERT_EQ(vector2.size(), 101U); ASSERT_EQ(vector2.get(0), 0U); ASSERT_EQ(vector2.get(1), 0U); ASSERT_EQ(vector2.get(99), 0U); ASSERT_EQ(vector2.get(100), 1U); chunked.Reset(&cache); } TEST(Clock, RepeatedAcquire) { ThreadClock thr1(1); thr1.tick(); ThreadClock thr2(2); thr2.tick(); SyncClock sync; thr1.ReleaseStore(&cache, &sync); thr2.acquire(&cache, &sync); thr2.acquire(&cache, &sync); sync.Reset(&cache); } TEST(Clock, ManyThreads) { SyncClock chunked; for (unsigned i = 0; i < 100; i++) { ThreadClock vector(0); vector.tick(); vector.set(i, 1); vector.release(&cache, &chunked); ASSERT_EQ(i + 1, chunked.size()); vector.acquire(&cache, &chunked); ASSERT_EQ(i + 1, vector.size()); } for (unsigned i = 0; i < 100; i++) ASSERT_EQ(1U, chunked.get(i)); ThreadClock vector(1); vector.acquire(&cache, &chunked); ASSERT_EQ(100U, vector.size()); for (unsigned i = 0; i < 100; i++) ASSERT_EQ(1U, vector.get(i)); chunked.Reset(&cache); } TEST(Clock, DifferentSizes) { { ThreadClock vector1(10); vector1.tick(); ThreadClock vector2(20); vector2.tick(); { SyncClock chunked; vector1.release(&cache, &chunked); ASSERT_EQ(chunked.size(), 11U); vector2.release(&cache, &chunked); ASSERT_EQ(chunked.size(), 21U); chunked.Reset(&cache); } { SyncClock chunked; vector2.release(&cache, &chunked); ASSERT_EQ(chunked.size(), 21U); vector1.release(&cache, &chunked); ASSERT_EQ(chunked.size(), 21U); chunked.Reset(&cache); } { SyncClock chunked; vector1.release(&cache, &chunked); vector2.acquire(&cache, &chunked); ASSERT_EQ(vector2.size(), 21U); chunked.Reset(&cache); } { SyncClock chunked; vector2.release(&cache, &chunked); vector1.acquire(&cache, &chunked); ASSERT_EQ(vector1.size(), 21U); chunked.Reset(&cache); } } } TEST(Clock, Growth) { { ThreadClock vector(10); vector.tick(); vector.set(5, 42); SyncClock sync; vector.release(&cache, &sync); ASSERT_EQ(sync.size(), 11U); ASSERT_EQ(sync.get(0), 0ULL); ASSERT_EQ(sync.get(1), 0ULL); ASSERT_EQ(sync.get(5), 42ULL); ASSERT_EQ(sync.get(9), 0ULL); ASSERT_EQ(sync.get(10), 1ULL); sync.Reset(&cache); } { ThreadClock vector1(10); vector1.tick(); ThreadClock vector2(20); vector2.tick(); SyncClock sync; vector1.release(&cache, &sync); vector2.release(&cache, &sync); ASSERT_EQ(sync.size(), 21U); ASSERT_EQ(sync.get(0), 0ULL); ASSERT_EQ(sync.get(10), 1ULL); ASSERT_EQ(sync.get(19), 0ULL); ASSERT_EQ(sync.get(20), 1ULL); sync.Reset(&cache); } { ThreadClock vector(100); vector.tick(); vector.set(5, 42); vector.set(90, 84); SyncClock sync; vector.release(&cache, &sync); ASSERT_EQ(sync.size(), 101U); ASSERT_EQ(sync.get(0), 0ULL); ASSERT_EQ(sync.get(1), 0ULL); ASSERT_EQ(sync.get(5), 42ULL); ASSERT_EQ(sync.get(60), 0ULL); ASSERT_EQ(sync.get(70), 0ULL); ASSERT_EQ(sync.get(90), 84ULL); ASSERT_EQ(sync.get(99), 0ULL); ASSERT_EQ(sync.get(100), 1ULL); sync.Reset(&cache); } { ThreadClock vector1(10); vector1.tick(); ThreadClock vector2(100); vector2.tick(); SyncClock sync; vector1.release(&cache, &sync); vector2.release(&cache, &sync); ASSERT_EQ(sync.size(), 101U); ASSERT_EQ(sync.get(0), 0ULL); ASSERT_EQ(sync.get(10), 1ULL); ASSERT_EQ(sync.get(99), 0ULL); ASSERT_EQ(sync.get(100), 1ULL); sync.Reset(&cache); } } -const int kThreads = 4; -const int kClocks = 4; +const uptr kThreads = 4; +const uptr kClocks = 4; // SimpleSyncClock and SimpleThreadClock implement the same thing as // SyncClock and ThreadClock, but in a very simple way. struct SimpleSyncClock { u64 clock[kThreads]; uptr size; SimpleSyncClock() { Reset(); } void Reset() { size = 0; for (uptr i = 0; i < kThreads; i++) clock[i] = 0; } bool verify(const SyncClock *other) const { for (uptr i = 0; i < min(size, other->size()); i++) { if (clock[i] != other->get(i)) return false; } for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { if (i < size && clock[i] != 0) return false; if (i < other->size() && other->get(i) != 0) return false; } return true; } }; struct SimpleThreadClock { u64 clock[kThreads]; uptr size; unsigned tid; explicit SimpleThreadClock(unsigned tid) { this->tid = tid; size = tid + 1; for (uptr i = 0; i < kThreads; i++) clock[i] = 0; } void tick() { clock[tid]++; } void acquire(const SimpleSyncClock *src) { if (size < src->size) size = src->size; for (uptr i = 0; i < kThreads; i++) clock[i] = max(clock[i], src->clock[i]); } void release(SimpleSyncClock *dst) const { if (dst->size < size) dst->size = size; for (uptr i = 0; i < kThreads; i++) dst->clock[i] = max(dst->clock[i], clock[i]); } void acq_rel(SimpleSyncClock *dst) { acquire(dst); release(dst); } void ReleaseStore(SimpleSyncClock *dst) const { if (dst->size < size) dst->size = size; for (uptr i = 0; i < kThreads; i++) dst->clock[i] = clock[i]; } bool verify(const ThreadClock *other) const { for (uptr i = 0; i < min(size, other->size()); i++) { if (clock[i] != other->get(i)) return false; } for (uptr i = min(size, other->size()); i < max(size, other->size()); i++) { if (i < size && clock[i] != 0) return false; if (i < other->size() && other->get(i) != 0) return false; } return true; } }; static bool ClockFuzzer(bool printing) { // Create kThreads thread clocks. SimpleThreadClock *thr0[kThreads]; ThreadClock *thr1[kThreads]; unsigned reused[kThreads]; for (unsigned i = 0; i < kThreads; i++) { reused[i] = 0; thr0[i] = new SimpleThreadClock(i); thr1[i] = new ThreadClock(i, reused[i]); } // Create kClocks sync clocks. SimpleSyncClock *sync0[kClocks]; SyncClock *sync1[kClocks]; for (unsigned i = 0; i < kClocks; i++) { sync0[i] = new SimpleSyncClock(); sync1[i] = new SyncClock(); } // Do N random operations (acquire, release, etc) and compare results // for SimpleThread/SyncClock and real Thread/SyncClock. for (int i = 0; i < 10000; i++) { unsigned tid = rand() % kThreads; unsigned cid = rand() % kClocks; thr0[tid]->tick(); thr1[tid]->tick(); switch (rand() % 6) { case 0: if (printing) printf("acquire thr%d <- clk%d\n", tid, cid); thr0[tid]->acquire(sync0[cid]); thr1[tid]->acquire(&cache, sync1[cid]); break; case 1: if (printing) printf("release thr%d -> clk%d\n", tid, cid); thr0[tid]->release(sync0[cid]); thr1[tid]->release(&cache, sync1[cid]); break; case 2: if (printing) printf("acq_rel thr%d <> clk%d\n", tid, cid); thr0[tid]->acq_rel(sync0[cid]); thr1[tid]->acq_rel(&cache, sync1[cid]); break; case 3: if (printing) printf("rel_str thr%d >> clk%d\n", tid, cid); thr0[tid]->ReleaseStore(sync0[cid]); thr1[tid]->ReleaseStore(&cache, sync1[cid]); break; case 4: if (printing) printf("reset clk%d\n", cid); sync0[cid]->Reset(); sync1[cid]->Reset(&cache); break; case 5: if (printing) printf("reset thr%d\n", tid); u64 epoch = thr0[tid]->clock[tid] + 1; reused[tid]++; delete thr0[tid]; thr0[tid] = new SimpleThreadClock(tid); thr0[tid]->clock[tid] = epoch; delete thr1[tid]; thr1[tid] = new ThreadClock(tid, reused[tid]); thr1[tid]->set(epoch); break; } if (printing) { for (unsigned i = 0; i < kThreads; i++) { printf("thr%d: ", i); thr1[i]->DebugDump(printf); printf("\n"); } for (unsigned i = 0; i < kClocks; i++) { printf("clk%d: ", i); sync1[i]->DebugDump(printf); printf("\n"); } printf("\n"); } if (!thr0[tid]->verify(thr1[tid]) || !sync0[cid]->verify(sync1[cid])) { if (!printing) return false; printf("differs with model:\n"); for (unsigned i = 0; i < kThreads; i++) { printf("thr%d: clock=[", i); for (uptr j = 0; j < thr0[i]->size; j++) printf("%s%llu", j == 0 ? "" : ",", thr0[i]->clock[j]); printf("]\n"); } for (unsigned i = 0; i < kClocks; i++) { printf("clk%d: clock=[", i); for (uptr j = 0; j < sync0[i]->size; j++) printf("%s%llu", j == 0 ? "" : ",", sync0[i]->clock[j]); printf("]\n"); } return false; } } for (unsigned i = 0; i < kClocks; i++) { sync1[i]->Reset(&cache); } return true; } TEST(Clock, Fuzzer) { timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); int seed = ts.tv_sec + ts.tv_nsec; printf("seed=%d\n", seed); srand(seed); if (!ClockFuzzer(false)) { // Redo the test with the same seed, but logging operations. srand(seed); ClockFuzzer(true); ASSERT_TRUE(false); } } } // namespace __tsan Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mman_test.cc (revision 279191) @@ -1,153 +1,153 @@ //===-- tsan_mman_test.cc -------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include #include #include "tsan_mman.h" #include "tsan_rtl.h" #include "gtest/gtest.h" namespace __tsan { TEST(Mman, Internal) { char *p = (char*)internal_alloc(MBlockScopedBuf, 10); EXPECT_NE(p, (char*)0); char *p2 = (char*)internal_alloc(MBlockScopedBuf, 20); EXPECT_NE(p2, (char*)0); EXPECT_NE(p2, p); for (int i = 0; i < 10; i++) { p[i] = 42; } for (int i = 0; i < 20; i++) { ((char*)p2)[i] = 42; } internal_free(p); internal_free(p2); } TEST(Mman, User) { ThreadState *thr = cur_thread(); uptr pc = 0; char *p = (char*)user_alloc(thr, pc, 10); EXPECT_NE(p, (char*)0); char *p2 = (char*)user_alloc(thr, pc, 20); EXPECT_NE(p2, (char*)0); EXPECT_NE(p2, p); EXPECT_EQ(10U, user_alloc_usable_size(p)); EXPECT_EQ(20U, user_alloc_usable_size(p2)); user_free(thr, pc, p); user_free(thr, pc, p2); } TEST(Mman, UserRealloc) { ThreadState *thr = cur_thread(); uptr pc = 0; { void *p = user_realloc(thr, pc, 0, 0); // Strictly saying this is incorrect, realloc(NULL, N) is equivalent to // malloc(N), thus must return non-NULL pointer. EXPECT_EQ(p, (void*)0); } { void *p = user_realloc(thr, pc, 0, 100); EXPECT_NE(p, (void*)0); memset(p, 0xde, 100); user_free(thr, pc, p); } { void *p = user_alloc(thr, pc, 100); EXPECT_NE(p, (void*)0); memset(p, 0xde, 100); void *p2 = user_realloc(thr, pc, p, 0); EXPECT_EQ(p2, (void*)0); } { void *p = user_realloc(thr, pc, 0, 100); EXPECT_NE(p, (void*)0); memset(p, 0xde, 100); void *p2 = user_realloc(thr, pc, p, 10000); EXPECT_NE(p2, (void*)0); for (int i = 0; i < 100; i++) EXPECT_EQ(((char*)p2)[i], (char)0xde); memset(p2, 0xde, 10000); user_free(thr, pc, p2); } { void *p = user_realloc(thr, pc, 0, 10000); EXPECT_NE(p, (void*)0); memset(p, 0xde, 10000); void *p2 = user_realloc(thr, pc, p, 10); EXPECT_NE(p2, (void*)0); for (int i = 0; i < 10; i++) EXPECT_EQ(((char*)p2)[i], (char)0xde); user_free(thr, pc, p2); } } TEST(Mman, UsableSize) { ThreadState *thr = cur_thread(); uptr pc = 0; char *p = (char*)user_alloc(thr, pc, 10); char *p2 = (char*)user_alloc(thr, pc, 20); EXPECT_EQ(0U, user_alloc_usable_size(NULL)); EXPECT_EQ(10U, user_alloc_usable_size(p)); EXPECT_EQ(20U, user_alloc_usable_size(p2)); user_free(thr, pc, p); user_free(thr, pc, p2); EXPECT_EQ(0U, user_alloc_usable_size((void*)0x4123)); } TEST(Mman, Stats) { ThreadState *thr = cur_thread(); uptr alloc0 = __sanitizer_get_current_allocated_bytes(); uptr heap0 = __sanitizer_get_heap_size(); uptr free0 = __sanitizer_get_free_bytes(); uptr unmapped0 = __sanitizer_get_unmapped_bytes(); EXPECT_EQ(10U, __sanitizer_get_estimated_allocated_size(10)); EXPECT_EQ(20U, __sanitizer_get_estimated_allocated_size(20)); EXPECT_EQ(100U, __sanitizer_get_estimated_allocated_size(100)); char *p = (char*)user_alloc(thr, 0, 10); EXPECT_TRUE(__sanitizer_get_ownership(p)); EXPECT_EQ(10U, __sanitizer_get_allocated_size(p)); EXPECT_EQ(alloc0 + 16, __sanitizer_get_current_allocated_bytes()); EXPECT_GE(__sanitizer_get_heap_size(), heap0); EXPECT_EQ(free0, __sanitizer_get_free_bytes()); EXPECT_EQ(unmapped0, __sanitizer_get_unmapped_bytes()); user_free(thr, 0, p); EXPECT_EQ(alloc0, __sanitizer_get_current_allocated_bytes()); EXPECT_GE(__sanitizer_get_heap_size(), heap0); EXPECT_EQ(free0, __sanitizer_get_free_bytes()); EXPECT_EQ(unmapped0, __sanitizer_get_unmapped_bytes()); } TEST(Mman, CallocOverflow) { -#if TSAN_DEBUG +#if SANITIZER_DEBUG // EXPECT_DEATH clones a thread with 4K stack, // which is overflown by tsan memory accesses functions in debug mode. return; #endif size_t kArraySize = 4096; volatile size_t kMaxSizeT = std::numeric_limits::max(); volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; volatile void *p = NULL; EXPECT_DEATH(p = calloc(kArraySize, kArraySize2), "allocator is terminating the process instead of returning 0"); EXPECT_EQ(0L, p); } } // namespace __tsan Index: projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/tsan/tests/unit/tsan_mutex_test.cc (revision 279191) @@ -1,126 +1,126 @@ //===-- tsan_mutex_test.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_mutex.h" #include "tsan_mutex.h" #include "gtest/gtest.h" namespace __tsan { template class TestData { public: explicit TestData(MutexType *mtx) : mtx_(mtx) { for (int i = 0; i < kSize; i++) data_[i] = 0; } void Write() { Lock l(mtx_); T v0 = data_[0]; for (int i = 0; i < kSize; i++) { CHECK_EQ(data_[i], v0); data_[i]++; } } void Read() { ReadLock l(mtx_); T v0 = data_[0]; for (int i = 0; i < kSize; i++) { CHECK_EQ(data_[i], v0); } } void Backoff() { volatile T data[kSize] = {}; for (int i = 0; i < kSize; i++) { data[i]++; CHECK_EQ(data[i], 1); } } private: typedef GenericScopedLock Lock; static const int kSize = 64; typedef u64 T; MutexType *mtx_; char pad_[kCacheLineSize]; T data_[kSize]; }; const int kThreads = 8; const int kWriteRate = 1024; -#if TSAN_DEBUG +#if SANITIZER_DEBUG const int kIters = 16*1024; #else const int kIters = 64*1024; #endif template static void *write_mutex_thread(void *param) { TestData *data = (TestData*)param; for (int i = 0; i < kIters; i++) { data->Write(); data->Backoff(); } return 0; } template static void *read_mutex_thread(void *param) { TestData *data = (TestData*)param; for (int i = 0; i < kIters; i++) { if ((i % kWriteRate) == 0) data->Write(); else data->Read(); data->Backoff(); } return 0; } TEST(Mutex, Write) { Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations); TestData data(&mtx); pthread_t threads[kThreads]; for (int i = 0; i < kThreads; i++) pthread_create(&threads[i], 0, write_mutex_thread, &data); for (int i = 0; i < kThreads; i++) pthread_join(threads[i], 0); } TEST(Mutex, ReadWrite) { Mutex mtx(MutexTypeAnnotations, StatMtxAnnotations); TestData data(&mtx); pthread_t threads[kThreads]; for (int i = 0; i < kThreads; i++) pthread_create(&threads[i], 0, read_mutex_thread, &data); for (int i = 0; i < kThreads; i++) pthread_join(threads[i], 0); } TEST(Mutex, SpinWrite) { SpinMutex mtx; TestData data(&mtx); pthread_t threads[kThreads]; for (int i = 0; i < kThreads; i++) pthread_create(&threads[i], 0, write_mutex_thread, &data); for (int i = 0; i < kThreads; i++) pthread_join(threads[i], 0); } } // namespace __tsan Index: projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_flags.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_flags.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_flags.cc (revision 279191) @@ -1,63 +1,79 @@ //===-- ubsan_flags.cc ----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Runtime flags for UndefinedBehaviorSanitizer. // //===----------------------------------------------------------------------===// #include "ubsan_flags.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" namespace __ubsan { static const char *MaybeCallUbsanDefaultOptions() { return (&__ubsan_default_options) ? __ubsan_default_options() : ""; } -void InitializeCommonFlags() { - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - cf->print_summary = false; - // Override from user-specified string. - ParseCommonFlagsFromString(cf, MaybeCallUbsanDefaultOptions()); - // Override from environment variable. - ParseCommonFlagsFromString(cf, GetEnv("UBSAN_OPTIONS")); -} - Flags ubsan_flags; -static void ParseFlagsFromString(Flags *f, const char *str) { - if (!str) - return; - ParseFlag(str, &f->halt_on_error, "halt_on_error", - "Crash the program after printing the first error report"); - ParseFlag(str, &f->print_stacktrace, "print_stacktrace", - "Include full stacktrace into an error report"); +void Flags::SetDefaults() { +#define UBSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "ubsan_flags.inc" +#undef UBSAN_FLAG } -void InitializeFlags() { +void RegisterUbsanFlags(FlagParser *parser, Flags *f) { +#define UBSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "ubsan_flags.inc" +#undef UBSAN_FLAG +} + +void InitializeFlags(bool standalone) { Flags *f = flags(); - // Default values. - f->halt_on_error = false; - f->print_stacktrace = false; + FlagParser parser; + RegisterUbsanFlags(&parser, f); + + if (standalone) { + RegisterCommonFlags(&parser); + + SetCommonFlagsDefaults(); + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.print_summary = false; + OverrideCommonFlags(cf); + } else { + // Ignore common flags if not standalone. + // This is inconsistent with LSan, which allows common flags in LSAN_FLAGS. + // This is caused by undefined initialization order between ASan and UBsan, + // which makes it impossible to make sure that common flags from ASAN_OPTIONS + // have not been used (in __asan_init) before they are overwritten with flags + // from UBSAN_OPTIONS. + CommonFlags cf_ignored; + RegisterCommonFlags(&parser, &cf_ignored); + } + + f->SetDefaults(); // Override from user-specified string. - ParseFlagsFromString(f, MaybeCallUbsanDefaultOptions()); + parser.ParseString(MaybeCallUbsanDefaultOptions()); // Override from environment variable. - ParseFlagsFromString(f, GetEnv("UBSAN_OPTIONS")); + parser.ParseString(GetEnv("UBSAN_OPTIONS")); + SetVerbosity(common_flags()->verbosity); } } // namespace __ubsan #if !SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *__ubsan_default_options() { return ""; } } // extern "C" #endif Index: projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_flags.h =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_flags.h (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_flags.h (revision 279191) @@ -1,40 +1,42 @@ //===-- ubsan_flags.h -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Runtime flags for UndefinedBehaviorSanitizer. // //===----------------------------------------------------------------------===// #ifndef UBSAN_FLAGS_H #define UBSAN_FLAGS_H #include "sanitizer_common/sanitizer_internal_defs.h" namespace __ubsan { struct Flags { - bool halt_on_error; - bool print_stacktrace; +#define UBSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "ubsan_flags.inc" +#undef UBSAN_FLAG + + void SetDefaults(); }; extern Flags ubsan_flags; inline Flags *flags() { return &ubsan_flags; } -void InitializeCommonFlags(); -void InitializeFlags(); +void InitializeFlags(bool standalone); } // namespace __ubsan extern "C" { // Users may provide their own implementation of __ubsan_default_options to // override the default flag values. SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *__ubsan_default_options(); } // extern "C" #endif // UBSAN_FLAGS_H Index: projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_flags.inc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_flags.inc (nonexistent) +++ projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_flags.inc (revision 279191) @@ -0,0 +1,24 @@ +//===-- ubsan_flags.inc -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// UBSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_FLAG +# error "Define UBSAN_FLAG prior to including this file!" +#endif + +// UBSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +UBSAN_FLAG(bool, halt_on_error, false, + "Crash the program after printing the first error report") +UBSAN_FLAG(bool, print_stacktrace, false, + "Include full stacktrace into an error report") + Property changes on: projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_flags.inc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_init.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_init.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_init.cc (revision 279191) @@ -1,61 +1,63 @@ //===-- ubsan_init.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Initialization of UBSan runtime. // //===----------------------------------------------------------------------===// #include "ubsan_init.h" #include "ubsan_flags.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_symbolizer.h" using namespace __ubsan; static bool ubsan_inited; void __ubsan::InitIfNecessary() { #if !SANITIZER_CAN_USE_PREINIT_ARRAY // No need to lock mutex if we're initializing from preinit array. static StaticSpinMutex init_mu; SpinMutexLock l(&init_mu); #endif if (LIKELY(ubsan_inited)) return; + bool standalone = false; if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) { // WARNING: If this condition holds, then either UBSan runs in a standalone // mode, or initializer for another sanitizer hasn't run yet. In a latter // case, another sanitizer will overwrite "SanitizerToolName" and reparse // common flags. It means, that we are not allowed to *use* common flags // in this function. SanitizerToolName = "UndefinedBehaviorSanitizer"; - InitializeCommonFlags(); + standalone = true; } // Initialize UBSan-specific flags. - InitializeFlags(); + InitializeFlags(standalone); SuppressionContext::InitIfNecessary(); + InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); ubsan_inited = true; } #if SANITIZER_CAN_USE_PREINIT_ARRAY __attribute__((section(".preinit_array"), used)) void (*__local_ubsan_preinit)(void) = __ubsan::InitIfNecessary; #else // Use a dynamic initializer. class UbsanInitializer { public: UbsanInitializer() { InitIfNecessary(); } }; static UbsanInitializer ubsan_initializer; #endif // SANITIZER_CAN_USE_PREINIT_ARRAY Index: projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_type_hash.cc =================================================================== --- projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_type_hash.cc (revision 279190) +++ projects/clang360-import/contrib/compiler-rt/lib/ubsan/ubsan_type_hash.cc (revision 279191) @@ -1,255 +1,250 @@ //===-- ubsan_type_hash.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Implementation of a hash table for fast checking of inheritance // relationships. This file is only linked into C++ compilations, and is // permitted to use language features which require a C++ ABI library. // //===----------------------------------------------------------------------===// #include "ubsan_type_hash.h" #include "sanitizer_common/sanitizer_common.h" // The following are intended to be binary compatible with the definitions // given in the Itanium ABI. We make no attempt to be ODR-compatible with // those definitions, since existing ABI implementations aren't. namespace std { class type_info { public: virtual ~type_info(); const char *__type_name; }; } namespace __cxxabiv1 { /// Type info for classes with no bases, and base class for type info for /// classes with bases. class __class_type_info : public std::type_info { virtual ~__class_type_info(); }; /// Type info for classes with simple single public inheritance. class __si_class_type_info : public __class_type_info { public: virtual ~__si_class_type_info(); const __class_type_info *__base_type; }; class __base_class_type_info { public: const __class_type_info *__base_type; long __offset_flags; enum __offset_flags_masks { __virtual_mask = 0x1, __public_mask = 0x2, __offset_shift = 8 }; }; /// Type info for classes with multiple, virtual, or non-public inheritance. class __vmi_class_type_info : public __class_type_info { public: virtual ~__vmi_class_type_info(); unsigned int flags; unsigned int base_count; __base_class_type_info base_info[1]; }; } namespace abi = __cxxabiv1; // We implement a simple two-level cache for type-checking results. For each // (vptr,type) pair, a hash is computed. This hash is assumed to be globally // unique; if it collides, we will get false negatives, but: // * such a collision would have to occur on the *first* bad access, // * the probability of such a collision is low (and for a 64-bit target, is // negligible), and // * the vptr, and thus the hash, can be affected by ASLR, so multiple runs // give better coverage. // // The first caching layer is a small hash table with no chaining; buckets are // reused as needed. The second caching layer is a large hash table with open // chaining. We can freely evict from either layer since this is just a cache. // // FIXME: Make these hash table accesses thread-safe. The races here are benign: // assuming the unsequenced loads and stores don't misbehave too badly, // the worst case is false negatives or poor cache behavior, not false // positives or crashes. /// Find a bucket to store the given hash value in. static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { static const unsigned HashTableSize = 65537; static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize]; unsigned First = (V & 65535) ^ 1; unsigned Probe = First; for (int Tries = 5; Tries; --Tries) { if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V) return &__ubsan_vptr_hash_set[Probe]; Probe += ((V >> 16) & 65535) + 1; if (Probe >= HashTableSize) Probe -= HashTableSize; } // FIXME: Pick a random entry from the probe sequence to evict rather than // just taking the first. return &__ubsan_vptr_hash_set[First]; } /// A cache of recently-checked hashes. Mini hash table with "random" evictions. __ubsan::HashValue __ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize]; /// \brief Determine whether \p Derived has a \p Base base class subobject at /// offset \p Offset. -static bool isDerivedFromAtOffset(sptr Object, - const abi::__class_type_info *Derived, +static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived, const abi::__class_type_info *Base, sptr Offset) { if (Derived->__type_name == Base->__type_name) return Offset == 0; if (const abi::__si_class_type_info *SI = dynamic_cast(Derived)) - return isDerivedFromAtOffset(Object, SI->__base_type, Base, Offset); + return isDerivedFromAtOffset(SI->__base_type, Base, Offset); const abi::__vmi_class_type_info *VTI = dynamic_cast(Derived); if (!VTI) // No base class subobjects. return false; // Look for a base class which is derived from \p Base at the right offset. for (unsigned int base = 0; base != VTI->base_count; ++base) { // FIXME: Curtail the recursion if this base can't possibly contain the // given offset. sptr OffsetHere = VTI->base_info[base].__offset_flags >> abi::__base_class_type_info::__offset_shift; if (VTI->base_info[base].__offset_flags & - abi::__base_class_type_info::__virtual_mask) { - sptr VTable = *reinterpret_cast(Object); - OffsetHere = *reinterpret_cast(VTable + OffsetHere); - } - if (isDerivedFromAtOffset(Object + OffsetHere, - VTI->base_info[base].__base_type, Base, - Offset - OffsetHere)) + abi::__base_class_type_info::__virtual_mask) + // For now, just punt on virtual bases and say 'yes'. + // FIXME: OffsetHere is the offset in the vtable of the virtual base + // offset. Read the vbase offset out of the vtable and use it. return true; + if (isDerivedFromAtOffset(VTI->base_info[base].__base_type, + Base, Offset - OffsetHere)) + return true; } return false; } /// \brief Find the derived-most dynamic base class of \p Derived at offset /// \p Offset. -static const abi::__class_type_info * -findBaseAtOffset(sptr Object, const abi::__class_type_info *Derived, - sptr Offset) { +static const abi::__class_type_info *findBaseAtOffset( + const abi::__class_type_info *Derived, sptr Offset) { if (!Offset) return Derived; if (const abi::__si_class_type_info *SI = dynamic_cast(Derived)) - return findBaseAtOffset(Object, SI->__base_type, Offset); + return findBaseAtOffset(SI->__base_type, Offset); const abi::__vmi_class_type_info *VTI = dynamic_cast(Derived); if (!VTI) // No base class subobjects. return 0; for (unsigned int base = 0; base != VTI->base_count; ++base) { sptr OffsetHere = VTI->base_info[base].__offset_flags >> abi::__base_class_type_info::__offset_shift; if (VTI->base_info[base].__offset_flags & - abi::__base_class_type_info::__virtual_mask) { - sptr VTable = *reinterpret_cast(Object); - OffsetHere = *reinterpret_cast(VTable + OffsetHere); - } - if (const abi::__class_type_info *Base = findBaseAtOffset( - Object + OffsetHere, VTI->base_info[base].__base_type, - Offset - OffsetHere)) + abi::__base_class_type_info::__virtual_mask) + // FIXME: Can't handle virtual bases yet. + continue; + if (const abi::__class_type_info *Base = + findBaseAtOffset(VTI->base_info[base].__base_type, + Offset - OffsetHere)) return Base; } return 0; } namespace { struct VtablePrefix { /// The offset from the vptr to the start of the most-derived object. /// This should never be greater than zero, and will usually be exactly /// zero. sptr Offset; /// The type_info object describing the most-derived class type. std::type_info *TypeInfo; }; VtablePrefix *getVtablePrefix(void *Object) { VtablePrefix **VptrPtr = reinterpret_cast(Object); if (!*VptrPtr) return 0; VtablePrefix *Prefix = *VptrPtr - 1; if (Prefix->Offset > 0 || !Prefix->TypeInfo) // This can't possibly be a valid vtable. return 0; return Prefix; } } bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { // A crash anywhere within this function probably means the vptr is corrupted. // FIXME: Perform these checks more cautiously. // Check whether this is something we've evicted from the cache. HashValue *Bucket = getTypeCacheHashTableBucket(Hash); if (*Bucket == Hash) { __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; return true; } VtablePrefix *Vtable = getVtablePrefix(Object); if (!Vtable) return false; // Check that this is actually a type_info object for a class type. abi::__class_type_info *Derived = dynamic_cast(Vtable->TypeInfo); if (!Derived) return false; abi::__class_type_info *Base = (abi::__class_type_info*)Type; - if (!isDerivedFromAtOffset(reinterpret_cast(Object), Derived, Base, - -Vtable->Offset)) + if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset)) return false; // Success. Cache this result. __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; *Bucket = Hash; return true; } __ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) { VtablePrefix *Vtable = getVtablePrefix(Object); if (!Vtable) return DynamicTypeInfo(0, 0, 0); const abi::__class_type_info *ObjectType = findBaseAtOffset( - reinterpret_cast(Object), - static_cast(Vtable->TypeInfo), - -Vtable->Offset); + static_cast(Vtable->TypeInfo), + -Vtable->Offset); return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, ObjectType ? ObjectType->__type_name : ""); } Index: projects/clang360-import/contrib/compiler-rt =================================================================== --- projects/clang360-import/contrib/compiler-rt (revision 279190) +++ projects/clang360-import/contrib/compiler-rt (revision 279191) Property changes on: projects/clang360-import/contrib/compiler-rt ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /vendor/compiler-rt/dist:r276796-279189 Index: projects/clang360-import/lib/libclang_rt/Makefile.inc =================================================================== --- projects/clang360-import/lib/libclang_rt/Makefile.inc (revision 279190) +++ projects/clang360-import/lib/libclang_rt/Makefile.inc (revision 279191) @@ -1,25 +1,26 @@ # $FreeBSD$ .include CRTARCH=${MACHINE_CPUARCH:C/amd64/x86_64/} CRTSRC=${.CURDIR}/../../../contrib/compiler-rt LIBDIR=/usr/lib/clang/3.6.0/lib/freebsd NO_PIC= MK_PROFILE=no WARNS?=0 SSP_CFLAGS= CFLAGS+=-DNDEBUG CFLAGS+=${PICFLAG} CFLAGS+=-fno-builtin CFLAGS+=-fno-exceptions CFLAGS+=-fno-rtti CFLAGS+=-fno-stack-protector CFLAGS+=-funwind-tables CFLAGS+=-fvisibility-inlines-hidden CFLAGS+=-fvisibility=hidden CFLAGS+=-I${CRTSRC}/lib +CXXFLAGS+=-std=c++11 Index: projects/clang360-import/lib/libclang_rt/asan/Makefile =================================================================== --- projects/clang360-import/lib/libclang_rt/asan/Makefile (revision 279190) +++ projects/clang360-import/lib/libclang_rt/asan/Makefile (revision 279191) @@ -1,78 +1,80 @@ # $FreeBSD$ .include LIB= clang_rt.asan-${CRTARCH} .PATH: ${CRTSRC}/lib/asan\ ${CRTSRC}/lib/interception\ ${CRTSRC}/lib/sanitizer_common\ ${CRTSRC}/lib/lsan SRCS= asan_preinit.cc\ - asan_allocator2.cc\ + asan_allocator.cc\ asan_activation.cc\ asan_debugging.cc\ asan_fake_stack.cc\ + asan_flags.cc\ asan_globals.cc\ asan_interceptors.cc\ asan_linux.cc\ asan_mac.cc\ asan_malloc_linux.cc\ asan_malloc_mac.cc\ asan_malloc_win.cc\ asan_poisoning.cc\ asan_posix.cc\ asan_report.cc\ asan_rtl.cc\ asan_stack.cc\ asan_stats.cc\ asan_suppressions.cc\ asan_thread.cc\ asan_win.cc\ interception_linux.cc\ interception_mac.cc\ interception_win.cc\ interception_type_test.cc\ sanitizer_allocator.cc\ sanitizer_common.cc\ sanitizer_deadlock_detector1.cc\ sanitizer_deadlock_detector2.cc\ sanitizer_flags.cc\ + sanitizer_flag_parser.cc\ sanitizer_libc.cc\ sanitizer_libignore.cc\ sanitizer_linux.cc\ sanitizer_mac.cc\ sanitizer_persistent_allocator.cc\ sanitizer_platform_limits_linux.cc\ sanitizer_platform_limits_posix.cc\ sanitizer_posix.cc\ sanitizer_printf.cc\ sanitizer_procmaps_common.cc\ sanitizer_procmaps_freebsd.cc\ sanitizer_procmaps_linux.cc\ sanitizer_procmaps_mac.cc\ sanitizer_stackdepot.cc\ sanitizer_stacktrace.cc\ sanitizer_stacktrace_printer.cc\ sanitizer_suppressions.cc\ sanitizer_symbolizer.cc\ sanitizer_symbolizer_libbacktrace.cc\ sanitizer_symbolizer_win.cc\ sanitizer_tls_get_addr.cc\ sanitizer_thread_registry.cc\ sanitizer_win.cc\ sanitizer_common_libcdep.cc\ sanitizer_coverage_libcdep.cc\ sanitizer_coverage_mapping_libcdep.cc\ sanitizer_linux_libcdep.cc\ sanitizer_posix_libcdep.cc\ sanitizer_stacktrace_libcdep.cc\ sanitizer_stoptheworld_linux_libcdep.cc\ sanitizer_symbolizer_libcdep.cc\ sanitizer_symbolizer_posix_libcdep.cc\ sanitizer_unwind_posix_libcdep.cc\ lsan_common.cc\ lsan_common_linux.cc .include Index: projects/clang360-import/lib/libclang_rt/san/Makefile =================================================================== --- projects/clang360-import/lib/libclang_rt/san/Makefile (revision 279190) +++ projects/clang360-import/lib/libclang_rt/san/Makefile (revision 279191) @@ -1,48 +1,49 @@ # $FreeBSD$ .include LIB= clang_rt.san-${CRTARCH} .PATH: ${CRTSRC}/lib/sanitizer_common\ SRCS= sanitizer_allocator.cc\ sanitizer_common.cc\ sanitizer_deadlock_detector1.cc\ sanitizer_deadlock_detector2.cc\ sanitizer_flags.cc\ + sanitizer_flag_parser.cc\ sanitizer_libc.cc\ sanitizer_libignore.cc\ sanitizer_linux.cc\ sanitizer_mac.cc\ sanitizer_persistent_allocator.cc\ sanitizer_platform_limits_linux.cc\ sanitizer_platform_limits_posix.cc\ sanitizer_posix.cc\ sanitizer_printf.cc\ sanitizer_procmaps_common.cc\ sanitizer_procmaps_freebsd.cc\ sanitizer_procmaps_linux.cc\ sanitizer_procmaps_mac.cc\ sanitizer_stackdepot.cc\ sanitizer_stacktrace.cc\ sanitizer_stacktrace_printer.cc\ sanitizer_suppressions.cc\ sanitizer_symbolizer.cc\ sanitizer_symbolizer_libbacktrace.cc\ sanitizer_symbolizer_win.cc\ sanitizer_tls_get_addr.cc\ sanitizer_thread_registry.cc\ sanitizer_win.cc\ sanitizer_common_libcdep.cc\ sanitizer_coverage_libcdep.cc\ sanitizer_coverage_mapping_libcdep.cc\ sanitizer_linux_libcdep.cc\ sanitizer_posix_libcdep.cc\ sanitizer_stacktrace_libcdep.cc\ sanitizer_stoptheworld_linux_libcdep.cc\ sanitizer_symbolizer_libcdep.cc\ sanitizer_symbolizer_posix_libcdep.cc\ sanitizer_unwind_posix_libcdep.cc .include Index: projects/clang360-import/lib/libcompiler_rt/Makefile =================================================================== --- projects/clang360-import/lib/libcompiler_rt/Makefile (revision 279190) +++ projects/clang360-import/lib/libcompiler_rt/Makefile (revision 279191) @@ -1,218 +1,219 @@ # $FreeBSD$ .include LIB= compiler_rt NO_PIC= WARNS?= 2 CFLAGS+=${PICFLAG} -fvisibility=hidden -DVISIBILITY_HIDDEN +CFLAGS+=-I${.CURDIR}/../../contrib/libcxxrt .if ${MACHINE_CPUARCH} == "amd64" CRTARCH=x86_64 .else CRTARCH=${MACHINE_CPUARCH} .endif CRTSRC=${.CURDIR}/../../contrib/compiler-rt/lib/builtins .PATH: ${CRTSRC}/${CRTARCH} ${CRTSRC} SRCF= absvdi2 \ absvsi2 \ absvti2 \ addtf3 \ addvdi3 \ addvsi3 \ addvti3 \ apple_versioning \ ashldi3 \ ashlti3 \ ashrdi3 \ ashrti3 \ clear_cache \ clzdi2 \ clzsi2 \ clzti2 \ cmpdi2 \ cmpti2 \ ctzdi2 \ ctzsi2 \ ctzti2 \ divdc3 \ divdi3 \ divmoddi4 \ divmodsi4 \ divsc3 \ divtf3 \ divti3 \ divxc3 \ enable_execute_stack \ eprintf \ ffsdi2 \ ffsti2 \ fixdfdi \ fixdfti \ fixsfdi \ fixsfti \ fixunsdfdi \ fixunsdfsi \ fixunsdfti \ fixunssfdi \ fixunssfsi \ fixunssfti \ fixunsxfdi \ fixunsxfsi \ fixunsxfti \ fixxfdi \ fixxfti \ floatdidf \ floatdisf \ floatdixf \ floattidf \ floattisf \ floattixf \ floatundidf \ floatundisf \ floatundixf \ floatunsidf \ floatunsisf \ floatuntidf \ floatuntisf \ floatuntixf \ gcc_personality_v0 \ int_util \ lshrdi3 \ lshrti3 \ moddi3 \ modti3 \ muldc3 \ muldi3 \ mulodi4 \ mulosi4 \ muloti4 \ mulsc3 \ multf3 \ multi3 \ mulvdi3 \ mulvsi3 \ mulvti3 \ mulxc3 \ negdf2 \ negdi2 \ negsf2 \ negti2 \ negvdi2 \ negvsi2 \ negvti2 \ paritydi2 \ paritysi2 \ parityti2 \ popcountdi2 \ popcountsi2 \ popcountti2 \ powidf2 \ powisf2 \ powitf2 \ powixf2 \ subtf3 \ subvdi3 \ subvsi3 \ subvti3 \ trampoline_setup \ ucmpdi2 \ ucmpti2 \ udivdi3 \ udivmoddi4 \ udivmodsi4 \ udivmodti4 \ udivti3 \ umoddi3 \ umodti3 # These are already shipped by libc.a on arm and mips .if ${MACHINE_CPUARCH} != "arm" && ${MACHINE_CPUARCH} != "mips" SRCF+= adddf3 \ addsf3 \ divdf3 \ divsf3 \ extendsfdf2 \ fixdfsi \ fixsfsi \ floatsidf \ floatsisf \ muldf3 \ mulsf3 \ subdf3 \ subsf3 \ truncdfsf2 .endif .if ${MACHINE_CPUARCH} != "arm" SRCF+= comparedf2 \ comparesf2 .endif .if ${MACHINE_CPUARCH} != "mips" SRCF+= divsi3 \ modsi3 \ udivsi3 \ umodsi3 .endif # FreeBSD-specific atomic intrinsics. .if ${MACHINE_CPUARCH} == "arm" || ${MACHINE_CPUARCH} == "armv6" .PATH: ${.CURDIR}/../../sys/arm/arm SRCF+= stdatomic CFLAGS+= -DEMIT_SYNC_ATOMICS .elif ${MACHINE_CPUARCH} == "mips" .PATH: ${.CURDIR}/../../sys/mips/mips SRCF+= stdatomic .endif .for file in ${SRCF} . if ${MACHINE_ARCH:Marm*hf*} != "" && exists(${CRTSRC}/${CRTARCH}/${file}vfp.S) SRCS+= ${file}vfp.S . elif !(${MACHINE_CPUARCH} == "arm" && ${MACHINE_ARCH:Marm*hf*} == "") && exists(${CRTSRC}/${CRTARCH}/${file}.S) SRCS+= ${file}.S . else SRCS+= ${file}.c . endif .endfor .if ${MACHINE_CPUARCH} == "arm" SRCS+= aeabi_div0.c \ aeabi_idivmod.S \ aeabi_ldivmod.S \ aeabi_memcmp.S \ aeabi_memcpy.S \ aeabi_memmove.S \ aeabi_memset.S \ aeabi_uidivmod.S \ aeabi_uldivmod.S \ bswapdi2.S \ bswapsi2.S \ switch16.S \ switch32.S \ switch8.S \ switchu8.S \ sync_synchronize.S .endif .if ${MK_INSTALLLIB} != "no" SYMLINKS+=libcompiler_rt.a ${LIBDIR}/libgcc.a .endif .if ${MK_PROFILE} != "no" SYMLINKS+=libcompiler_rt_p.a ${LIBDIR}/libgcc_p.a .endif .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" || \ ${MACHINE_CPUARCH} == "powerpc" AFLAGS+=--noexecstack ACFLAGS+=-Wa,--noexecstack .endif .include