aboutsummaryrefslogtreecommitdiff
path: root/lib/sanitizer_common
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sanitizer_common')
-rw-r--r--lib/sanitizer_common/CMakeLists.txt12
-rw-r--r--lib/sanitizer_common/sanitizer_allocator.cc9
-rw-r--r--lib/sanitizer_common/sanitizer_allocator.h83
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_internal.h9
-rw-r--r--lib/sanitizer_common/sanitizer_common.cc59
-rw-r--r--lib/sanitizer_common/sanitizer_common.h84
-rw-r--r--lib/sanitizer_common/sanitizer_common_interceptors.inc65
-rw-r--r--lib/sanitizer_common/sanitizer_common_libcdep.cc66
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_libcdep.cc345
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc57
-rw-r--r--lib/sanitizer_common/sanitizer_deadlock_detector.h6
-rw-r--r--lib/sanitizer_common/sanitizer_flag_parser.cc153
-rw-r--r--lib/sanitizer_common/sanitizer_flag_parser.h121
-rw-r--r--lib/sanitizer_common/sanitizer_flags.cc294
-rw-r--r--lib/sanitizer_common/sanitizer_flags.h74
-rw-r--r--lib/sanitizer_common/sanitizer_flags.inc149
-rw-r--r--lib/sanitizer_common/sanitizer_internal_defs.h20
-rw-r--r--lib/sanitizer_common/sanitizer_libc.cc17
-rw-r--r--lib/sanitizer_common/sanitizer_libc.h21
-rw-r--r--lib/sanitizer_common/sanitizer_linux.cc114
-rw-r--r--lib/sanitizer_common/sanitizer_linux_libcdep.cc60
-rw-r--r--lib/sanitizer_common/sanitizer_list.h16
-rw-r--r--lib/sanitizer_common/sanitizer_mac.cc17
-rw-r--r--lib/sanitizer_common/sanitizer_mac.h1
-rw-r--r--lib/sanitizer_common/sanitizer_mutex.h6
-rw-r--r--lib/sanitizer_common/sanitizer_platform_interceptors.h45
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.cc6
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.h11
-rw-r--r--lib/sanitizer_common/sanitizer_posix.cc8
-rw-r--r--lib/sanitizer_common/sanitizer_posix_libcdep.cc12
-rw-r--r--lib/sanitizer_common/sanitizer_quarantine.h18
-rw-r--r--lib/sanitizer_common/sanitizer_stackdepot.cc9
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace.cc19
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace.h43
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc2
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc8
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h2
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc2
-rw-r--r--lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc6
-rw-r--r--lib/sanitizer_common/sanitizer_win.cc110
-rwxr-xr-xlib/sanitizer_common/scripts/check_lint.sh9
-rwxr-xr-xlib/sanitizer_common/scripts/sancov.py2
-rw-r--r--lib/sanitizer_common/tests/CMakeLists.txt25
-rw-r--r--lib/sanitizer_common/tests/sanitizer_allocator_test.cc50
-rw-r--r--lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc2
-rw-r--r--lib/sanitizer_common/tests/sanitizer_flags_test.cc104
-rw-r--r--lib/sanitizer_common/tests/sanitizer_libc_test.cc70
-rw-r--r--lib/sanitizer_common/tests/sanitizer_printf_test.cc13
-rw-r--r--lib/sanitizer_common/tests/sanitizer_procmaps_test.cc1
-rw-r--r--lib/sanitizer_common/tests/sanitizer_test_utils.h6
50 files changed, 1703 insertions, 738 deletions
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index fe4418cd3cfa..86697e7f7c40 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -7,6 +7,7 @@ set(SANITIZER_SOURCES
sanitizer_deadlock_detector1.cc
sanitizer_deadlock_detector2.cc
sanitizer_flags.cc
+ sanitizer_flag_parser.cc
sanitizer_libc.cc
sanitizer_libignore.cc
sanitizer_linux.cc
@@ -63,7 +64,9 @@ set(SANITIZER_HEADERS
sanitizer_common_syscalls.inc
sanitizer_deadlock_detector.h
sanitizer_deadlock_detector_interface.h
+ sanitizer_flag_parser.h
sanitizer_flags.h
+ sanitizer_flags.inc
sanitizer_internal_defs.h
sanitizer_lfstack.h
sanitizer_libc.h
@@ -105,11 +108,10 @@ endif()
set(SANITIZER_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_no_rtti_flag(SANITIZER_CFLAGS)
-# Stack frames on PowerPC are much larger than anticipated.
-if(NOT ${LLVM_NATIVE_ARCH} STREQUAL "PowerPC")
- append_list_if(COMPILER_RT_HAS_WFRAME_LARGER_THAN_FLAG -Wframe-larger-than=512 SANITIZER_CFLAGS)
-endif()
-append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors SANITIZER_CFLAGS)
+append_list_if(SANITIZER_LIMIT_FRAME_SIZE -Wframe-larger-than=512
+ SANITIZER_CFLAGS)
+append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors
+ SANITIZER_CFLAGS)
add_custom_target(sanitizer_common)
set(SANITIZER_RUNTIME_LIBRARIES)
diff --git a/lib/sanitizer_common/sanitizer_allocator.cc b/lib/sanitizer_common/sanitizer_allocator.cc
index 47509f83665b..03b3e83153de 100644
--- a/lib/sanitizer_common/sanitizer_allocator.cc
+++ b/lib/sanitizer_common/sanitizer_allocator.cc
@@ -14,7 +14,6 @@
#include "sanitizer_allocator.h"
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
namespace __sanitizer {
@@ -61,7 +60,7 @@ InternalAllocator *internal_allocator() {
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);
}
}
@@ -140,14 +139,12 @@ bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) {
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
diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h
index 23218016b791..b5105f8c2555 100644
--- a/lib/sanitizer_common/sanitizer_allocator.h
+++ b/lib/sanitizer_common/sanitizer_allocator.h
@@ -23,8 +23,8 @@
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.
@@ -211,6 +211,7 @@ class AllocatorStats {
void Init() {
internal_memset(this, 0, sizeof(*this));
}
+ void InitLinkerInitialized() {}
void Add(AllocatorStat i, uptr v) {
v += atomic_load(&stats_[i], memory_order_relaxed);
@@ -240,11 +241,14 @@ class AllocatorStats {
// 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_);
@@ -1002,9 +1006,14 @@ struct SizeClassAllocatorLocalCache {
template <class MapUnmapCallback = NoOpMapUnmapCallback>
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) {
@@ -1012,7 +1021,9 @@ class LargeMmapAllocator {
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<uptr>(
MmapOrDie(map_size, "LargeMmapAllocator"));
CHECK(IsAligned(map_beg, page_size_));
@@ -1048,6 +1059,16 @@ class LargeMmapAllocator {
return reinterpret_cast<void*>(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);
{
@@ -1226,6 +1247,7 @@ class LargeMmapAllocator {
struct Stats {
uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64];
} stats;
+ atomic_uint8_t may_return_null_;
SpinMutex mutex_;
};
@@ -1239,19 +1261,32 @@ template <class PrimaryAllocator, class AllocatorCache,
class SecondaryAllocator> // 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;
@@ -1267,6 +1302,30 @@ class CombinedAllocator {
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))
@@ -1379,6 +1438,8 @@ class CombinedAllocator {
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.
diff --git a/lib/sanitizer_common/sanitizer_allocator_internal.h b/lib/sanitizer_common/sanitizer_allocator_internal.h
index 4409fd65bf31..9b9cfd0b5931 100644
--- a/lib/sanitizer_common/sanitizer_allocator_internal.h
+++ b/lib/sanitizer_common/sanitizer_allocator_internal.h
@@ -49,6 +49,15 @@ 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
diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc
index c3740f24a366..489081e0760b 100644
--- a/lib/sanitizer_common/sanitizer_common.cc
+++ b/lib/sanitizer_common/sanitizer_common.cc
@@ -12,13 +12,17 @@
//===----------------------------------------------------------------------===//
#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)
@@ -94,19 +98,23 @@ uptr stoptheworld_tracer_pid = 0;
// 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);
}
@@ -125,8 +133,8 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
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;
@@ -135,7 +143,7 @@ uptr ReadFileToBuffer(const char *file_name, char **buff,
// 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__);
@@ -145,6 +153,10 @@ uptr ReadFileToBuffer(const char *file_name, char **buff,
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;
@@ -233,20 +245,28 @@ void ReportErrorSummary(const char *error_type, const char *file,
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;
@@ -280,4 +300,9 @@ void __sanitizer_set_report_path(const char *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"
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index c00dce66bb07..720cd73a43c5 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -16,10 +16,11 @@
#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;
@@ -40,6 +41,14 @@ 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(&current_verbosity, verbosity, memory_order_relaxed);
+}
+INLINE int Verbosity() {
+ return atomic_load(&current_verbosity, memory_order_relaxed);
+}
+
uptr GetPageSize();
uptr GetPageSizeCached();
uptr GetMmapGranularity();
@@ -67,6 +76,8 @@ 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.
@@ -135,11 +146,11 @@ 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.
@@ -179,8 +190,8 @@ uptr OpenFile(const char *filename, bool write);
// 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'.
@@ -214,10 +225,13 @@ 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();
@@ -245,11 +259,18 @@ bool SanitizerGetThreadName(char *name, int max_len);
// 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);
@@ -376,14 +397,14 @@ INLINE int ToLower(int c) {
// small vectors.
// WARNING: The current implementation supports only POD types.
template<typename T>
-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) {
@@ -434,15 +455,24 @@ class InternalMmapVector {
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<typename T>
+class InternalMmapVector : public InternalMmapVectorNoCtor<T> {
+ public:
+ explicit InternalMmapVector(uptr initial_capacity) {
+ InternalMmapVectorNoCtor<T>::Initialize(initial_capacity);
+ }
+ ~InternalMmapVector() { InternalMmapVectorNoCtor<T>::Destroy(); }
+ // Disallow evil constructors.
+ InternalMmapVector(const InternalMmapVector&);
+ void operator=(const InternalMmapVector&);
+};
+
// HeapSort for arrays and InternalMmapVector.
template<class Container, class Compare>
void InternalSort(Container *v, uptr size, Compare comp) {
@@ -501,28 +531,30 @@ uptr InternalBinarySearch(const Container &v, uptr first, uptr last,
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<AddressRange>::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<AddressRange> ranges_;
};
// OS-dependent function that fills array with descriptions of at most
@@ -556,6 +588,10 @@ 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
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 274e87c3d67d..87c33e186320 100644
--- a/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -17,6 +17,7 @@
// 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
@@ -43,6 +44,8 @@
#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
@@ -82,7 +85,7 @@
#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
@@ -915,6 +918,16 @@ 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)
@@ -991,6 +1004,14 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size,
#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); \
@@ -1007,8 +1028,12 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size,
#if SANITIZER_INTERCEPT_IOCTL
#include "sanitizer_common_interceptors_ioctl.inc"
-INTERCEPTOR(int, ioctl, int d, unsigned 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);
@@ -1017,6 +1042,10 @@ INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) {
// 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) {
@@ -2139,6 +2168,16 @@ INTERCEPTOR(int, sysinfo, void *info) {
#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);
@@ -2167,6 +2206,7 @@ INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry,
}
#define INIT_READDIR \
+ COMMON_INTERCEPT_FUNCTION(opendir); \
COMMON_INTERCEPT_FUNCTION(readdir); \
COMMON_INTERCEPT_FUNCTION(readdir_r);
#else
@@ -2560,6 +2600,19 @@ INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) {
#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;
@@ -3868,6 +3921,12 @@ INTERCEPTOR(__sanitizer_clock_t, times, void *tms) {
#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);
@@ -4762,6 +4821,7 @@ static void InitializeCommonInterceptors() {
INIT_SCANF;
INIT_ISOC99_SCANF;
INIT_PRINTF;
+ INIT_PRINTF_L;
INIT_ISOC99_PRINTF;
INIT_FREXP;
INIT_FREXPF_FREXPL;
@@ -4812,6 +4872,7 @@ static void InitializeCommonInterceptors() {
INIT_CANONICALIZE_FILE_NAME;
INIT_CONFSTR;
INIT_SCHED_GETAFFINITY;
+ INIT_SCHED_GETPARAM;
INIT_STRERROR;
INIT_STRERROR_R;
INIT_XPG_STRERROR_R;
diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc
index 20c1d5a78987..e357e1cbbfc9 100644
--- a/lib/sanitizer_common/sanitizer_common_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc
@@ -13,6 +13,7 @@
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
+#include "sanitizer_stackdepot.h"
#include "sanitizer_stacktrace.h"
#include "sanitizer_symbolizer.h"
@@ -59,6 +60,71 @@ void ReportErrorSummary(const char *error_type, StackTrace *stack) {
#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
diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
index 3fa8a18696f8..e8f42f68a42f 100644
--- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
@@ -12,14 +12,16 @@
//
// 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:
@@ -56,23 +58,32 @@ static atomic_uintptr_t coverage_counter;
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();
@@ -80,7 +91,7 @@ class CoverageData {
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;
@@ -96,45 +107,41 @@ class CoverageData {
// Descriptor of the file mapped pc array.
int pc_fd;
+ // Vector of coverage guard arrays, protected by mu.
+ InternalMmapVectorNoCtor<s32*> 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());
@@ -142,19 +149,23 @@ void CoverageData::DirectOpen() {
}
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<uptr *>(
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<uptr **>(MmapNoReserveOrDie(
@@ -162,30 +173,72 @@ void CoverageData::Init() {
atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed);
atomic_store(&cc_array_index, 0, memory_order_relaxed);
- tr_event_array = reinterpret_cast<u32 *>(
- 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<u32 *>(MmapNoReserveOrDie(
+ sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(),
+ "CovInit::tr_event_array"));
+ Mprotect(reinterpret_cast<uptr>(&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<TracedPc *>(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<s32>(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() {
@@ -203,15 +256,16 @@ 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);
@@ -220,29 +274,45 @@ void CoverageData::Extend(uptr npcs) {
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<s32>(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<atomic_uint32_t*>(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<atomic_uint8_t*>(guard);
- atomic_store(atomic_guard, 1, memory_order_relaxed);
}
// Registers a pair caller=>callee.
@@ -338,18 +408,19 @@ static void CovWritePacked(int pid, const char *module, const void *blob,
// If packed = true and name == 0: <pid>.<sancov>.<packed>.
// If packed = true and name != 0: <name>.<sancov>.<packed> (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)) {
@@ -361,23 +432,18 @@ static int CovOpenFile(bool packed, const char* name) {
// 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 = "<unknown>";
- 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 = "<unknown>";
+ 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;
@@ -386,10 +452,21 @@ void CoverageData::DumpTrace() {
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<u8*>(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
@@ -434,28 +511,45 @@ void CoverageData::DumpCallerCalleePairs() {
// 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<u32>(*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<char> 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<u32>(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<u32> offsets(size);
uptr *vb = coverage_data.data();
@@ -491,8 +585,8 @@ static void CovDump() {
} 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));
@@ -506,13 +600,12 @@ static void CovDump() {
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;
@@ -524,7 +617,7 @@ void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
int MaybeOpenCovFile(const char *name) {
CHECK(name);
- if (!common_flags()->coverage) return -1;
+ if (!coverage_enabled) return -1;
return CovOpenFile(true /* packed */, name);
}
@@ -536,28 +629,60 @@ 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<atomic_uint32_t*>(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);
}
@@ -571,11 +696,23 @@ uptr __sanitizer_get_total_unique_coverage() {
}
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(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
-void __sanitizer_cov_trace_basic_block(uptr *cache) {
- coverage_data.TraceBasicaBlock(cache);
+uptr __sanitizer_get_coverage_guards(uptr **data) {
+ *data = coverage_data.data();
+ return coverage_data.size();
}
} // extern "C"
diff --git a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
index fe72d06a2fb1..6b5e91fbc018 100644
--- a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
@@ -62,8 +62,8 @@ struct CachedMapping {
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);
@@ -71,36 +71,41 @@ void CovUpdateMapping(uptr caller_pc) {
return;
InternalScopedString text(kMaxTextSize);
- InternalScopedBuffer<LoadedModule> 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<LoadedModule> 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();
}
@@ -111,9 +116,9 @@ void CovUpdateMapping(uptr caller_pc) {
}
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)) {
diff --git a/lib/sanitizer_common/sanitizer_deadlock_detector.h b/lib/sanitizer_common/sanitizer_deadlock_detector.h
index 90e1cc4eb597..86d5743e9794 100644
--- a/lib/sanitizer_common/sanitizer_deadlock_detector.h
+++ b/lib/sanitizer_common/sanitizer_deadlock_detector.h
@@ -50,6 +50,8 @@ class DeadlockDetectorTLS {
if (epoch_ == current_epoch) return;
bv_.clear();
epoch_ = current_epoch;
+ n_recursive_locks = 0;
+ n_all_locks_ = 0;
}
uptr getEpoch() const { return epoch_; }
@@ -83,7 +85,8 @@ class DeadlockDetectorTLS {
}
}
// 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<u32>(lock_id)) {
@@ -175,6 +178,7 @@ class DeadlockDetector {
recycled_nodes_.clear();
available_nodes_.setAll();
g_.clear();
+ n_edges_ = 0;
return getAvailableNode(data);
}
diff --git a/lib/sanitizer_common/sanitizer_flag_parser.cc b/lib/sanitizer_common/sanitizer_flag_parser.cc
new file mode 100644
index 000000000000..d125002daf4c
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_flag_parser.cc
@@ -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
diff --git a/lib/sanitizer_common/sanitizer_flag_parser.h b/lib/sanitizer_common/sanitizer_flag_parser.h
new file mode 100644
index 000000000000..87afb8238c01
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_flag_parser.h
@@ -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 <typename T>
+class FlagHandler : public FlagHandlerBase {
+ T *t_;
+
+ public:
+ explicit FlagHandler(T *t) : t_(t) {}
+ bool Parse(const char *value);
+};
+
+template <>
+inline bool FlagHandler<bool>::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<const char *>::Parse(const char *value) {
+ *t_ = internal_strdup(value);
+ return true;
+}
+
+template <>
+inline bool FlagHandler<int>::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<uptr>::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 <typename T>
+static void RegisterFlag(FlagParser *parser, const char *name, const char *desc,
+ T *var) {
+ FlagHandler<T> *fh = new (FlagParser::Alloc) FlagHandler<T>(var); // NOLINT
+ parser->RegisterHandler(name, fh, desc);
+}
+
+void ReportUnrecognizedFlags();
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FLAG_REGISTRY_H
diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc
index 40b6ec067150..a2965351cf2a 100644
--- a/lib/sanitizer_common/sanitizer_flags.cc
+++ b/lib/sanitizer_common/sanitizer_flags.cc
@@ -16,6 +16,7 @@
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
#include "sanitizer_list.h"
+#include "sanitizer_flag_parser.h"
namespace __sanitizer {
@@ -34,274 +35,53 @@ IntrusiveList<FlagDescription> flag_descriptions;
# 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;
- }
- 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));
-}
-
-static LowLevelAllocator allocator_for_flags;
-
-// The linear scan is suboptimal, but the number of flags is relatively small.
-bool FlagInDescriptionList(const char *name) {
- IntrusiveList<FlagDescription>::Iterator it(&flag_descriptions);
- while (it.hasNext()) {
- if (!internal_strcmp(it.next()->name, name)) return true;
+ parser_->ParseString(data);
+ UnmapOrDie(data, data_mapped_size);
+ 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<FlagDescription>::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<int>(internal_atoll(value));
+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");
}
-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<uptr>(internal_atoll(value));
-}
+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
-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
diff --git a/lib/sanitizer_common/sanitizer_flags.h b/lib/sanitizer_common/sanitizer_flags.h
index 4791397a5761..fda6d710757e 100644
--- a/lib/sanitizer_common/sanitizer_flags.h
+++ b/lib/sanitizer_common/sanitizer_flags.h
@@ -18,62 +18,38 @@
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
diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc
new file mode 100644
index 000000000000..e8c8a87537d7
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_flags.inc
@@ -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. ")
diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h
index 97eef744bd14..2a0b41f0bb99 100644
--- a/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -15,6 +15,10 @@
#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)
@@ -81,8 +85,9 @@ 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;
@@ -120,7 +125,7 @@ extern "C" {
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,
@@ -240,7 +245,7 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
#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)
@@ -320,4 +325,11 @@ extern "C" void* _ReturnAddress(void);
} 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
diff --git a/lib/sanitizer_common/sanitizer_libc.cc b/lib/sanitizer_common/sanitizer_libc.cc
index d8bd1cf7a7e0..cb162a4c4984 100644
--- a/lib/sanitizer_common/sanitizer_libc.cc
+++ b/lib/sanitizer_common/sanitizer_libc.cc
@@ -28,6 +28,15 @@ void *internal_memchr(const void *s, int c, uptr n) {
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<void *>(const_cast<char *>(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;
@@ -101,6 +110,14 @@ char* internal_strdup(const char *s) {
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;
diff --git a/lib/sanitizer_common/sanitizer_libc.h b/lib/sanitizer_common/sanitizer_libc.h
index 6995626821ab..c086b8a9139e 100644
--- a/lib/sanitizer_common/sanitizer_libc.h
+++ b/lib/sanitizer_common/sanitizer_libc.h
@@ -26,6 +26,7 @@ namespace __sanitizer {
// 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);
@@ -38,6 +39,7 @@ 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);
@@ -98,6 +100,25 @@ 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);
diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc
index 36de1ec70e97..26138ba29d35 100644
--- a/lib/sanitizer_common/sanitizer_linux.cc
+++ b/lib/sanitizer_common/sanitizer_linux.cc
@@ -31,6 +31,17 @@
#include <asm/param.h>
#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 <sys/types.h>
+#define stat kernel_stat
+#include <asm/stat.h>
+#undef stat
+#endif
+
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
@@ -98,14 +109,16 @@ namespace __sanitizer {
#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
}
@@ -179,6 +192,26 @@ static void stat64_to_stat(struct stat64 *in, struct stat *out) {
}
#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);
@@ -186,7 +219,15 @@ uptr internal_stat(const char *path, void *buf) {
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);
@@ -381,33 +422,6 @@ static void ReadNullSepFileToArray(const char *path, char ***arr,
}
#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) {
@@ -435,32 +449,18 @@ void ReExec() {
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<atomic_uint32_t *>(&opaque_storage_);
if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
return;
@@ -760,6 +760,7 @@ bool LibraryNameIs(const char *full_name, const char *base_name) {
#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;
@@ -896,8 +897,29 @@ void GetExtraActivationFlags(char *buf, uptr size) {
#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
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index 4e09081e7f54..df42c3604ae9 100644
--- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -58,8 +58,10 @@ 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);
}
@@ -67,8 +69,10 @@ 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);
}
@@ -120,6 +124,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
*stack_bottom = (uptr)stackaddr;
}
+#if !SANITIZER_GO
bool SetEnv(const char *name, const char *value) {
void *f = dlsym(RTLD_NEXT, "setenv");
if (f == 0)
@@ -130,6 +135,7 @@ bool SetEnv(const char *name, const char *value) {
internal_memcpy(&setenv_f, &f, sizeof(f));
return setenv_f(name, value, 1) == 0;
}
+#endif
bool SanitizerSetThreadName(const char *name) {
#ifdef PR_SET_NAME
@@ -163,7 +169,7 @@ static uptr g_tls_size;
#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");
@@ -208,6 +214,8 @@ uptr ThreadDescriptorSize() {
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);
}
@@ -259,6 +267,7 @@ uptr ThreadSelf() {
}
#endif // SANITIZER_FREEBSD
+#if !SANITIZER_GO
static void GetTls(uptr *addr, uptr *size) {
#if SANITIZER_LINUX
# if defined(__x86_64__) || defined(__i386__)
@@ -287,6 +296,7 @@ static void GetTls(uptr *addr, uptr *size) {
# error "Unknown OS"
#endif
}
+#endif
uptr GetTlsSize() {
#if SANITIZER_FREEBSD
@@ -300,6 +310,10 @@ uptr GetTlsSize() {
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;
@@ -316,6 +330,7 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
*tls_addr = *stk_addr + *stk_size;
}
}
+#endif
}
void AdjustStackSize(void *attr_) {
@@ -420,6 +435,45 @@ void PrepareForSandboxing(__sanitizer_sandbox_arguments *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
diff --git a/lib/sanitizer_common/sanitizer_list.h b/lib/sanitizer_common/sanitizer_list.h
index a47bc7d45e3e..6dd9c8f7bca1 100644
--- a/lib/sanitizer_common/sanitizer_list.h
+++ b/lib/sanitizer_common/sanitizer_list.h
@@ -115,21 +115,25 @@ struct IntrusiveList {
}
}
- class Iterator {
+ template<class ListTy, class ItemTy>
+ class IteratorBase {
public:
- explicit Iterator(IntrusiveList<Item> *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<Item> *list_;
- Item *current_;
+ ListTy *list_;
+ ItemTy *current_;
};
+ typedef IteratorBase<IntrusiveList<Item>, Item> Iterator;
+ typedef IteratorBase<const IntrusiveList<Item>, const Item> ConstIterator;
+
// private, don't use directly.
uptr size_;
Item *first_;
diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc
index 98c5b94112cf..39a5c7e8d24f 100644
--- a/lib/sanitizer_common/sanitizer_mac.cc
+++ b/lib/sanitizer_common/sanitizer_mac.cc
@@ -109,6 +109,10 @@ 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();
}
@@ -213,10 +217,6 @@ 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));
}
@@ -298,7 +298,11 @@ MacosVersion GetMacosVersionInternal() {
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;
@@ -321,6 +325,9 @@ 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
diff --git a/lib/sanitizer_common/sanitizer_mac.h b/lib/sanitizer_common/sanitizer_mac.h
index 3ed0ed3b00c2..9eed905187ec 100644
--- a/lib/sanitizer_common/sanitizer_mac.h
+++ b/lib/sanitizer_common/sanitizer_mac.h
@@ -27,6 +27,7 @@ enum MacosVersion {
MACOS_VERSION_MOUNTAIN_LION,
MACOS_VERSION_MAVERICKS,
MACOS_VERSION_YOSEMITE,
+ MACOS_VERSION_UNKNOWN_NEWER
};
MacosVersion GetMacosVersion();
diff --git a/lib/sanitizer_common/sanitizer_mutex.h b/lib/sanitizer_common/sanitizer_mutex.h
index c7589f76ed3b..d06fc45ff931 100644
--- a/lib/sanitizer_common/sanitizer_mutex.h
+++ b/lib/sanitizer_common/sanitizer_mutex.h
@@ -73,7 +73,13 @@ class SpinMutex : public StaticSpinMutex {
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();
diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h
index 7ca88fa972f3..54a1cfd8582b 100644
--- a/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -57,7 +57,7 @@
#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
@@ -70,7 +70,7 @@
#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
@@ -85,6 +85,7 @@
#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
@@ -93,12 +94,13 @@
#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
@@ -109,10 +111,10 @@
#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
@@ -133,12 +135,15 @@
#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
@@ -147,7 +152,8 @@
#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
@@ -158,21 +164,22 @@
#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
@@ -193,7 +200,7 @@
#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
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
index 6ffc1433cddd..4eabcd7a3d22 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
@@ -1061,7 +1061,13 @@ 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);
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
index 80a3ddb36670..d375e01a20e4 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
@@ -18,6 +18,15 @@
#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;
@@ -169,7 +178,7 @@ namespace __sanitizer {
unsigned __seq;
u64 __unused1;
u64 __unused2;
-#elif defined(__mips__)
+#elif defined(__mips__) || defined(__aarch64__)
unsigned int mode;
unsigned short __seq;
unsigned short __pad1;
diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc
index 4205c2b116ba..6cb51efce2d1 100644
--- a/lib/sanitizer_common/sanitizer_posix.cc
+++ b/lib/sanitizer_common/sanitizer_posix.cc
@@ -78,16 +78,15 @@ static uptr GetKernelAreaSize() {
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
@@ -238,7 +237,8 @@ bool MemoryRangeIsAvailable(uptr range_start, uptr range_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;
diff --git a/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
index ed1e3729a00e..11828e6cdf51 100644
--- a/lib/sanitizer_common/sanitizer_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
@@ -44,6 +44,18 @@ 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));
diff --git a/lib/sanitizer_common/sanitizer_quarantine.h b/lib/sanitizer_common/sanitizer_quarantine.h
index db4eb74505f9..404d3753f7e9 100644
--- a/lib/sanitizer_common/sanitizer_quarantine.h
+++ b/lib/sanitizer_common/sanitizer_quarantine.h
@@ -49,11 +49,14 @@ class Quarantine {
}
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_)
@@ -65,15 +68,15 @@ class Quarantine {
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_;
@@ -83,9 +86,10 @@ class Quarantine {
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);
}
@@ -130,6 +134,7 @@ class QuarantineCache {
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);
@@ -168,6 +173,7 @@ class QuarantineCache {
NOINLINE QuarantineBatch* AllocBatch(Callback cb) {
QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b));
+ CHECK(b);
b->count = 0;
b->size = 0;
list_.push_back(b);
diff --git a/lib/sanitizer_common/sanitizer_stackdepot.cc b/lib/sanitizer_common/sanitizer_stackdepot.cc
index f10f1f973fd0..59b53f4dcd84 100644
--- a/lib/sanitizer_common/sanitizer_stackdepot.cc
+++ b/lib/sanitizer_common/sanitizer_stackdepot.cc
@@ -22,7 +22,8 @@ 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;
@@ -37,7 +38,8 @@ struct StackDepotNode {
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;
@@ -72,10 +74,11 @@ struct StackDepotNode {
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); }
diff --git a/lib/sanitizer_common/sanitizer_stacktrace.cc b/lib/sanitizer_common/sanitizer_stacktrace.cc
index cf061fb8c73f..2deadb6e3560 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace.cc
@@ -17,21 +17,6 @@
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;
@@ -83,7 +68,7 @@ static inline uhwptr *GetCanonicFrame(uptr bp,
}
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;
@@ -120,7 +105,7 @@ void BufferedStackTrace::PopStackFrames(uptr 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;
diff --git a/lib/sanitizer_common/sanitizer_stacktrace.h b/lib/sanitizer_common/sanitizer_stacktrace.h
index e755c052cb77..6c3a1511f337 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace.h
+++ b/lib/sanitizer_common/sanitizer_stacktrace.h
@@ -17,7 +17,7 @@
namespace __sanitizer {
-static const uptr kStackTraceMax = 256;
+static const u32 kStackTraceMax = 256;
#if SANITIZER_LINUX && (defined(__aarch64__) || defined(__powerpc__) || \
defined(__powerpc64__) || defined(__sparc__) || \
@@ -40,10 +40,18 @@ static const uptr kStackTraceMax = 256;
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;
@@ -57,12 +65,29 @@ struct StackTrace {
}
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];
@@ -71,15 +96,15 @@ struct BufferedStackTrace : public StackTrace {
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);
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
index 0d90980e6a68..0f98c7d5af4c 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
@@ -44,7 +44,7 @@ void StackTrace::Print() const {
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;
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
index bb8ba6b81674..9317a78eef13 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
@@ -173,9 +173,9 @@ SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr,
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;
}
@@ -192,7 +192,7 @@ SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr,
return nullptr;
}
-bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) {
+bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
return false;
}
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
index a335cb23788c..1ff005042a11 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
+++ b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
@@ -35,7 +35,7 @@ class LibbacktraceSymbolizer {
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);
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
index d46c249e6cab..69ac18e8426f 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
@@ -596,7 +596,7 @@ class POSIXSymbolizer : public Symbolizer {
// 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);
diff --git a/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc
index a98e61771c02..7ab2efbd75bd 100644
--- a/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc
@@ -96,7 +96,7 @@ uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
struct UnwindTraceArg {
BufferedStackTrace *stack;
- uptr max_depth;
+ u32 max_depth;
};
_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
@@ -108,7 +108,7 @@ _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
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)};
@@ -128,7 +128,7 @@ void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
}
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);
diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc
index 3e9014199651..edd4bd11b369 100644
--- a/lib/sanitizer_common/sanitizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_win.cc
@@ -20,6 +20,7 @@
#include <windows.h>
#include <dbghelp.h>
#include <io.h>
+#include <psapi.h>
#include <stdlib.h>
#include "sanitizer_common.h"
@@ -122,18 +123,34 @@ void *MmapNoReserveOrDie(uptr size, const char *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) {
@@ -187,8 +204,77 @@ 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() {
@@ -238,8 +324,9 @@ u64 NanoTime() {
}
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,
@@ -379,6 +466,9 @@ 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;
@@ -448,7 +538,7 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
}
#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.
@@ -464,7 +554,7 @@ void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
}
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));
diff --git a/lib/sanitizer_common/scripts/check_lint.sh b/lib/sanitizer_common/scripts/check_lint.sh
index 267273d97794..7ed05d73563a 100755
--- a/lib/sanitizer_common/scripts/check_lint.sh
+++ b/lib/sanitizer_common/scripts/check_lint.sh
@@ -32,7 +32,14 @@ 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
diff --git a/lib/sanitizer_common/scripts/sancov.py b/lib/sanitizer_common/scripts/sancov.py
index 476953015280..566116eb2334 100755
--- a/lib/sanitizer_common/scripts/sancov.py
+++ b/lib/sanitizer_common/scripts/sancov.py
@@ -118,7 +118,7 @@ def UnpackOneRawFile(path, map_path):
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:
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt
index bb7a399b0ec6..75008db3b6c9 100644
--- a/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/lib/sanitizer_common/tests/CMakeLists.txt
@@ -2,6 +2,9 @@ include(CompilerRTCompile)
clang_compiler_add_cxx_check()
+# FIXME: use SANITIZER_COMMON_SUPPORTED_ARCH here
+filter_available_targets(SANITIZER_UNITTEST_SUPPORTED_ARCH x86_64 i386 mips64 mips64el)
+
set(SANITIZER_UNITTESTS
sanitizer_allocator_test.cc
sanitizer_atomic_test.cc
@@ -157,24 +160,18 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID)
$<TARGET_OBJECTS:RTSanitizerCommon.osx>)
else()
if(CAN_TARGET_x86_64)
- add_sanitizer_common_lib("RTSanitizerCommon.test.x86_64"
- $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
- $<TARGET_OBJECTS:RTSanitizerCommonLibc.x86_64>)
add_sanitizer_common_lib("RTSanitizerCommon.test.nolibc.x86_64"
$<TARGET_OBJECTS:RTSanitizerCommon.x86_64>)
endif()
- if(CAN_TARGET_i386)
- add_sanitizer_common_lib("RTSanitizerCommon.test.i386"
- $<TARGET_OBJECTS:RTSanitizerCommon.i386>
- $<TARGET_OBJECTS:RTSanitizerCommonLibc.i386>)
- endif()
- endif()
- if(CAN_TARGET_x86_64)
- add_sanitizer_tests_for_arch(x86_64)
- endif()
- if(CAN_TARGET_i386)
- add_sanitizer_tests_for_arch(i386)
+ foreach(arch ${SANITIZER_UNITTEST_SUPPORTED_ARCH})
+ add_sanitizer_common_lib("RTSanitizerCommon.test.${arch}"
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>)
+ endforeach()
endif()
+ foreach(arch ${SANITIZER_UNITTEST_SUPPORTED_ARCH})
+ add_sanitizer_tests_for_arch(${arch})
+ endforeach()
endif()
if(ANDROID)
diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
index f61d58dea7d9..be8fc91aa861 100644
--- a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
@@ -14,7 +14,6 @@
#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"
@@ -27,9 +26,9 @@
#include <set>
// 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;
@@ -39,6 +38,8 @@ typedef SizeClassAllocator64<
typedef SizeClassAllocator64<
kAllocatorSpace, kAllocatorSize, 16, CompactSizeClassMap> Allocator64Compact;
+#elif defined(__mips64)
+static const u64 kAddressSpaceSize = 1ULL << 40;
#else
static const u64 kAddressSpaceSize = 1ULL << 32;
#endif
@@ -140,7 +141,7 @@ void TestSizeClassAllocator() {
delete a;
}
-#if SANITIZER_WORDSIZE == 64
+#if SANITIZER_CAN_USE_ALLOCATOR64
TEST(SanitizerCommon, SizeClassAllocator64) {
TestSizeClassAllocator<Allocator64>();
}
@@ -184,7 +185,7 @@ void SizeClassAllocatorMetadataStress() {
delete a;
}
-#if SANITIZER_WORDSIZE == 64
+#if SANITIZER_CAN_USE_ALLOCATOR64
TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) {
SizeClassAllocatorMetadataStress<Allocator64>();
}
@@ -192,7 +193,7 @@ TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) {
TEST(SanitizerCommon, SizeClassAllocator64CompactMetadataStress) {
SizeClassAllocatorMetadataStress<Allocator64Compact>();
}
-#endif // SANITIZER_WORDSIZE == 64
+#endif // SANITIZER_CAN_USE_ALLOCATOR64
TEST(SanitizerCommon, SizeClassAllocator32CompactMetadataStress) {
SizeClassAllocatorMetadataStress<Allocator32Compact>();
}
@@ -221,7 +222,7 @@ void SizeClassAllocatorGetBlockBeginStress() {
delete a;
}
-#if SANITIZER_WORDSIZE == 64
+#if SANITIZER_CAN_USE_ALLOCATOR64
TEST(SanitizerCommon, SizeClassAllocator64GetBlockBegin) {
SizeClassAllocatorGetBlockBeginStress<Allocator64>();
}
@@ -231,7 +232,7 @@ TEST(SanitizerCommon, SizeClassAllocator64CompactGetBlockBegin) {
TEST(SanitizerCommon, SizeClassAllocator32CompactGetBlockBegin) {
SizeClassAllocatorGetBlockBeginStress<Allocator32Compact>();
}
-#endif // SANITIZER_WORDSIZE == 64
+#endif // SANITIZER_CAN_USE_ALLOCATOR64
struct TestMapUnmapCallback {
static int map_count, unmap_count;
@@ -241,7 +242,7 @@ struct TestMapUnmapCallback {
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;
@@ -297,7 +298,7 @@ TEST(SanitizerCommon, LargeMmapAllocatorMapUnmapCallback) {
TestMapUnmapCallback::map_count = 0;
TestMapUnmapCallback::unmap_count = 0;
LargeMmapAllocator<TestMapUnmapCallback> a;
- a.Init();
+ a.Init(/* may_return_null */ false);
AllocatorStats stats;
stats.Init();
void *x = a.Allocate(&stats, 1 << 20, 1);
@@ -322,7 +323,7 @@ void FailInAssertionOnOOM() {
a.TestOnlyUnmap();
}
-#if SANITIZER_WORDSIZE == 64
+#if SANITIZER_CAN_USE_ALLOCATOR64
TEST(SanitizerCommon, SizeClassAllocator64Overflow) {
EXPECT_DEATH(FailInAssertionOnOOM<Allocator64>(), "Out of memory");
}
@@ -331,7 +332,7 @@ TEST(SanitizerCommon, SizeClassAllocator64Overflow) {
#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();
@@ -413,25 +414,22 @@ void TestCombinedAllocator() {
CombinedAllocator<PrimaryAllocator, AllocatorCache, SecondaryAllocator>
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;
@@ -465,7 +463,7 @@ void TestCombinedAllocator() {
a->TestOnlyUnmap();
}
-#if SANITIZER_WORDSIZE == 64
+#if SANITIZER_CAN_USE_ALLOCATOR64
TEST(SanitizerCommon, CombinedAllocator64) {
TestCombinedAllocator<Allocator64,
LargeMmapAllocator<>,
@@ -521,7 +519,7 @@ void TestSizeClassAllocatorLocalCache() {
delete a;
}
-#if SANITIZER_WORDSIZE == 64
+#if SANITIZER_CAN_USE_ALLOCATOR64
TEST(SanitizerCommon, SizeClassAllocator64LocalCache) {
TestSizeClassAllocatorLocalCache<
SizeClassAllocatorLocalCache<Allocator64> >();
@@ -538,7 +536,7 @@ TEST(SanitizerCommon, SizeClassAllocator32CompactLocalCache) {
SizeClassAllocatorLocalCache<Allocator32Compact> >();
}
-#if SANITIZER_WORDSIZE == 64
+#if SANITIZER_CAN_USE_ALLOCATOR64
typedef SizeClassAllocatorLocalCache<Allocator64> AllocatorCache;
static AllocatorCache static_allocator_cache;
@@ -694,7 +692,7 @@ void TestSizeClassAllocatorIteration() {
delete a;
}
-#if SANITIZER_WORDSIZE == 64
+#if SANITIZER_CAN_USE_ALLOCATOR64
TEST(SanitizerCommon, SizeClassAllocator64Iteration) {
TestSizeClassAllocatorIteration<Allocator64>();
}
@@ -706,7 +704,7 @@ TEST(SanitizerCommon, SizeClassAllocator32Iteration) {
TEST(SanitizerCommon, LargeMmapAllocatorIteration) {
LargeMmapAllocator<> a;
- a.Init();
+ a.Init(/* may_return_null */ false);
AllocatorStats stats;
stats.Init();
@@ -733,7 +731,7 @@ TEST(SanitizerCommon, LargeMmapAllocatorIteration) {
TEST(SanitizerCommon, LargeMmapAllocatorBlockBegin) {
LargeMmapAllocator<> a;
- a.Init();
+ a.Init(/* may_return_null */ false);
AllocatorStats stats;
stats.Init();
@@ -769,7 +767,7 @@ TEST(SanitizerCommon, LargeMmapAllocatorBlockBegin) {
}
-#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...
@@ -859,4 +857,4 @@ TEST(SanitizerCommon, ThreadedTwoLevelByteMap) {
EXPECT_EQ((uptr)TestMapUnmapCallback::unmap_count, m.size1());
}
-#endif // #if TSAN_DEBUG==0
+#endif // #if !SANITIZER_DEBUG
diff --git a/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc b/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc
index 8c8363353507..7835eef76d06 100644
--- a/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc
@@ -268,7 +268,7 @@ void RunMultipleEpochsTest() {
}
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.
diff --git a/lib/sanitizer_common/tests/sanitizer_flags_test.cc b/lib/sanitizer_common/tests/sanitizer_flags_test.cc
index 1055f5d24d6b..3e5d8381ed3a 100644
--- a/lib/sanitizer_common/tests/sanitizer_flags_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_flags_test.cc
@@ -12,7 +12,9 @@
//===----------------------------------------------------------------------===//
#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 <string.h>
@@ -20,58 +22,79 @@
namespace __sanitizer {
static const char kFlagName[] = "flag_name";
+static const char kFlagDesc[] = "flag description";
template <typename T>
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));
}
@@ -81,6 +104,39 @@ TEST(SanitizerCommon, MultipleFlags) {
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
diff --git a/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/lib/sanitizer_common/tests/sanitizer_libc_test.cc
index 660710d5bb7e..8712d2c1b2a5 100644
--- a/lib/sanitizer_common/tests/sanitizer_libc_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_libc_test.cc
@@ -55,6 +55,19 @@ struct stat_and_more {
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) {
@@ -63,28 +76,16 @@ TEST(SanitizerCommon, FileOps) {
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);
@@ -92,8 +93,8 @@ TEST(SanitizerCommon, FileOps) {
#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);
@@ -115,6 +116,7 @@ TEST(SanitizerCommon, FileOps) {
EXPECT_EQ(len2, internal_read(fd, buf, len2));
EXPECT_EQ(0, internal_memcmp(buf, str2, len2));
internal_close(fd);
+ internal_unlink(tmpfile);
}
#endif
@@ -125,3 +127,35 @@ TEST(SanitizerCommon, InternalStrFunctions) {
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
diff --git a/lib/sanitizer_common/tests/sanitizer_printf_test.cc b/lib/sanitizer_common/tests/sanitizer_printf_test.cc
index d0b46ac94ff2..5e39e0a591d6 100644
--- a/lib/sanitizer_common/tests/sanitizer_printf_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_printf_test.cc
@@ -28,14 +28,11 @@ TEST(Printf, Basic) {
(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) {
diff --git a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc
index 495b726dcc45..abe4ef43093f 100644
--- a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc
@@ -48,6 +48,7 @@ TEST(MemoryMappingLayout, DumpListOfModules) {
if (strstr(modules[i].full_name(), binary_name) != 0)
found = true;
}
+ modules[i].clear();
}
EXPECT_TRUE(found);
free(modules);
diff --git a/lib/sanitizer_common/tests/sanitizer_test_utils.h b/lib/sanitizer_common/tests/sanitizer_test_utils.h
index 64db37f341d3..9c162a66f547 100644
--- a/lib/sanitizer_common/tests/sanitizer_test_utils.h
+++ b/lib/sanitizer_common/tests/sanitizer_test_utils.h
@@ -118,4 +118,10 @@ static inline uint32_t my_rand() {
# 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