diff options
Diffstat (limited to 'libsanitizer/sanitizer_common')
21 files changed, 982 insertions, 293 deletions
diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.cc b/libsanitizer/sanitizer_common/sanitizer_allocator.cc index d09112705f5..a54de9d6f9a 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.cc +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.cc @@ -73,4 +73,10 @@ void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) { low_level_alloc_callback = callback; } +bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) { + if (!size) return false; + uptr max = (uptr)-1L; + return (max / size) < n; +} + } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.h b/libsanitizer/sanitizer_common/sanitizer_allocator.h index 1c9852e5003..889281216fc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.h @@ -23,9 +23,9 @@ namespace __sanitizer { // SizeClassMap maps allocation sizes into size classes and back. // Class 0 corresponds to size 0. -// Classes 1 - 16 correspond to sizes 8 - 128 (size = class_id * 8). -// Next 8 classes: 128 + i * 16 (i = 1 to 8). +// Classes 1 - 16 correspond to sizes 16 to 256 (size = class_id * 16). // Next 8 classes: 256 + i * 32 (i = 1 to 8). +// Next 8 classes: 512 + i * 64 (i = 1 to 8). // ... // Next 8 classes: 2^k + i * 2^(k-3) (i = 1 to 8). // Last class corresponds to kMaxSize = 1 << kMaxSizeLog. @@ -40,33 +40,48 @@ namespace __sanitizer { // - (1 << kMaxBytesCachedLog) is the maximal number of bytes per size class. // // Part of output of SizeClassMap::Print(): -// c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0 -// c01 => s: 8 diff: +8 00% l 3 cached: 256 2048; id 1 -// c02 => s: 16 diff: +8 100% l 4 cached: 256 4096; id 2 -// ... -// c07 => s: 56 diff: +8 16% l 5 cached: 256 14336; id 7 +// c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0 +// c01 => s: 16 diff: +16 00% l 4 cached: 256 4096; id 1 +// c02 => s: 32 diff: +16 100% l 5 cached: 256 8192; id 2 +// c03 => s: 48 diff: +16 50% l 5 cached: 256 12288; id 3 +// c04 => s: 64 diff: +16 33% l 6 cached: 256 16384; id 4 +// c05 => s: 80 diff: +16 25% l 6 cached: 256 20480; id 5 +// c06 => s: 96 diff: +16 20% l 6 cached: 256 24576; id 6 +// c07 => s: 112 diff: +16 16% l 6 cached: 256 28672; id 7 // -// c08 => s: 64 diff: +8 14% l 6 cached: 256 16384; id 8 -// ... -// c15 => s: 120 diff: +8 07% l 6 cached: 256 30720; id 15 +// c08 => s: 128 diff: +16 14% l 7 cached: 256 32768; id 8 +// c09 => s: 144 diff: +16 12% l 7 cached: 256 36864; id 9 +// c10 => s: 160 diff: +16 11% l 7 cached: 256 40960; id 10 +// c11 => s: 176 diff: +16 10% l 7 cached: 256 45056; id 11 +// c12 => s: 192 diff: +16 09% l 7 cached: 256 49152; id 12 +// c13 => s: 208 diff: +16 08% l 7 cached: 256 53248; id 13 +// c14 => s: 224 diff: +16 07% l 7 cached: 256 57344; id 14 +// c15 => s: 240 diff: +16 07% l 7 cached: 256 61440; id 15 // -// c16 => s: 128 diff: +8 06% l 7 cached: 256 32768; id 16 -// c17 => s: 144 diff: +16 12% l 7 cached: 227 32688; id 17 -// ... -// c23 => s: 240 diff: +16 07% l 7 cached: 136 32640; id 23 +// c16 => s: 256 diff: +16 06% l 8 cached: 256 65536; id 16 +// c17 => s: 288 diff: +32 12% l 8 cached: 227 65376; id 17 +// c18 => s: 320 diff: +32 11% l 8 cached: 204 65280; id 18 +// c19 => s: 352 diff: +32 10% l 8 cached: 186 65472; id 19 +// c20 => s: 384 diff: +32 09% l 8 cached: 170 65280; id 20 +// c21 => s: 416 diff: +32 08% l 8 cached: 157 65312; id 21 +// c22 => s: 448 diff: +32 07% l 8 cached: 146 65408; id 22 +// c23 => s: 480 diff: +32 07% l 8 cached: 136 65280; id 23 // -// c24 => s: 256 diff: +16 06% l 8 cached: 128 32768; id 24 -// c25 => s: 288 diff: +32 12% l 8 cached: 113 32544; id 25 -// ... -// c31 => s: 480 diff: +32 07% l 8 cached: 68 32640; id 31 +// c24 => s: 512 diff: +32 06% l 9 cached: 128 65536; id 24 +// c25 => s: 576 diff: +64 12% l 9 cached: 113 65088; id 25 +// c26 => s: 640 diff: +64 11% l 9 cached: 102 65280; id 26 +// c27 => s: 704 diff: +64 10% l 9 cached: 93 65472; id 27 +// c28 => s: 768 diff: +64 09% l 9 cached: 85 65280; id 28 +// c29 => s: 832 diff: +64 08% l 9 cached: 78 64896; id 29 +// c30 => s: 896 diff: +64 07% l 9 cached: 73 65408; id 30 +// c31 => s: 960 diff: +64 07% l 9 cached: 68 65280; id 31 // -// c32 => s: 512 diff: +32 06% l 9 cached: 64 32768; id 32 - +// c32 => s: 1024 diff: +64 06% l 10 cached: 64 65536; id 32 template <uptr kMaxSizeLog, uptr kMaxNumCachedT, uptr kMaxBytesCachedLog, uptr kMinBatchClassT> class SizeClassMap { - static const uptr kMinSizeLog = 3; + static const uptr kMinSizeLog = 4; static const uptr kMidSizeLog = kMinSizeLog + 4; static const uptr kMinSize = 1 << kMinSizeLog; static const uptr kMidSize = 1 << kMidSizeLog; @@ -104,7 +119,7 @@ class SizeClassMap { if (size <= kMidSize) return (size + kMinSize - 1) >> kMinSizeLog; if (size > kMaxSize) return 0; - uptr l = SANITIZER_WORDSIZE - 1 - __builtin_clzl(size); + uptr l = MostSignificantSetBitIndex(size); uptr hbits = (size >> (l - S)) & M; uptr lbits = size & ((1 << (l - S)) - 1); uptr l1 = l - kMidSizeLog; @@ -114,7 +129,7 @@ class SizeClassMap { static uptr MaxCached(uptr class_id) { if (class_id == 0) return 0; uptr n = (1UL << kMaxBytesCachedLog) / Size(class_id); - return Max(1UL, Min(kMaxNumCached, n)); + return Max<uptr>(1, Min(kMaxNumCached, n)); } static void Print() { @@ -126,7 +141,7 @@ class SizeClassMap { Printf("\n"); uptr d = s - prev_s; uptr p = prev_s ? (d * 100 / prev_s) : 0; - uptr l = SANITIZER_WORDSIZE - 1 - __builtin_clzl(s); + uptr l = MostSignificantSetBitIndex(s); uptr cached = MaxCached(i) * s; Printf("c%02zd => s: %zd diff: +%zd %02zd%% l %zd " "cached: %zd %zd; id %zd\n", @@ -172,12 +187,92 @@ class SizeClassMap { } }; -typedef SizeClassMap<17, 256, 16, FIRST_32_SECOND_64(33, 36)> +typedef SizeClassMap<17, 256, 16, FIRST_32_SECOND_64(25, 28)> DefaultSizeClassMap; -typedef SizeClassMap<17, 64, 14, FIRST_32_SECOND_64(25, 28)> +typedef SizeClassMap<17, 64, 14, FIRST_32_SECOND_64(17, 20)> CompactSizeClassMap; template<class SizeClassAllocator> struct SizeClassAllocatorLocalCache; +// Memory allocator statistics +enum AllocatorStat { + AllocatorStatMalloced, + AllocatorStatFreed, + AllocatorStatMmapped, + AllocatorStatUnmapped, + AllocatorStatCount +}; + +typedef u64 AllocatorStatCounters[AllocatorStatCount]; + +// Per-thread stats, live in per-thread cache. +class AllocatorStats { + public: + void Init() { + internal_memset(this, 0, sizeof(*this)); + } + + void Add(AllocatorStat i, u64 v) { + v += atomic_load(&stats_[i], memory_order_relaxed); + atomic_store(&stats_[i], v, memory_order_relaxed); + } + + void Set(AllocatorStat i, u64 v) { + atomic_store(&stats_[i], v, memory_order_relaxed); + } + + u64 Get(AllocatorStat i) const { + return atomic_load(&stats_[i], memory_order_relaxed); + } + + private: + friend class AllocatorGlobalStats; + AllocatorStats *next_; + AllocatorStats *prev_; + atomic_uint64_t stats_[AllocatorStatCount]; +}; + +// Global stats, used for aggregation and querying. +class AllocatorGlobalStats : public AllocatorStats { + public: + void Init() { + internal_memset(this, 0, sizeof(*this)); + next_ = this; + prev_ = this; + } + + void Register(AllocatorStats *s) { + SpinMutexLock l(&mu_); + s->next_ = next_; + s->prev_ = this; + next_->prev_ = s; + next_ = s; + } + + void Unregister(AllocatorStats *s) { + SpinMutexLock l(&mu_); + s->prev_->next_ = s->next_; + s->next_->prev_ = s->prev_; + for (int i = 0; i < AllocatorStatCount; i++) + Add(AllocatorStat(i), s->Get(AllocatorStat(i))); + } + + void Get(AllocatorStatCounters s) const { + internal_memset(s, 0, AllocatorStatCount * sizeof(u64)); + SpinMutexLock l(&mu_); + const AllocatorStats *stats = this; + for (;;) { + for (int i = 0; i < AllocatorStatCount; i++) + s[i] += stats->Get(AllocatorStat(i)); + stats = stats->next_; + if (stats == this) + break; + } + } + + private: + mutable SpinMutex mu_; +}; + // Allocators call these callbacks on mmap/munmap. struct NoOpMapUnmapCallback { void OnMap(uptr p, uptr size) const { } @@ -231,17 +326,18 @@ class SizeClassAllocator64 { alignment <= SizeClassMap::kMaxSize; } - Batch *NOINLINE AllocateBatch(AllocatorCache *c, uptr class_id) { + NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c, + uptr class_id) { CHECK_LT(class_id, kNumClasses); RegionInfo *region = GetRegionInfo(class_id); Batch *b = region->free_list.Pop(); if (b == 0) - b = PopulateFreeList(c, class_id, region); + b = PopulateFreeList(stat, c, class_id, region); region->n_allocated += b->count; return b; } - void NOINLINE DeallocateBatch(uptr class_id, Batch *b) { + NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) { RegionInfo *region = GetRegionInfo(class_id); region->free_list.Push(b); region->n_freed += b->count; @@ -320,6 +416,20 @@ class SizeClassAllocator64 { } } + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + for (uptr i = 0; i < kNumClasses; i++) { + GetRegionInfo(i)->mutex.Lock(); + } + } + + void ForceUnlock() { + for (int i = (int)kNumClasses - 1; i >= 0; i--) { + GetRegionInfo(i)->mutex.Unlock(); + } + } + typedef SizeClassMap SizeClassMapT; static const uptr kNumClasses = SizeClassMap::kNumClasses; static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded; @@ -334,7 +444,7 @@ class SizeClassAllocator64 { // or with one element if its size is greater. static const uptr kPopulateSize = 1 << 14; // Call mmap for user memory with at least this size. - static const uptr kUserMapSize = 1 << 15; + static const uptr kUserMapSize = 1 << 16; // Call mmap for metadata memory with at least this size. static const uptr kMetaMapSize = 1 << 16; @@ -368,8 +478,8 @@ class SizeClassAllocator64 { return offset / (u32)size; } - Batch *NOINLINE PopulateFreeList(AllocatorCache *c, uptr class_id, - RegionInfo *region) { + NOINLINE Batch* PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, + uptr class_id, RegionInfo *region) { BlockingMutexLock l(®ion->mutex); Batch *b = region->free_list.Pop(); if (b) @@ -386,6 +496,7 @@ class SizeClassAllocator64 { map_size += kUserMapSize; CHECK_GE(region->mapped_user + map_size, end_idx); MapWithCallback(region_beg + region->mapped_user, map_size); + stat->Add(AllocatorStatMmapped, map_size); region->mapped_user += map_size; } uptr total_count = (region->mapped_user - beg_idx - size) @@ -467,6 +578,7 @@ class SizeClassAllocator32 { MapUnmapCallback().OnMap((uptr)res, size); return res; } + void UnmapWithCallback(uptr beg, uptr size) { MapUnmapCallback().OnUnmap(beg, size); UnmapOrDie(reinterpret_cast<void *>(beg), size); @@ -488,19 +600,20 @@ class SizeClassAllocator32 { return reinterpret_cast<void*>(meta); } - Batch *NOINLINE AllocateBatch(AllocatorCache *c, uptr class_id) { + NOINLINE Batch* AllocateBatch(AllocatorStats *stat, AllocatorCache *c, + uptr class_id) { CHECK_LT(class_id, kNumClasses); SizeClassInfo *sci = GetSizeClassInfo(class_id); SpinMutexLock l(&sci->mutex); if (sci->free_list.empty()) - PopulateFreeList(c, sci, class_id); + PopulateFreeList(stat, c, sci, class_id); CHECK(!sci->free_list.empty()); Batch *b = sci->free_list.front(); sci->free_list.pop_front(); return b; } - void NOINLINE DeallocateBatch(uptr class_id, Batch *b) { + NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, Batch *b) { CHECK_LT(class_id, kNumClasses); SizeClassInfo *sci = GetSizeClassInfo(class_id); SpinMutexLock l(&sci->mutex); @@ -549,6 +662,20 @@ class SizeClassAllocator32 { UnmapWithCallback(reinterpret_cast<uptr>(state_), sizeof(State)); } + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + for (uptr i = 0; i < kNumClasses; i++) { + GetSizeClassInfo(i)->mutex.Lock(); + } + } + + void ForceUnlock() { + for (int i = kNumClasses - 1; i >= 0; i--) { + GetSizeClassInfo(i)->mutex.Unlock(); + } + } + void PrintStats() { } @@ -577,11 +704,12 @@ class SizeClassAllocator32 { return mem & ~(kRegionSize - 1); } - uptr AllocateRegion(uptr class_id) { + uptr AllocateRegion(AllocatorStats *stat, uptr class_id) { CHECK_LT(class_id, kNumClasses); uptr res = reinterpret_cast<uptr>(MmapAlignedOrDie(kRegionSize, kRegionSize, "SizeClassAllocator32")); MapUnmapCallback().OnMap(res, kRegionSize); + stat->Add(AllocatorStatMmapped, kRegionSize); CHECK_EQ(0U, (res & (kRegionSize - 1))); CHECK_EQ(0U, state_->possible_regions[ComputeRegionId(res)]); state_->possible_regions[ComputeRegionId(res)] = class_id; @@ -593,9 +721,10 @@ class SizeClassAllocator32 { return &state_->size_class_info_array[class_id]; } - void PopulateFreeList(AllocatorCache *c, SizeClassInfo *sci, uptr class_id) { + void PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, + SizeClassInfo *sci, uptr class_id) { uptr size = SizeClassMap::Size(class_id); - uptr reg = AllocateRegion(class_id); + uptr reg = AllocateRegion(stat, class_id); uptr n_chunks = kRegionSize / (size + kMetadataSize); uptr max_count = SizeClassMap::MaxCached(class_id); Batch *b = 0; @@ -632,14 +761,22 @@ struct SizeClassAllocatorLocalCache { typedef SizeClassAllocator Allocator; static const uptr kNumClasses = SizeClassAllocator::kNumClasses; - // Don't need to call Init if the object is a global (i.e. zero-initialized). - void Init() { - internal_memset(this, 0, sizeof(*this)); + void Init(AllocatorGlobalStats *s) { + stats_.Init(); + if (s) + s->Register(&stats_); + } + + void Destroy(SizeClassAllocator *allocator, AllocatorGlobalStats *s) { + Drain(allocator); + if (s) + s->Unregister(&stats_); } void *Allocate(SizeClassAllocator *allocator, uptr class_id) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); + stats_.Add(AllocatorStatMalloced, SizeClassMap::Size(class_id)); PerClass *c = &per_class_[class_id]; if (UNLIKELY(c->count == 0)) Refill(allocator, class_id); @@ -651,6 +788,7 @@ struct SizeClassAllocatorLocalCache { void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); + stats_.Add(AllocatorStatFreed, SizeClassMap::Size(class_id)); PerClass *c = &per_class_[class_id]; if (UNLIKELY(c->count == c->max_count)) Drain(allocator, class_id); @@ -674,6 +812,7 @@ struct SizeClassAllocatorLocalCache { void *batch[2 * SizeClassMap::kMaxNumCached]; }; PerClass per_class_[kNumClasses]; + AllocatorStats stats_; void InitCache() { if (per_class_[0].max_count) @@ -684,10 +823,11 @@ struct SizeClassAllocatorLocalCache { } } - void NOINLINE Refill(SizeClassAllocator *allocator, uptr class_id) { + NOINLINE void Refill(SizeClassAllocator *allocator, uptr class_id) { InitCache(); PerClass *c = &per_class_[class_id]; - Batch *b = allocator->AllocateBatch(this, class_id); + Batch *b = allocator->AllocateBatch(&stats_, this, class_id); + CHECK_GT(b->count, 0); for (uptr i = 0; i < b->count; i++) c->batch[i] = b->batch[i]; c->count = b->count; @@ -695,7 +835,7 @@ struct SizeClassAllocatorLocalCache { Deallocate(allocator, SizeClassMap::ClassID(sizeof(Batch)), b); } - void NOINLINE Drain(SizeClassAllocator *allocator, uptr class_id) { + NOINLINE void Drain(SizeClassAllocator *allocator, uptr class_id) { InitCache(); PerClass *c = &per_class_[class_id]; Batch *b; @@ -710,7 +850,7 @@ struct SizeClassAllocatorLocalCache { } b->count = cnt; c->count -= cnt; - allocator->DeallocateBatch(class_id, b); + allocator->DeallocateBatch(&stats_, class_id, b); } }; @@ -725,7 +865,7 @@ class LargeMmapAllocator { page_size_ = GetPageSizeCached(); } - void *Allocate(uptr size, uptr alignment) { + void *Allocate(AllocatorStats *stat, uptr size, uptr alignment) { CHECK(IsPowerOfTwo(alignment)); uptr map_size = RoundUpMapSize(size); if (alignment > page_size_) @@ -744,7 +884,7 @@ class LargeMmapAllocator { h->size = size; h->map_beg = map_beg; h->map_size = map_size; - uptr size_log = SANITIZER_WORDSIZE - __builtin_clzl(map_size) - 1; + uptr size_log = MostSignificantSetBitIndex(map_size); CHECK_LT(size_log, ARRAY_SIZE(stats.by_size_log)); { SpinMutexLock l(&mutex_); @@ -756,11 +896,13 @@ class LargeMmapAllocator { stats.currently_allocated += map_size; stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated); stats.by_size_log[size_log]++; + stat->Add(AllocatorStatMalloced, map_size); + stat->Add(AllocatorStatMmapped, map_size); } return reinterpret_cast<void*>(res); } - void Deallocate(void *p) { + void Deallocate(AllocatorStats *stat, void *p) { Header *h = GetHeader(p); { SpinMutexLock l(&mutex_); @@ -772,6 +914,8 @@ class LargeMmapAllocator { n_chunks_--; stats.n_frees++; stats.currently_allocated -= h->map_size; + stat->Add(AllocatorStatFreed, h->map_size); + stat->Add(AllocatorStatUnmapped, h->map_size); } MapUnmapCallback().OnUnmap(h->map_beg, h->map_size); UnmapOrDie(reinterpret_cast<void*>(h->map_beg), h->map_size); @@ -838,6 +982,16 @@ class LargeMmapAllocator { Printf("\n"); } + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + mutex_.Lock(); + } + + void ForceUnlock() { + mutex_.Unlock(); + } + private: static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18); struct Header { @@ -884,6 +1038,7 @@ class CombinedAllocator { void Init() { primary_.Init(); secondary_.Init(); + stats_.Init(); } void *Allocate(AllocatorCache *cache, uptr size, uptr alignment, @@ -899,7 +1054,7 @@ class CombinedAllocator { if (primary_.CanAllocate(size, alignment)) res = cache->Allocate(&primary_, primary_.ClassID(size)); else - res = secondary_.Allocate(size, alignment); + res = secondary_.Allocate(&stats_, size, alignment); if (alignment > 8) CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0); if (cleared && res) @@ -912,7 +1067,7 @@ class CombinedAllocator { if (primary_.PointerIsMine(p)) cache->Deallocate(&primary_, primary_.GetSizeClass(p), p); else - secondary_.Deallocate(p); + secondary_.Deallocate(&stats_, p); } void *Reallocate(AllocatorCache *cache, void *p, uptr new_size, @@ -967,20 +1122,48 @@ class CombinedAllocator { void TestOnlyUnmap() { primary_.TestOnlyUnmap(); } + void InitCache(AllocatorCache *cache) { + cache->Init(&stats_); + } + + void DestroyCache(AllocatorCache *cache) { + cache->Destroy(&primary_, &stats_); + } + void SwallowCache(AllocatorCache *cache) { cache->Drain(&primary_); } + void GetStats(AllocatorStatCounters s) const { + stats_.Get(s); + } + void PrintStats() { primary_.PrintStats(); secondary_.PrintStats(); } + // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone + // introspection API. + void ForceLock() { + primary_.ForceLock(); + secondary_.ForceLock(); + } + + void ForceUnlock() { + secondary_.ForceUnlock(); + primary_.ForceUnlock(); + } + private: PrimaryAllocator primary_; SecondaryAllocator secondary_; + AllocatorGlobalStats stats_; }; +// Returns true if calloc(size, n) should return 0 due to overflow in size*n. +bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n); + } // namespace __sanitizer #endif // SANITIZER_ALLOCATOR_H diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cc b/libsanitizer/sanitizer_common/sanitizer_common.cc index 9b70ee0eb54..4447c346eba 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common.cc @@ -14,6 +14,8 @@ namespace __sanitizer { +const char *SanitizerToolName = "SanitizerTool"; + uptr GetPageSizeCached() { static uptr PageSize; if (!PageSize) @@ -64,7 +66,7 @@ static void MaybeOpenReportFile() { InternalScopedBuffer<char> report_path_full(4096); internal_snprintf(report_path_full.data(), report_path_full.size(), "%s.%d", report_path_prefix, GetPid()); - fd_t fd = internal_open(report_path_full.data(), true); + fd_t fd = OpenFile(report_path_full.data(), true); if (fd == kInvalidFd) { report_fd = kStderrFd; log_to_file = false; @@ -103,7 +105,7 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, *buff_size = 0; // The files we usually open are not seekable, so try different buffer sizes. for (uptr size = kMinFileLen; size <= max_len; size *= 2) { - fd_t fd = internal_open(file_name, /*write*/ false); + fd_t fd = OpenFile(file_name, /*write*/ false); if (fd == kInvalidFd) return 0; UnmapOrDie(*buff, *buff_size); *buff = (char*)MmapOrDie(size, __FUNCTION__); @@ -188,6 +190,16 @@ void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) { return (void*)res; } +void ReportErrorSummary(const char *error_type, const char *file, + int line, const char *function) { + const int kMaxSize = 1024; // We don't want a summary too long. + InternalScopedBuffer<char> buff(kMaxSize); + internal_snprintf(buff.data(), kMaxSize, "%s: %s %s:%d %s", + SanitizerToolName, error_type, + file ? file : "??", line, function ? function : "??"); + __sanitizer_report_error_summary(buff.data()); +} + } // namespace __sanitizer using namespace __sanitizer; // NOLINT @@ -220,4 +232,8 @@ void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) { (void)reserved; PrepareForSandboxing(); } + +void __sanitizer_report_error_summary(const char *error_summary) { + Printf("SUMMARY: %s\n", error_summary); +} } // extern "C" diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h index 6b104884342..109966ba360 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.h +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -17,6 +17,7 @@ #include "sanitizer_internal_defs.h" namespace __sanitizer { +struct StackTrace; // Constants. const uptr kWordSize = SANITIZER_WORDSIZE / 8; @@ -28,6 +29,8 @@ const uptr kCacheLineSize = 128; const uptr kCacheLineSize = 64; #endif +extern const char *SanitizerToolName; // Can be changed by the tool. + uptr GetPageSize(); uptr GetPageSizeCached(); uptr GetMmapGranularity(); @@ -103,6 +106,7 @@ void Printf(const char *format, ...); void Report(const char *format, ...); void SetPrintfAndReportCallback(void (*callback)(const char *)); +fd_t OpenFile(const char *filename, bool write); // Opens the file 'file_name" and reads up to 'max_len' bytes. // The resulting buffer is mmaped and stored in '*buff'. // The size of the mmaped region is stored in '*buff_size', @@ -152,20 +156,79 @@ typedef void (*CheckFailedCallbackType)(const char *, int, const char *, u64, u64); void SetCheckFailedCallback(CheckFailedCallbackType callback); +// Construct a one-line string like +// SanitizerToolName: error_type file:line function +// and call __sanitizer_report_error_summary on it. +void ReportErrorSummary(const char *error_type, const char *file, + int line, const char *function); + // Math +#if defined(_WIN32) && !defined(__clang__) +extern "C" { +unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT +unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT +#if defined(_WIN64) +unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask); // NOLINT +unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); // NOLINT +#endif +} +#endif + +INLINE uptr MostSignificantSetBitIndex(uptr x) { + CHECK(x != 0); + unsigned long up; // NOLINT +#if !defined(_WIN32) || defined(__clang__) + up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x); +#elif defined(_WIN64) + _BitScanReverse64(&up, x); +#else + _BitScanReverse(&up, x); +#endif + return up; +} + INLINE bool IsPowerOfTwo(uptr x) { return (x & (x - 1)) == 0; } + +INLINE uptr RoundUpToPowerOfTwo(uptr size) { + CHECK(size); + if (IsPowerOfTwo(size)) return size; + + uptr up = MostSignificantSetBitIndex(size); + CHECK(size < (1ULL << (up + 1))); + CHECK(size > (1ULL << up)); + return 1UL << (up + 1); +} + INLINE uptr RoundUpTo(uptr size, uptr boundary) { CHECK(IsPowerOfTwo(boundary)); return (size + boundary - 1) & ~(boundary - 1); } + INLINE uptr RoundDownTo(uptr x, uptr boundary) { return x & ~(boundary - 1); } + INLINE bool IsAligned(uptr a, uptr alignment) { return (a & (alignment - 1)) == 0; } + +INLINE uptr Log2(uptr x) { + CHECK(IsPowerOfTwo(x)); +#if !defined(_WIN32) || defined(__clang__) + return __builtin_ctzl(x); +#elif defined(_WIN64) + unsigned long ret; // NOLINT + _BitScanForward64(&ret, x); + return ret; +#else + unsigned long ret; // NOLINT + _BitScanForward(&ret, x); + return ret; +#endif +} + // Don't use std::min, std::max or std::swap, to minimize dependency // on libstdc++. template<class T> T Min(T a, T b) { return a < b ? a : b; } diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc index 724a326ef4c..4ba7b8fee9f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc @@ -22,9 +22,13 @@ #include <stdarg.h> +#ifdef _WIN32 +#define va_copy(dst, src) ((dst) = (src)) +#endif // _WIN32 + #if SANITIZER_INTERCEPT_READ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { - void* ctx; + void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count); SSIZE_T res = REAL(read)(fd, ptr, count); if (res > 0) @@ -33,14 +37,14 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -# define INIT_READ INTERCEPT_FUNCTION(read) +#define INIT_READ INTERCEPT_FUNCTION(read) #else -# define INIT_READ +#define INIT_READ #endif #if SANITIZER_INTERCEPT_PREAD INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { - void* ctx; + void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset); SSIZE_T res = REAL(pread)(fd, ptr, count, offset); if (res > 0) @@ -49,14 +53,14 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -# define INIT_PREAD INTERCEPT_FUNCTION(pread) +#define INIT_PREAD INTERCEPT_FUNCTION(pread) #else -# define INIT_PREAD +#define INIT_PREAD #endif #if SANITIZER_INTERCEPT_PREAD64 INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { - void* ctx; + void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset); SSIZE_T res = REAL(pread64)(fd, ptr, count, offset); if (res > 0) @@ -65,14 +69,14 @@ INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -# define INIT_PREAD64 INTERCEPT_FUNCTION(pread64) +#define INIT_PREAD64 INTERCEPT_FUNCTION(pread64) #else -# define INIT_PREAD64 +#define INIT_PREAD64 #endif #if SANITIZER_INTERCEPT_WRITE INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { - void* ctx; + void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); @@ -81,142 +85,154 @@ INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -# define INIT_WRITE INTERCEPT_FUNCTION(write) +#define INIT_WRITE INTERCEPT_FUNCTION(write) #else -# define INIT_WRITE +#define INIT_WRITE #endif #if SANITIZER_INTERCEPT_PWRITE -INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count) { - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count); +INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); - SSIZE_T res = REAL(pwrite)(fd, ptr, count); + SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -# define INIT_PWRITE INTERCEPT_FUNCTION(pwrite) +#define INIT_PWRITE INTERCEPT_FUNCTION(pwrite) #else -# define INIT_PWRITE +#define INIT_PWRITE #endif #if SANITIZER_INTERCEPT_PWRITE64 -INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count) { - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count); +INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count, + OFF64_T offset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); - SSIZE_T res = REAL(pwrite64)(fd, ptr, count); + SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -# define INIT_PWRITE64 INTERCEPT_FUNCTION(pwrite64) +#define INIT_PWRITE64 INTERCEPT_FUNCTION(pwrite64) #else -# define INIT_PWRITE64 +#define INIT_PWRITE64 #endif #if SANITIZER_INTERCEPT_PRCTL -INTERCEPTOR(int, prctl, int option, - unsigned long arg2, unsigned long arg3, // NOLINT - unsigned long arg4, unsigned long arg5) { // NOLINT - void* ctx; +INTERCEPTOR(int, prctl, int option, unsigned long arg2, + unsigned long arg3, // NOLINT + unsigned long arg4, unsigned long arg5) { // NOLINT + void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5); static const int PR_SET_NAME = 15; int res = REAL(prctl(option, arg2, arg3, arg4, arg5)); if (option == PR_SET_NAME) { char buff[16]; - internal_strncpy(buff, (char*)arg2, 15); + internal_strncpy(buff, (char *)arg2, 15); buff[15] = 0; COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff); } return res; } -# define INIT_PRCTL INTERCEPT_FUNCTION(prctl) +#define INIT_PRCTL INTERCEPT_FUNCTION(prctl) #else -# define INIT_PRCTL -#endif // SANITIZER_INTERCEPT_PRCTL - +#define INIT_PRCTL +#endif // SANITIZER_INTERCEPT_PRCTL #if SANITIZER_INTERCEPT_SCANF #include "sanitizer_common_interceptors_scanf.inc" -INTERCEPTOR(int, vscanf, const char *format, va_list ap) { // NOLINT - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, vscanf, format, ap); - scanf_common(ctx, format, ap); - int res = REAL(vscanf)(format, ap); // NOLINT - return res; -} +#define VSCANF_INTERCEPTOR_IMPL(vname, allowGnuMalloc, ...) \ + { \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ + va_list aq; \ + va_copy(aq, ap); \ + int res = REAL(vname)(__VA_ARGS__); \ + if (res > 0) \ + scanf_common(ctx, res, allowGnuMalloc, format, aq); \ + va_end(aq); \ + return res; \ + } -INTERCEPTOR(int, vsscanf, const char *str, const char *format, // NOLINT - va_list ap) { - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, vsscanf, str, format, ap); - scanf_common(ctx, format, ap); - int res = REAL(vsscanf)(str, format, ap); // NOLINT - // FIXME: read of str - return res; -} +INTERCEPTOR(int, vscanf, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(vscanf, true, format, ap) -INTERCEPTOR(int, vfscanf, void *stream, const char *format, // NOLINT - va_list ap) { - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, vfscanf, stream, format, ap); - scanf_common(ctx, format, ap); - int res = REAL(vfscanf)(stream, format, ap); // NOLINT - return res; -} +INTERCEPTOR(int, vsscanf, const char *str, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(vsscanf, true, str, format, ap) -INTERCEPTOR(int, scanf, const char *format, ...) { // NOLINT - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, scanf, format); - va_list ap; - va_start(ap, format); - int res = vscanf(format, ap); // NOLINT - va_end(ap); - return res; -} +INTERCEPTOR(int, vfscanf, void *stream, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(vfscanf, true, stream, format, ap) -INTERCEPTOR(int, fscanf, void* stream, const char *format, ...) { // NOLINT - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, fscanf, stream, format); - va_list ap; - va_start(ap, format); - int res = vfscanf(stream, format, ap); // NOLINT - va_end(ap); - return res; -} +INTERCEPTOR(int, __isoc99_vscanf, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(__isoc99_vscanf, false, format, ap) -INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) { // NOLINT - void* ctx; - COMMON_INTERCEPTOR_ENTER(ctx, sscanf, str, format); // NOLINT - va_list ap; - va_start(ap, format); - int res = vsscanf(str, format, ap); // NOLINT - va_end(ap); - return res; -} +INTERCEPTOR(int, __isoc99_vsscanf, const char *str, const char *format, + va_list ap) +VSCANF_INTERCEPTOR_IMPL(__isoc99_vsscanf, false, str, format, ap) + +INTERCEPTOR(int, __isoc99_vfscanf, void *stream, const char *format, va_list ap) +VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap) + +#define SCANF_INTERCEPTOR_IMPL(name, vname, ...) \ + { \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, name, __VA_ARGS__); \ + va_list ap; \ + va_start(ap, format); \ + int res = vname(__VA_ARGS__, ap); \ + va_end(ap); \ + return res; \ + } + +INTERCEPTOR(int, scanf, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(scanf, vscanf, format) + +INTERCEPTOR(int, fscanf, void *stream, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format) + +INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format) + +INTERCEPTOR(int, __isoc99_scanf, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format) + +INTERCEPTOR(int, __isoc99_fscanf, void *stream, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format) + +INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...) +SCANF_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) -#define INIT_SCANF \ - INTERCEPT_FUNCTION(scanf); \ - INTERCEPT_FUNCTION(sscanf); /* NOLINT */ \ - INTERCEPT_FUNCTION(fscanf); \ - INTERCEPT_FUNCTION(vscanf); \ - INTERCEPT_FUNCTION(vsscanf); \ - INTERCEPT_FUNCTION(vfscanf) +#define INIT_SCANF \ + INTERCEPT_FUNCTION(scanf); \ + INTERCEPT_FUNCTION(sscanf); \ + INTERCEPT_FUNCTION(fscanf); \ + INTERCEPT_FUNCTION(vscanf); \ + INTERCEPT_FUNCTION(vsscanf); \ + INTERCEPT_FUNCTION(vfscanf); \ + INTERCEPT_FUNCTION(__isoc99_scanf); \ + INTERCEPT_FUNCTION(__isoc99_sscanf); \ + INTERCEPT_FUNCTION(__isoc99_fscanf); \ + INTERCEPT_FUNCTION(__isoc99_vscanf); \ + INTERCEPT_FUNCTION(__isoc99_vsscanf); \ + INTERCEPT_FUNCTION(__isoc99_vfscanf); #else #define INIT_SCANF #endif -#define SANITIZER_COMMON_INTERCEPTORS_INIT \ - INIT_READ; \ - INIT_PREAD; \ - INIT_PREAD64; \ - INIT_PRCTL; \ - INIT_WRITE; \ +#define SANITIZER_COMMON_INTERCEPTORS_INIT \ + INIT_READ; \ + INIT_PREAD; \ + INIT_PREAD64; \ + INIT_PRCTL; \ + INIT_WRITE; \ + INIT_PWRITE; \ + INIT_PWRITE64; \ INIT_SCANF; diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc index f7cab5f0dbb..5b761382d3e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc @@ -6,87 +6,41 @@ //===----------------------------------------------------------------------===// // // Scanf implementation for use in *Sanitizer interceptors. +// Follows http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html +// with a few common GNU extensions. // //===----------------------------------------------------------------------===// #include <stdarg.h> -#ifdef _WIN32 -#define va_copy(dst, src) ((dst) = (src)) -#endif // _WIN32 - -struct ScanfSpec { - char c; - unsigned size; -}; - -// One-letter specs. -static const ScanfSpec scanf_specs[] = { - {'p', sizeof(void *)}, - {'e', sizeof(float)}, - {'E', sizeof(float)}, - {'a', sizeof(float)}, - {'f', sizeof(float)}, - {'g', sizeof(float)}, - {'d', sizeof(int)}, - {'i', sizeof(int)}, - {'o', sizeof(int)}, - {'u', sizeof(int)}, - {'x', sizeof(int)}, - {'X', sizeof(int)}, - {'n', sizeof(int)}, - {'t', sizeof(PTRDIFF_T)}, - {'z', sizeof(SIZE_T)}, - {'j', sizeof(INTMAX_T)}, - {'h', sizeof(short)} -}; - -static const unsigned scanf_specs_cnt = - sizeof(scanf_specs) / sizeof(scanf_specs[0]); - -// %ll?, %L?, %q? specs -static const ScanfSpec scanf_llspecs[] = { - {'e', sizeof(long double)}, - {'f', sizeof(long double)}, - {'g', sizeof(long double)}, - {'d', sizeof(long long)}, - {'i', sizeof(long long)}, - {'o', sizeof(long long)}, - {'u', sizeof(long long)}, - {'x', sizeof(long long)} +struct ScanfDirective { + int argIdx; // argument index, or -1 of not specified ("%n$") + int fieldWidth; + bool suppressed; // suppress assignment ("*") + bool allocate; // allocate space ("m") + char lengthModifier[2]; + char convSpecifier; + bool maybeGnuMalloc; }; -static const unsigned scanf_llspecs_cnt = - sizeof(scanf_llspecs) / sizeof(scanf_llspecs[0]); - -// %l? specs -static const ScanfSpec scanf_lspecs[] = { - {'e', sizeof(double)}, - {'f', sizeof(double)}, - {'g', sizeof(double)}, - {'d', sizeof(long)}, - {'i', sizeof(long)}, - {'o', sizeof(long)}, - {'u', sizeof(long)}, - {'x', sizeof(long)}, - {'X', sizeof(long)}, -}; - -static const unsigned scanf_lspecs_cnt = - sizeof(scanf_lspecs) / sizeof(scanf_lspecs[0]); - -static unsigned match_spec(const struct ScanfSpec *spec, unsigned n, char c) { - for (unsigned i = 0; i < n; ++i) - if (spec[i].c == c) - return spec[i].size; - return 0; +static const char *parse_number(const char *p, int *out) { + *out = internal_atoll(p); + while (*p >= '0' && *p <= '9') + ++p; + return p; } -static void scanf_common(void *ctx, const char *format, va_list ap_const) { - va_list aq; - va_copy(aq, ap_const); +static bool char_is_one_of(char c, const char *s) { + return !!internal_strchr(s, c); +} - const char *p = format; - unsigned size; +// Parse scanf format string. If a valid directive in encountered, it is +// returned in dir. This function returns the pointer to the first +// unprocessed character, or 0 in case of error. +// In case of the end-of-string, a pointer to the closing \0 is returned. +static const char *scanf_parse_next(const char *p, bool allowGnuMalloc, + ScanfDirective *dir) { + internal_memset(dir, 0, sizeof(*dir)); + dir->argIdx = -1; while (*p) { if (*p != '%') { @@ -94,51 +48,260 @@ static void scanf_common(void *ctx, const char *format, va_list ap_const) { continue; } ++p; - if (*p == '*' || *p == '%' || *p == 0) { + // %% + if (*p == '%') { ++p; continue; } - if (*p == '0' || (*p >= '1' && *p <= '9')) { - size = internal_atoll(p); - // +1 for the \0 at the end - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size + 1); + if (*p == '\0') { + return 0; + } + // %n$ + if (*p >= '0' && *p <= '9') { + int number; + const char *q = parse_number(p, &number); + if (*q == '$') { + dir->argIdx = number; + p = q + 1; + } + // Otherwise, do not change p. This will be re-parsed later as the field + // width. + } + // * + if (*p == '*') { + dir->suppressed = true; ++p; - continue; } - - if (*p == 'L' || *p == 'q') { + // Field width. + if (*p >= '0' && *p <= '9') { + p = parse_number(p, &dir->fieldWidth); + if (dir->fieldWidth <= 0) + return 0; + } + // m + if (*p == 'm') { + dir->allocate = true; ++p; - size = match_spec(scanf_llspecs, scanf_llspecs_cnt, *p); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size); - continue; } - - if (*p == 'l') { + // Length modifier. + if (char_is_one_of(*p, "jztLq")) { + dir->lengthModifier[0] = *p; + ++p; + } else if (*p == 'h') { + dir->lengthModifier[0] = 'h'; + ++p; + if (*p == 'h') { + dir->lengthModifier[1] = 'h'; + ++p; + } + } else if (*p == 'l') { + dir->lengthModifier[0] = 'l'; ++p; if (*p == 'l') { + dir->lengthModifier[1] = 'l'; ++p; - size = match_spec(scanf_llspecs, scanf_llspecs_cnt, *p); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size); - continue; - } else { - size = match_spec(scanf_lspecs, scanf_lspecs_cnt, *p); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size); - continue; } } + // Conversion specifier. + dir->convSpecifier = *p++; + // Consume %[...] expression. + if (dir->convSpecifier == '[') { + if (*p == '^') + ++p; + if (*p == ']') + ++p; + while (*p && *p != ']') + ++p; + if (*p == 0) + return 0; // unexpected end of string + // Consume the closing ']'. + ++p; + } + // This is unfortunately ambiguous between old GNU extension + // of %as, %aS and %a[...] and newer POSIX %a followed by + // letters s, S or [. + if (allowGnuMalloc && dir->convSpecifier == 'a' && + !dir->lengthModifier[0]) { + if (*p == 's' || *p == 'S') { + dir->maybeGnuMalloc = true; + ++p; + } else if (*p == '[') { + // Watch for %a[h-j%d], if % appears in the + // [...] range, then we need to give up, we don't know + // if scanf will parse it as POSIX %a [h-j %d ] or + // GNU allocation of string with range dh-j plus %. + const char *q = p + 1; + if (*q == '^') + ++q; + if (*q == ']') + ++q; + while (*q && *q != ']' && *q != '%') + ++q; + if (*q == 0 || *q == '%') + return 0; + p = q + 1; // Consume the closing ']'. + dir->maybeGnuMalloc = true; + } + } + break; + } + return p; +} - if (*p == 'h' && *(p + 1) == 'h') { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), sizeof(char)); - p += 2; - continue; +// Returns true if the character is an integer conversion specifier. +static bool scanf_is_integer_conv(char c) { + return char_is_one_of(c, "diouxXn"); +} + +// Returns true if the character is an floating point conversion specifier. +static bool scanf_is_float_conv(char c) { + return char_is_one_of(c, "aAeEfFgG"); +} + +// Returns string output character size for string-like conversions, +// or 0 if the conversion is invalid. +static int scanf_get_char_size(ScanfDirective *dir) { + if (char_is_one_of(dir->convSpecifier, "CS")) { + // wchar_t + return 0; + } + + if (char_is_one_of(dir->convSpecifier, "cs[")) { + if (dir->lengthModifier[0] == 'l') + // wchar_t + return 0; + else if (dir->lengthModifier[0] == 0) + return sizeof(char); + else + return 0; + } + + return 0; +} + +enum ScanfStoreSize { + // Store size not known in advance; can be calculated as strlen() of the + // destination buffer. + SSS_STRLEN = -1, + // Invalid conversion specifier. + SSS_INVALID = 0 +}; + +// Returns the store size of a scanf directive (if >0), or a value of +// ScanfStoreSize. +static int scanf_get_store_size(ScanfDirective *dir) { + if (dir->allocate) { + if (!char_is_one_of(dir->convSpecifier, "cCsS[")) + return SSS_INVALID; + return sizeof(char *); + } + + if (dir->maybeGnuMalloc) { + if (dir->convSpecifier != 'a' || dir->lengthModifier[0]) + return SSS_INVALID; + // This is ambiguous, so check the smaller size of char * (if it is + // a GNU extension of %as, %aS or %a[...]) and float (if it is + // POSIX %a followed by s, S or [ letters). + return sizeof(char *) < sizeof(float) ? sizeof(char *) : sizeof(float); + } + + if (scanf_is_integer_conv(dir->convSpecifier)) { + switch (dir->lengthModifier[0]) { + case 'h': + return dir->lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short); + case 'l': + return dir->lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long); + case 'L': + return sizeof(long long); + case 'j': + return sizeof(INTMAX_T); + case 'z': + return sizeof(SIZE_T); + case 't': + return sizeof(PTRDIFF_T); + case 0: + return sizeof(int); + default: + return SSS_INVALID; } + } - size = match_spec(scanf_specs, scanf_specs_cnt, *p); - if (size) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size); - ++p; + if (scanf_is_float_conv(dir->convSpecifier)) { + switch (dir->lengthModifier[0]) { + case 'L': + case 'q': + return sizeof(long double); + case 'l': + return dir->lengthModifier[1] == 'l' ? sizeof(long double) + : sizeof(double); + case 0: + return sizeof(float); + default: + return SSS_INVALID; + } + } + + if (char_is_one_of(dir->convSpecifier, "sS[")) { + unsigned charSize = scanf_get_char_size(dir); + if (charSize == 0) + return SSS_INVALID; + if (dir->fieldWidth == 0) + return SSS_STRLEN; + return (dir->fieldWidth + 1) * charSize; + } + + if (char_is_one_of(dir->convSpecifier, "cC")) { + unsigned charSize = scanf_get_char_size(dir); + if (charSize == 0) + return SSS_INVALID; + if (dir->fieldWidth == 0) + return charSize; + return dir->fieldWidth * charSize; + } + + if (dir->convSpecifier == 'p') { + if (dir->lengthModifier[1] != 0) + return SSS_INVALID; + return sizeof(void *); + } + + return SSS_INVALID; +} + +// Common part of *scanf interceptors. +// Process format string and va_list, and report all store ranges. +// Stops when "consuming" n_inputs input items. +static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc, + const char *format, va_list aq) { + CHECK_GT(n_inputs, 0); + const char *p = format; + + while (*p && n_inputs) { + ScanfDirective dir; + p = scanf_parse_next(p, allowGnuMalloc, &dir); + if (!p) + break; + if (dir.convSpecifier == 0) { + // This can only happen at the end of the format string. + CHECK_EQ(*p, 0); + break; + } + // Here the directive is valid. Do what it says. + if (dir.argIdx != -1) { + // Unsupported. + break; + } + if (dir.suppressed) continue; + int size = scanf_get_store_size(&dir); + if (size == SSS_INVALID) + break; + void *argp = va_arg(aq, void *); + if (dir.convSpecifier != 'n') + --n_inputs; + if (size == SSS_STRLEN) { + size = internal_strlen((const char *)argp) + 1; } + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size); } - va_end(aq); } diff --git a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h index 01f08f57801..1a25b70c23e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h +++ b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h @@ -11,7 +11,85 @@ #ifndef SANITIZER_DEFS_H #define SANITIZER_DEFS_H -#include "sanitizer/common_interface_defs.h" +#if defined(_WIN32) +// FIXME find out what we need on Windows. __declspec(dllexport) ? +# define SANITIZER_INTERFACE_ATTRIBUTE +# define SANITIZER_WEAK_ATTRIBUTE +#elif defined(SANITIZER_GO) +# define SANITIZER_INTERFACE_ATTRIBUTE +# define SANITIZER_WEAK_ATTRIBUTE +#else +# define SANITIZER_INTERFACE_ATTRIBUTE __attribute__((visibility("default"))) +# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) +#endif + +#ifdef __linux__ +# define SANITIZER_SUPPORTS_WEAK_HOOKS 1 +#else +# define SANITIZER_SUPPORTS_WEAK_HOOKS 0 +#endif + +// __has_feature +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif + +// For portability reasons we do not include stddef.h, stdint.h or any other +// system header, but we do need some basic types that are not defined +// in a portable way by the language itself. +namespace __sanitizer { + +#if defined(_WIN64) +// 64-bit Windows uses LLP64 data model. +typedef unsigned long long uptr; // NOLINT +typedef signed long long sptr; // NOLINT +#else +typedef unsigned long uptr; // NOLINT +typedef signed long sptr; // NOLINT +#endif // defined(_WIN64) +#if defined(__x86_64__) +// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use +// 64-bit pointer to unwind stack frame. +typedef unsigned long long uhwptr; // NOLINT +#else +typedef uptr uhwptr; // NOLINT +#endif +typedef unsigned char u8; +typedef unsigned short u16; // NOLINT +typedef unsigned int u32; +typedef unsigned long long u64; // NOLINT +typedef signed char s8; +typedef signed short s16; // NOLINT +typedef signed int s32; +typedef signed long long s64; // NOLINT +typedef int fd_t; + +} // namespace __sanitizer + +extern "C" { + // Tell the tools to write their reports to "path.<pid>" instead of stderr. + void __sanitizer_set_report_path(const char *path) + SANITIZER_INTERFACE_ATTRIBUTE; + + // Tell the tools to write their reports to given file descriptor instead of + // stderr. + void __sanitizer_set_report_fd(int fd) + SANITIZER_INTERFACE_ATTRIBUTE; + + // Notify the tools that the sandbox is going to be turned on. The reserved + // parameter will be used in the future to hold a structure with functions + // that the tools may call to bypass the sandbox. + void __sanitizer_sandbox_on_notify(void *reserved) + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + + // This function is called by the tool when it has just finished reporting + // an error. 'error_summary' is a one-line string that summarizes + // the error message. This function can be overridden by the client. + void __sanitizer_report_error_summary(const char *error_summary) + SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; +} // extern "C" + + using namespace __sanitizer; // NOLINT // ----------- ATTENTION ------------- // This header should NOT include any other headers to avoid portability issues. diff --git a/libsanitizer/sanitizer_common/sanitizer_lfstack.h b/libsanitizer/sanitizer_common/sanitizer_lfstack.h index 63fbf066943..8033b9a7f9c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_lfstack.h +++ b/libsanitizer/sanitizer_common/sanitizer_lfstack.h @@ -66,6 +66,6 @@ struct LFStack { atomic_uint64_t head_; }; -} +} // namespace __sanitizer #endif // #ifndef SANITIZER_LFSTACK_H diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.cc b/libsanitizer/sanitizer_common/sanitizer_libc.cc index b02cbd4aced..c57128a54cb 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.cc +++ b/libsanitizer/sanitizer_common/sanitizer_libc.cc @@ -204,7 +204,7 @@ s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) { } bool mem_is_zero(const char *beg, uptr size) { - CHECK_LE(size, 1UL << FIRST_32_SECOND_64(30, 40)); // Sanity check. + CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40)); // Sanity check. const char *end = beg + size; uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr)); uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr)); diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.h b/libsanitizer/sanitizer_common/sanitizer_libc.h index f193017f953..7a9774406fa 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.h +++ b/libsanitizer/sanitizer_common/sanitizer_libc.h @@ -9,14 +9,13 @@ // run-time libraries. // These tools can not use some of the libc functions directly because those // functions are intercepted. Instead, we implement a tiny subset of libc here. -// NOTE: This file may be included into user code. //===----------------------------------------------------------------------===// #ifndef SANITIZER_LIBC_H #define SANITIZER_LIBC_H // ----------- ATTENTION ------------- // This header should NOT include any other headers from sanitizer runtime. -#include "sanitizer/common_interface_defs.h" +#include "sanitizer_internal_defs.h" namespace __sanitizer { @@ -56,17 +55,24 @@ void *internal_mmap(void *addr, uptr length, int prot, int flags, int internal_munmap(void *addr, uptr length); // I/O -typedef int fd_t; const fd_t kInvalidFd = -1; const fd_t kStdinFd = 0; const fd_t kStdoutFd = 1; const fd_t kStderrFd = 2; int internal_close(fd_t fd); int internal_isatty(fd_t fd); -fd_t internal_open(const char *filename, bool write); + +// Use __sanitizer::OpenFile() instead. +fd_t internal_open(const char *filename, int flags); +fd_t internal_open(const char *filename, int flags, u32 mode); + uptr internal_read(fd_t fd, void *buf, uptr count); uptr internal_write(fd_t fd, const void *buf, uptr count); uptr internal_filesize(fd_t fd); // -1 on error. +int internal_stat(const char *path, void *buf); +int internal_lstat(const char *path, void *buf); +int internal_fstat(fd_t fd, void *buf); + int internal_dup2(int oldfd, int newfd); uptr internal_readlink(const char *path, char *buf, uptr bufsize); int internal_snprintf(char *buffer, uptr length, const char *format, ...); diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.cc b/libsanitizer/sanitizer_common/sanitizer_linux.cc index dc2148f7fc4..3d3aa0b00bd 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux.cc +++ b/libsanitizer/sanitizer_common/sanitizer_linux.cc @@ -28,11 +28,14 @@ #include <sys/syscall.h> #include <sys/time.h> #include <sys/types.h> +#include <sys/prctl.h> #include <unistd.h> #include <unwind.h> #include <errno.h> -#include <sys/prctl.h> -#include <linux/futex.h> + +// <linux/futex.h> is broken on some linux distributions. +const int FUTEX_WAIT = 0; +const int FUTEX_WAKE = 1; // Are we using 32-bit or 64-bit syscalls? // x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32 @@ -63,8 +66,16 @@ int internal_close(fd_t fd) { return syscall(__NR_close, fd); } -fd_t internal_open(const char *filename, bool write) { - return syscall(__NR_open, filename, +fd_t internal_open(const char *filename, int flags) { + return syscall(__NR_open, filename, flags); +} + +fd_t internal_open(const char *filename, int flags, u32 mode) { + return syscall(__NR_open, filename, flags, mode); +} + +fd_t OpenFile(const char *filename, bool write) { + return internal_open(filename, write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); } @@ -80,16 +91,38 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { return res; } +int internal_stat(const char *path, void *buf) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return syscall(__NR_stat, path, buf); +#else + return syscall(__NR_stat64, path, buf); +#endif +} + +int internal_lstat(const char *path, void *buf) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return syscall(__NR_lstat, path, buf); +#else + return syscall(__NR_lstat64, path, buf); +#endif +} + +int internal_fstat(fd_t fd, void *buf) { +#if SANITIZER_LINUX_USES_64BIT_SYSCALLS + return syscall(__NR_fstat, fd, buf); +#else + return syscall(__NR_fstat64, fd, buf); +#endif +} + uptr internal_filesize(fd_t fd) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS struct stat st; - if (syscall(__NR_fstat, fd, &st)) - return -1; #else struct stat64 st; - if (syscall(__NR_fstat64, fd, &st)) - return -1; #endif + if (internal_fstat(fd, &st)) + return -1; return (uptr)st.st_size; } diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_mac.cc index 76bf8670870..309b5a94005 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.cc +++ b/libsanitizer/sanitizer_common/sanitizer_mac.cc @@ -11,6 +11,12 @@ //===----------------------------------------------------------------------===// #ifdef __APPLE__ +// Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so +// the clients will most certainly use 64-bit ones as well. +#ifndef _DARWIN_USE_64_BIT_INODE +#define _DARWIN_USE_64_BIT_INODE 1 +#endif +#include <stdio.h> #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" @@ -46,9 +52,17 @@ int internal_close(fd_t fd) { return close(fd); } -fd_t internal_open(const char *filename, bool write) { - return open(filename, - write ? O_WRONLY | O_CREAT : O_RDONLY, 0660); +fd_t internal_open(const char *filename, int flags) { + return open(filename, flags); +} + +fd_t internal_open(const char *filename, int flags, u32 mode) { + return open(filename, flags, mode); +} + +fd_t OpenFile(const char *filename, bool write) { + return internal_open(filename, + write ? O_WRONLY | O_CREAT : O_RDONLY, 0660); } uptr internal_read(fd_t fd, void *buf, uptr count) { @@ -59,9 +73,21 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { return write(fd, buf, count); } +int internal_stat(const char *path, void *buf) { + return stat(path, (struct stat *)buf); +} + +int internal_lstat(const char *path, void *buf) { + return lstat(path, (struct stat *)buf); +} + +int internal_fstat(fd_t fd, void *buf) { + return fstat(fd, (struct stat *)buf); +} + uptr internal_filesize(fd_t fd) { struct stat st; - if (fstat(fd, &st)) + if (internal_fstat(fd, &st)) return -1; return (uptr)st.st_size; } diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h index 0ca9444fcb8..2c60253c533 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h @@ -33,4 +33,4 @@ # define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID # define SANITIZER_INTERCEPT_PRCTL SI_LINUX_NOT_ANDROID -# define SANITIZER_INTERCEPT_SCANF 0 +# define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.cc b/libsanitizer/sanitizer_common/sanitizer_posix.cc index 17287cd950e..48c5ebaef3e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix.cc +++ b/libsanitizer/sanitizer_common/sanitizer_posix.cc @@ -56,12 +56,12 @@ void *MmapOrDie(uptr size, const char *mem_type) { if (recursion_count) { // The Report() and CHECK calls below may call mmap recursively and fail. // If we went into recursion, just die. - RawWrite("AddressSanitizer is unable to mmap\n"); + RawWrite("ERROR: Failed to mmap\n"); Die(); } recursion_count++; - Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s: %s\n", - size, size, mem_type, strerror(errno)); + Report("ERROR: %s failed to allocate 0x%zx (%zd) bytes of %s: %s\n", + SanitizerToolName, size, size, mem_type, strerror(errno)); DumpProcessMap(); CHECK("unable to mmap" && 0); } @@ -72,8 +72,8 @@ void UnmapOrDie(void *addr, uptr size) { if (!addr || !size) return; int res = internal_munmap(addr, size); if (res != 0) { - Report("ERROR: Failed to deallocate 0x%zx (%zd) bytes at address %p\n", - size, size, addr); + Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n", + SanitizerToolName, size, size, addr); CHECK("unable to unmap" && 0); } } @@ -86,8 +86,9 @@ void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, -1, 0); if (p == (void*)-1) - Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n", - size, size, fixed_addr, errno); + Report("ERROR: " + "%s failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n", + SanitizerToolName, size, size, fixed_addr, errno); return p; } @@ -99,8 +100,9 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size) { MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); if (p == (void*)-1) { - Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n", - size, size, fixed_addr, errno); + Report("ERROR:" + " %s failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n", + SanitizerToolName, size, size, fixed_addr, errno); CHECK("unable to mmap" && 0); } return p; @@ -118,7 +120,7 @@ void FlushUnneededShadowMemory(uptr addr, uptr size) { } void *MapFileToMemory(const char *file_name, uptr *buff_size) { - fd_t fd = internal_open(file_name, false); + fd_t fd = OpenFile(file_name, false); CHECK_NE(fd, kInvalidFd); uptr fsize = internal_filesize(fd); CHECK_NE(fsize, (uptr)-1); @@ -187,7 +189,7 @@ void SetStackSizeLimitInBytes(uptr limit) { rlim.rlim_cur = limit; rlim.rlim_max = limit; if (setrlimit(RLIMIT_STACK, &rlim)) { - Report("setrlimit() failed %d\n", errno); + Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno); Die(); } CHECK(!StackSizeIsUnlimited()); diff --git a/libsanitizer/sanitizer_common/sanitizer_quarantine.h b/libsanitizer/sanitizer_common/sanitizer_quarantine.h index 042fba7c1da..f3ad91a9cc2 100644 --- a/libsanitizer/sanitizer_common/sanitizer_quarantine.h +++ b/libsanitizer/sanitizer_common/sanitizer_quarantine.h @@ -157,7 +157,7 @@ class QuarantineCache { atomic_store(&size_, Size() + add, memory_order_relaxed); } - QuarantineBatch *NOINLINE AllocBatch(Callback cb) { + NOINLINE QuarantineBatch* AllocBatch(Callback cb) { QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b)); b->count = 0; b->size = 0; @@ -165,6 +165,6 @@ class QuarantineCache { return b; } }; -} +} // namespace __sanitizer #endif // #ifndef SANITIZER_QUARANTINE_H diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h index 1e917eb53bb..bf73cf14aad 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h @@ -11,7 +11,7 @@ #ifndef SANITIZER_STACKDEPOT_H #define SANITIZER_STACKDEPOT_H -#include "sanitizer/common_interface_defs.h" +#include "sanitizer_internal_defs.h" namespace __sanitizer { diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc index 59af1c35292..259da0082e3 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc @@ -15,8 +15,9 @@ #include "sanitizer_symbolizer.h" namespace __sanitizer { -static const char *StripPathPrefix(const char *filepath, - const char *strip_file_prefix) { +const char *StripPathPrefix(const char *filepath, + const char *strip_file_prefix) { + if (filepath == 0) return 0; if (filepath == internal_strstr(filepath, strip_file_prefix)) return filepath + internal_strlen(strip_file_prefix); return filepath; diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h index c939644401c..fd0c4671a61 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h @@ -55,6 +55,10 @@ struct StackTrace { u32 *compressed, uptr size); }; + +const char *StripPathPrefix(const char *filepath, + const char *strip_file_prefix); + } // namespace __sanitizer // Use this macro if you want to print stack trace with the caller diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc index f62acf35f8f..2c9cb2b0a55 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc @@ -174,6 +174,53 @@ class ExternalSymbolizer { static LowLevelAllocator symbolizer_allocator; // Linker initialized. +#if SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset, + char *Buffer, int MaxLength); +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset, + char *Buffer, int MaxLength); +} // extern "C" + +class InternalSymbolizer { + public: + typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); + static InternalSymbolizer *get() { + if (__sanitizer_symbolize_code != 0 && + __sanitizer_symbolize_data != 0) { + void *mem = symbolizer_allocator.Allocate(sizeof(InternalSymbolizer)); + return new(mem) InternalSymbolizer(); + } + return 0; + } + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data + : __sanitizer_symbolize_code; + if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize)) + return buffer_; + return 0; + } + + private: + InternalSymbolizer() { } + + static const int kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; +}; +#else // SANITIZER_SUPPORTS_WEAK_HOOKS + +class InternalSymbolizer { + public: + static InternalSymbolizer *get() { return 0; } + char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + return 0; + } +}; + +#endif // SANITIZER_SUPPORTS_WEAK_HOOKS + class Symbolizer { public: uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) { @@ -266,8 +313,23 @@ class Symbolizer { return true; } + bool IsSymbolizerAvailable() { + if (internal_symbolizer_ == 0) + internal_symbolizer_ = InternalSymbolizer::get(); + return internal_symbolizer_ || external_symbolizer_; + } + private: char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + // First, try to use internal symbolizer. + if (internal_symbolizer_ == 0) { + internal_symbolizer_ = InternalSymbolizer::get(); + } + if (internal_symbolizer_) { + return internal_symbolizer_->SendCommand(is_data, module_name, + module_offset); + } + // Otherwise, fall back to external symbolizer. if (external_symbolizer_ == 0) { ReportExternalSymbolizerError( "WARNING: Trying to symbolize code, but external " @@ -322,6 +384,7 @@ class Symbolizer { uptr n_modules_; ExternalSymbolizer *external_symbolizer_; // Leaked. + InternalSymbolizer *internal_symbolizer_; // Leaked. }; static Symbolizer symbolizer; // Linker initialized. @@ -338,4 +401,8 @@ bool InitializeExternalSymbolizer(const char *path_to_symbolizer) { return symbolizer.InitializeExternalSymbolizer(path_to_symbolizer); } +bool IsSymbolizerAvailable() { + return symbolizer.IsSymbolizerAvailable(); +} + } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h index b88fa3f655a..751806e8472 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h @@ -67,6 +67,8 @@ struct DataInfo { uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames); bool SymbolizeData(uptr address, DataInfo *info); +bool IsSymbolizerAvailable(); + // Attempts to demangle the provided C++ mangled name. const char *Demangle(const char *Name); diff --git a/libsanitizer/sanitizer_common/sanitizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_win.cc index 242b4429bd7..3d5cde11cf8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_win.cc +++ b/libsanitizer/sanitizer_common/sanitizer_win.cc @@ -95,6 +95,11 @@ void *Mprotect(uptr fixed_addr, uptr size) { MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); } +void FlushUnneededShadowMemory(uptr addr, uptr size) { + // This is almost useless on 32-bits. + // FIXME: add madvice-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; @@ -189,7 +194,15 @@ int internal_isatty(fd_t fd) { return _isatty(fd); } -fd_t internal_open(const char *filename, bool write) { +fd_t internal_open(const char *filename, int flags) { + UNIMPLEMENTED(); +} + +fd_t internal_open(const char *filename, int flags, u32 mode) { + UNIMPLEMENTED(); +} + +fd_t OpenFile(const char *filename, bool write) { UNIMPLEMENTED(); } @@ -209,6 +222,18 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { return ret; } +int internal_stat(const char *path, void *buf) { + UNIMPLEMENTED(); +} + +int internal_lstat(const char *path, void *buf) { + UNIMPLEMENTED(); +} + +int internal_fstat(fd_t fd, void *buf) { + UNIMPLEMENTED(); +} + uptr internal_filesize(fd_t fd) { UNIMPLEMENTED(); } @@ -227,10 +252,8 @@ int internal_sched_yield() { } // ---------------------- BlockingMutex ---------------- {{{1 -enum LockState { - LOCK_UNINITIALIZED = 0, - LOCK_READY = -1, -}; +const uptr LOCK_UNINITIALIZED = 0; +const uptr LOCK_READY = (uptr)-1; BlockingMutex::BlockingMutex(LinkerInitialized li) { // FIXME: see comments in BlockingMutex::Lock() for the details. @@ -252,12 +275,12 @@ void BlockingMutex::Lock() { // locks while we're starting in one thread to avoid double-init races. } EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_); - CHECK(owner_ == LOCK_READY); + CHECK_EQ(owner_, LOCK_READY); owner_ = GetThreadSelf(); } void BlockingMutex::Unlock() { - CHECK(owner_ == GetThreadSelf()); + CHECK_EQ(owner_, GetThreadSelf()); owner_ = LOCK_READY; LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); } |