Index: compiler-rt/lib/fuzzer/CMakeLists.txt =================================================================== --- compiler-rt/lib/fuzzer/CMakeLists.txt +++ compiler-rt/lib/fuzzer/CMakeLists.txt @@ -40,6 +40,7 @@ FuzzerIO.h FuzzerInterface.h FuzzerInternal.h + FuzzerLock.h FuzzerMerge.h FuzzerMonitor.h FuzzerMutate.h @@ -65,6 +66,7 @@ COMPILER_RT_LIBCXX_PATH AND COMPILER_RT_LIBCXXABI_PATH) list(APPEND LIBFUZZER_CFLAGS -D_LIBCPP_ABI_VERSION=Fuzzer) + list(APPEND LIBFUZZER_CFLAGS -D_LIBCPP_ABI_VERSION=Fuzzer) append_list_if(COMPILER_RT_HAS_NOSTDINCXX_FLAG -nostdinc++ LIBFUZZER_CFLAGS) elseif(TARGET cxx-headers OR HAVE_LIBCXX) # libFuzzer uses C++ standard library headers. @@ -88,6 +90,8 @@ endif() endif() +list(APPEND LIBFUZZER_CFLAGS -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS) + add_compiler_rt_component(fuzzer) add_compiler_rt_object_libraries(RTfuzzer Index: compiler-rt/lib/fuzzer/FuzzerDefs.h =================================================================== --- compiler-rt/lib/fuzzer/FuzzerDefs.h +++ compiler-rt/lib/fuzzer/FuzzerDefs.h @@ -70,6 +70,17 @@ extern bool RunningUserCallback; +// Macros to delete copy and/or move constructors +#define FUZZER_NO_COPY(Type) \ + Type(const Type &) = delete; \ + Type &operator=(const Type &) = delete; +#define FUZZER_NO_MOVE(Type) \ + Type(Type &&) = delete; \ + Type &operator=(Type &&) = delete; +#define FUZZER_NO_COPY_OR_MOVE(Type) \ + FUZZER_NO_COPY(Type); \ + FUZZER_NO_MOVE(Type); + } // namespace fuzzer #endif // LLVM_FUZZER_DEFS_H Index: compiler-rt/lib/fuzzer/FuzzerLock.h =================================================================== --- /dev/null +++ compiler-rt/lib/fuzzer/FuzzerLock.h @@ -0,0 +1,71 @@ +//===-- FuzzerLock.h ------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Thread annotation wrappers for std::unique_lock and std::recursize_lock. +// +// These wrappers are minimal. They only add methods for the respective lock +// types as they are used. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_LOCK_H +#define LLVM_FUZZER_LOCK_H + +#include "FuzzerDefs.h" +#include + +// Enable thread safety attributes only with clang. +// The attributes can be safely erased when compiling with other compilers. +#if defined(__clang__) && (!defined(SWIG)) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) +#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) +#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) +#define REQUIRES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) +#define ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) +#define RELEASE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) +#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) +#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +namespace fuzzer { + +// Minimal wrapper for std::unique_lock. +class SCOPED_CAPABILITY UniqueLock final { +public: + explicit UniqueLock(std::mutex &Mutex) ACQUIRE(Mutex) : Lock(Mutex) {} + ~UniqueLock() RELEASE() {} + operator bool() const { return Lock.owns_lock(); } + std::unique_lock &get() { return Lock; } + std::mutex *release() { return Lock.release(); } + +private: + std::unique_lock Lock; + FUZZER_NO_COPY_OR_MOVE(UniqueLock); +}; + +// Minimal wrapper for std::recursive_mutex. +class CAPABILITY("recursive_mutex") RecursiveMutex final { +public: + RecursiveMutex() = default; + ~RecursiveMutex() = default; + void lock() ACQUIRE() { Mutex.lock(); } + void unlock() RELEASE() { Mutex.unlock(); } + +private: + std::recursive_mutex Mutex; + FUZZER_NO_COPY_OR_MOVE(RecursiveMutex); +}; + +} // namespace fuzzer + +#endif // LLVM_FUZZER_LOCK_H Index: compiler-rt/lib/fuzzer/FuzzerMonitor.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerMonitor.cpp +++ compiler-rt/lib/fuzzer/FuzzerMonitor.cpp @@ -11,6 +11,7 @@ #include "FuzzerMonitor.h" #include "FuzzerExtFunctions.h" #include "FuzzerIO.h" +#include "FuzzerLock.h" #include "FuzzerPlatform.h" #include "FuzzerUtil.h" #include @@ -77,8 +78,8 @@ std::atomic Frees; int TraceLevel = 0; - std::recursive_mutex TraceMutex; - bool TraceDisabled; + RecursiveMutex TraceMutex; + bool TraceDisabled GUARDED_BY(TraceMutex); size_t RssLimitMb; int PurgeIntervalSec; @@ -89,20 +90,23 @@ // Locks printing and avoids nested hooks triggered from mallocs/frees in // sanitizer. -class TraceLock { +class SCOPED_CAPABILITY TraceLock { public: - TraceLock() : Lock(AllocMonitor.TraceMutex) { + TraceLock() ACQUIRE(AllocMonitor.TraceMutex) : Lock(AllocMonitor.TraceMutex) { AllocMonitor.TraceDisabled = !AllocMonitor.TraceDisabled; } - ~TraceLock() { AllocMonitor.TraceDisabled = !AllocMonitor.TraceDisabled; } - bool IsDisabled() const { + ~TraceLock() RELEASE() { + AllocMonitor.TraceDisabled = !AllocMonitor.TraceDisabled; + } + + bool IsDisabled() const REQUIRES(AllocMonitor.TraceMutex) { // This is already inverted value. return !AllocMonitor.TraceDisabled; } private: - std::lock_guard Lock; + std::lock_guard Lock; }; ATTRIBUTE_NO_SANITIZE_MEMORY