WIP start adding support for TSAN
This commit is contained in:
@@ -537,6 +537,7 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/ir.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/libc_installation.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/libcxx.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/libtsan.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/libunwind.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/link/C.zig"
|
||||
|
||||
304
lib/tsan/interception/interception.h
Normal file
304
lib/tsan/interception/interception.h
Normal file
@@ -0,0 +1,304 @@
|
||||
//===-- interception.h ------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Machinery for providing replacements/wrappers for system functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef INTERCEPTION_H
|
||||
#define INTERCEPTION_H
|
||||
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
|
||||
#if !SANITIZER_LINUX && !SANITIZER_FREEBSD && !SANITIZER_MAC && \
|
||||
!SANITIZER_NETBSD && !SANITIZER_OPENBSD && !SANITIZER_WINDOWS && \
|
||||
!SANITIZER_FUCHSIA && !SANITIZER_RTEMS && !SANITIZER_SOLARIS
|
||||
# error "Interception doesn't work on this operating system."
|
||||
#endif
|
||||
|
||||
// These typedefs should be used only in the interceptor definitions to replace
|
||||
// the standard system types (e.g. SSIZE_T instead of ssize_t)
|
||||
typedef __sanitizer::uptr SIZE_T;
|
||||
typedef __sanitizer::sptr SSIZE_T;
|
||||
typedef __sanitizer::sptr PTRDIFF_T;
|
||||
typedef __sanitizer::s64 INTMAX_T;
|
||||
typedef __sanitizer::u64 UINTMAX_T;
|
||||
typedef __sanitizer::OFF_T OFF_T;
|
||||
typedef __sanitizer::OFF64_T OFF64_T;
|
||||
|
||||
// How to add an interceptor:
|
||||
// Suppose you need to wrap/replace system function (generally, from libc):
|
||||
// int foo(const char *bar, double baz);
|
||||
// You'll need to:
|
||||
// 1) define INTERCEPTOR(int, foo, const char *bar, double baz) { ... } in
|
||||
// your source file. See the notes below for cases when
|
||||
// INTERCEPTOR_WITH_SUFFIX(...) should be used instead.
|
||||
// 2) Call "INTERCEPT_FUNCTION(foo)" prior to the first call of "foo".
|
||||
// INTERCEPT_FUNCTION(foo) evaluates to "true" iff the function was
|
||||
// intercepted successfully.
|
||||
// You can access original function by calling REAL(foo)(bar, baz).
|
||||
// By default, REAL(foo) will be visible only inside your interceptor, and if
|
||||
// you want to use it in other parts of RTL, you'll need to:
|
||||
// 3a) add DECLARE_REAL(int, foo, const char*, double) to a
|
||||
// header file.
|
||||
// However, if the call "INTERCEPT_FUNCTION(foo)" and definition for
|
||||
// INTERCEPTOR(..., foo, ...) are in different files, you'll instead need to:
|
||||
// 3b) add DECLARE_REAL_AND_INTERCEPTOR(int, foo, const char*, double)
|
||||
// to a header file.
|
||||
|
||||
// Notes: 1. Things may not work properly if macro INTERCEPTOR(...) {...} or
|
||||
// DECLARE_REAL(...) are located inside namespaces.
|
||||
// 2. On Mac you can also use: "OVERRIDE_FUNCTION(foo, zoo)" to
|
||||
// effectively redirect calls from "foo" to "zoo". In this case
|
||||
// you aren't required to implement
|
||||
// INTERCEPTOR(int, foo, const char *bar, double baz) {...}
|
||||
// but instead you'll have to add
|
||||
// DECLARE_REAL(int, foo, const char *bar, double baz) in your
|
||||
// source file (to define a pointer to overriden function).
|
||||
// 3. Some Mac functions have symbol variants discriminated by
|
||||
// additional suffixes, e.g. _$UNIX2003 (see
|
||||
// https://developer.apple.com/library/mac/#releasenotes/Darwin/SymbolVariantsRelNotes/index.html
|
||||
// for more details). To intercept such functions you need to use the
|
||||
// INTERCEPTOR_WITH_SUFFIX(...) macro.
|
||||
|
||||
// How it works:
|
||||
// To replace system functions on Linux we just need to declare functions
|
||||
// with same names in our library and then obtain the real function pointers
|
||||
// using dlsym().
|
||||
// There is one complication. A user may also intercept some of the functions
|
||||
// we intercept. To resolve this we declare our interceptors with __interceptor_
|
||||
// prefix, and then make actual interceptors weak aliases to __interceptor_
|
||||
// functions.
|
||||
//
|
||||
// This is not so on Mac OS, where the two-level namespace makes
|
||||
// our replacement functions invisible to other libraries. This may be overcomed
|
||||
// using the DYLD_FORCE_FLAT_NAMESPACE, but some errors loading the shared
|
||||
// libraries in Chromium were noticed when doing so.
|
||||
// Instead we create a dylib containing a __DATA,__interpose section that
|
||||
// associates library functions with their wrappers. When this dylib is
|
||||
// preloaded before an executable using DYLD_INSERT_LIBRARIES, it routes all
|
||||
// the calls to interposed functions done through stubs to the wrapper
|
||||
// functions.
|
||||
// As it's decided at compile time which functions are to be intercepted on Mac,
|
||||
// INTERCEPT_FUNCTION() is effectively a no-op on this system.
|
||||
|
||||
#if SANITIZER_MAC
|
||||
#include <sys/cdefs.h> // For __DARWIN_ALIAS_C().
|
||||
|
||||
// Just a pair of pointers.
|
||||
struct interpose_substitution {
|
||||
const __sanitizer::uptr replacement;
|
||||
const __sanitizer::uptr original;
|
||||
};
|
||||
|
||||
// For a function foo() create a global pair of pointers { wrap_foo, foo } in
|
||||
// the __DATA,__interpose section.
|
||||
// As a result all the calls to foo() will be routed to wrap_foo() at runtime.
|
||||
#define INTERPOSER(func_name) __attribute__((used)) \
|
||||
const interpose_substitution substitution_##func_name[] \
|
||||
__attribute__((section("__DATA, __interpose"))) = { \
|
||||
{ reinterpret_cast<const uptr>(WRAP(func_name)), \
|
||||
reinterpret_cast<const uptr>(func_name) } \
|
||||
}
|
||||
|
||||
// For a function foo() and a wrapper function bar() create a global pair
|
||||
// of pointers { bar, foo } in the __DATA,__interpose section.
|
||||
// As a result all the calls to foo() will be routed to bar() at runtime.
|
||||
#define INTERPOSER_2(func_name, wrapper_name) __attribute__((used)) \
|
||||
const interpose_substitution substitution_##func_name[] \
|
||||
__attribute__((section("__DATA, __interpose"))) = { \
|
||||
{ reinterpret_cast<const uptr>(wrapper_name), \
|
||||
reinterpret_cast<const uptr>(func_name) } \
|
||||
}
|
||||
|
||||
# define WRAP(x) wrap_##x
|
||||
# define WRAPPER_NAME(x) "wrap_"#x
|
||||
# define INTERCEPTOR_ATTRIBUTE
|
||||
# define DECLARE_WRAPPER(ret_type, func, ...)
|
||||
|
||||
#elif SANITIZER_WINDOWS
|
||||
# define WRAP(x) __asan_wrap_##x
|
||||
# define WRAPPER_NAME(x) "__asan_wrap_"#x
|
||||
# define INTERCEPTOR_ATTRIBUTE __declspec(dllexport)
|
||||
# define DECLARE_WRAPPER(ret_type, func, ...) \
|
||||
extern "C" ret_type func(__VA_ARGS__);
|
||||
# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \
|
||||
extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__);
|
||||
#elif SANITIZER_RTEMS
|
||||
# define WRAP(x) x
|
||||
# define WRAPPER_NAME(x) #x
|
||||
# define INTERCEPTOR_ATTRIBUTE
|
||||
# define DECLARE_WRAPPER(ret_type, func, ...)
|
||||
#elif SANITIZER_FREEBSD || SANITIZER_NETBSD
|
||||
# define WRAP(x) __interceptor_ ## x
|
||||
# define WRAPPER_NAME(x) "__interceptor_" #x
|
||||
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
|
||||
// FreeBSD's dynamic linker (incompliantly) gives non-weak symbols higher
|
||||
// priority than weak ones so weak aliases won't work for indirect calls
|
||||
// in position-independent (-fPIC / -fPIE) mode.
|
||||
# define DECLARE_WRAPPER(ret_type, func, ...) \
|
||||
extern "C" ret_type func(__VA_ARGS__) \
|
||||
__attribute__((alias("__interceptor_" #func), visibility("default")));
|
||||
#elif !SANITIZER_FUCHSIA
|
||||
# define WRAP(x) __interceptor_ ## x
|
||||
# define WRAPPER_NAME(x) "__interceptor_" #x
|
||||
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
|
||||
# define DECLARE_WRAPPER(ret_type, func, ...) \
|
||||
extern "C" ret_type func(__VA_ARGS__) \
|
||||
__attribute__((weak, alias("__interceptor_" #func), visibility("default")));
|
||||
#endif
|
||||
|
||||
#if SANITIZER_FUCHSIA
|
||||
// There is no general interception at all on Fuchsia.
|
||||
// Sanitizer runtimes just define functions directly to preempt them,
|
||||
// and have bespoke ways to access the underlying libc functions.
|
||||
# include <zircon/sanitizer.h>
|
||||
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
|
||||
# define REAL(x) __unsanitized_##x
|
||||
# define DECLARE_REAL(ret_type, func, ...)
|
||||
#elif SANITIZER_RTEMS
|
||||
# define REAL(x) __real_ ## x
|
||||
# define DECLARE_REAL(ret_type, func, ...) \
|
||||
extern "C" ret_type REAL(func)(__VA_ARGS__);
|
||||
#elif !SANITIZER_MAC
|
||||
# define PTR_TO_REAL(x) real_##x
|
||||
# define REAL(x) __interception::PTR_TO_REAL(x)
|
||||
# define FUNC_TYPE(x) x##_type
|
||||
|
||||
# define DECLARE_REAL(ret_type, func, ...) \
|
||||
typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
|
||||
namespace __interception { \
|
||||
extern FUNC_TYPE(func) PTR_TO_REAL(func); \
|
||||
}
|
||||
# define ASSIGN_REAL(dst, src) REAL(dst) = REAL(src)
|
||||
#else // SANITIZER_MAC
|
||||
# define REAL(x) x
|
||||
# define DECLARE_REAL(ret_type, func, ...) \
|
||||
extern "C" ret_type func(__VA_ARGS__);
|
||||
# define ASSIGN_REAL(x, y)
|
||||
#endif // SANITIZER_MAC
|
||||
|
||||
#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \
|
||||
DECLARE_REAL(ret_type, func, __VA_ARGS__) \
|
||||
extern "C" ret_type WRAP(func)(__VA_ARGS__);
|
||||
// Declare an interceptor and its wrapper defined in a different translation
|
||||
// unit (ex. asm).
|
||||
# define DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(ret_type, func, ...) \
|
||||
extern "C" ret_type WRAP(func)(__VA_ARGS__); \
|
||||
extern "C" ret_type func(__VA_ARGS__);
|
||||
#else
|
||||
# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...)
|
||||
# define DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(ret_type, func, ...)
|
||||
#endif
|
||||
|
||||
// Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR
|
||||
// macros does its job. In exceptional cases you may need to call REAL(foo)
|
||||
// without defining INTERCEPTOR(..., foo, ...). For example, if you override
|
||||
// foo with an interceptor for other function.
|
||||
#if !SANITIZER_MAC && !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
# define DEFINE_REAL(ret_type, func, ...) \
|
||||
typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
|
||||
namespace __interception { \
|
||||
FUNC_TYPE(func) PTR_TO_REAL(func); \
|
||||
}
|
||||
#else
|
||||
# define DEFINE_REAL(ret_type, func, ...)
|
||||
#endif
|
||||
|
||||
#if SANITIZER_FUCHSIA
|
||||
|
||||
// We need to define the __interceptor_func name just to get
|
||||
// sanitizer_common/scripts/gen_dynamic_list.py to export func.
|
||||
// But we don't need to export __interceptor_func to get that.
|
||||
#define INTERCEPTOR(ret_type, func, ...) \
|
||||
extern "C"[[ gnu::alias(#func), gnu::visibility("hidden") ]] ret_type \
|
||||
__interceptor_##func(__VA_ARGS__); \
|
||||
extern "C" INTERCEPTOR_ATTRIBUTE ret_type func(__VA_ARGS__)
|
||||
|
||||
#elif !SANITIZER_MAC
|
||||
|
||||
#define INTERCEPTOR(ret_type, func, ...) \
|
||||
DEFINE_REAL(ret_type, func, __VA_ARGS__) \
|
||||
DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \
|
||||
extern "C" \
|
||||
INTERCEPTOR_ATTRIBUTE \
|
||||
ret_type WRAP(func)(__VA_ARGS__)
|
||||
|
||||
// We don't need INTERCEPTOR_WITH_SUFFIX on non-Darwin for now.
|
||||
#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \
|
||||
INTERCEPTOR(ret_type, func, __VA_ARGS__)
|
||||
|
||||
#else // SANITIZER_MAC
|
||||
|
||||
#define INTERCEPTOR_ZZZ(suffix, ret_type, func, ...) \
|
||||
extern "C" ret_type func(__VA_ARGS__) suffix; \
|
||||
extern "C" ret_type WRAP(func)(__VA_ARGS__); \
|
||||
INTERPOSER(func); \
|
||||
extern "C" INTERCEPTOR_ATTRIBUTE ret_type WRAP(func)(__VA_ARGS__)
|
||||
|
||||
#define INTERCEPTOR(ret_type, func, ...) \
|
||||
INTERCEPTOR_ZZZ(/*no symbol variants*/, ret_type, func, __VA_ARGS__)
|
||||
|
||||
#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \
|
||||
INTERCEPTOR_ZZZ(__DARWIN_ALIAS_C(func), ret_type, func, __VA_ARGS__)
|
||||
|
||||
// Override |overridee| with |overrider|.
|
||||
#define OVERRIDE_FUNCTION(overridee, overrider) \
|
||||
INTERPOSER_2(overridee, WRAP(overrider))
|
||||
#endif
|
||||
|
||||
#if SANITIZER_WINDOWS
|
||||
# define INTERCEPTOR_WINAPI(ret_type, func, ...) \
|
||||
typedef ret_type (__stdcall *FUNC_TYPE(func))(__VA_ARGS__); \
|
||||
namespace __interception { \
|
||||
FUNC_TYPE(func) PTR_TO_REAL(func); \
|
||||
} \
|
||||
extern "C" \
|
||||
INTERCEPTOR_ATTRIBUTE \
|
||||
ret_type __stdcall WRAP(func)(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
// ISO C++ forbids casting between pointer-to-function and pointer-to-object,
|
||||
// so we use casting via an integral type __interception::uptr,
|
||||
// assuming that system is POSIX-compliant. Using other hacks seem
|
||||
// challenging, as we don't even pass function type to
|
||||
// INTERCEPT_FUNCTION macro, only its name.
|
||||
namespace __interception {
|
||||
#if defined(_WIN64)
|
||||
typedef unsigned long long uptr;
|
||||
#else
|
||||
typedef unsigned long uptr;
|
||||
#endif // _WIN64
|
||||
} // namespace __interception
|
||||
|
||||
#define INCLUDED_FROM_INTERCEPTION_LIB
|
||||
|
||||
#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \
|
||||
SANITIZER_OPENBSD || SANITIZER_SOLARIS
|
||||
|
||||
# include "interception_linux.h"
|
||||
# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func)
|
||||
# define INTERCEPT_FUNCTION_VER(func, symver) \
|
||||
INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver)
|
||||
#elif SANITIZER_MAC
|
||||
# include "interception_mac.h"
|
||||
# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func)
|
||||
# define INTERCEPT_FUNCTION_VER(func, symver) \
|
||||
INTERCEPT_FUNCTION_VER_MAC(func, symver)
|
||||
#elif SANITIZER_WINDOWS
|
||||
# include "interception_win.h"
|
||||
# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_WIN(func)
|
||||
# define INTERCEPT_FUNCTION_VER(func, symver) \
|
||||
INTERCEPT_FUNCTION_VER_WIN(func, symver)
|
||||
#endif
|
||||
|
||||
#undef INCLUDED_FROM_INTERCEPTION_LIB
|
||||
|
||||
#endif // INTERCEPTION_H
|
||||
53
lib/tsan/interception/interception_linux.h
Normal file
53
lib/tsan/interception/interception_linux.h
Normal file
@@ -0,0 +1,53 @@
|
||||
//===-- interception_linux.h ------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Linux-specific interception methods.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \
|
||||
SANITIZER_OPENBSD || SANITIZER_SOLARIS
|
||||
|
||||
#if !defined(INCLUDED_FROM_INTERCEPTION_LIB)
|
||||
# error "interception_linux.h should be included from interception library only"
|
||||
#endif
|
||||
|
||||
#ifndef INTERCEPTION_LINUX_H
|
||||
#define INTERCEPTION_LINUX_H
|
||||
|
||||
namespace __interception {
|
||||
bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func,
|
||||
uptr wrapper);
|
||||
bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real,
|
||||
uptr func, uptr wrapper);
|
||||
} // namespace __interception
|
||||
|
||||
#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \
|
||||
::__interception::InterceptFunction( \
|
||||
#func, \
|
||||
(::__interception::uptr *) & REAL(func), \
|
||||
(::__interception::uptr) & (func), \
|
||||
(::__interception::uptr) & WRAP(func))
|
||||
|
||||
// Android, Solaris and OpenBSD do not have dlvsym
|
||||
#if !SANITIZER_ANDROID && !SANITIZER_SOLARIS && !SANITIZER_OPENBSD
|
||||
#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
|
||||
::__interception::InterceptFunction( \
|
||||
#func, symver, \
|
||||
(::__interception::uptr *) & REAL(func), \
|
||||
(::__interception::uptr) & (func), \
|
||||
(::__interception::uptr) & WRAP(func))
|
||||
#else
|
||||
#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
|
||||
INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func)
|
||||
#endif // !SANITIZER_ANDROID && !SANITIZER_SOLARIS
|
||||
|
||||
#endif // INTERCEPTION_LINUX_H
|
||||
#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD ||
|
||||
// SANITIZER_OPENBSD || SANITIZER_SOLARIS
|
||||
27
lib/tsan/interception/interception_mac.h
Normal file
27
lib/tsan/interception/interception_mac.h
Normal file
@@ -0,0 +1,27 @@
|
||||
//===-- interception_mac.h --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Mac-specific interception methods.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if SANITIZER_MAC
|
||||
|
||||
#if !defined(INCLUDED_FROM_INTERCEPTION_LIB)
|
||||
# error "interception_mac.h should be included from interception.h only"
|
||||
#endif
|
||||
|
||||
#ifndef INTERCEPTION_MAC_H
|
||||
#define INTERCEPTION_MAC_H
|
||||
|
||||
#define INTERCEPT_FUNCTION_MAC(func)
|
||||
#define INTERCEPT_FUNCTION_VER_MAC(func, symver)
|
||||
|
||||
#endif // INTERCEPTION_MAC_H
|
||||
#endif // SANITIZER_MAC
|
||||
83
lib/tsan/interception/interception_win.h
Normal file
83
lib/tsan/interception/interception_win.h
Normal file
@@ -0,0 +1,83 @@
|
||||
//===-- interception_linux.h ------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Windows-specific interception methods.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if SANITIZER_WINDOWS
|
||||
|
||||
#if !defined(INCLUDED_FROM_INTERCEPTION_LIB)
|
||||
# error "interception_win.h should be included from interception library only"
|
||||
#endif
|
||||
|
||||
#ifndef INTERCEPTION_WIN_H
|
||||
#define INTERCEPTION_WIN_H
|
||||
|
||||
namespace __interception {
|
||||
// All the functions in the OverrideFunction() family return true on success,
|
||||
// false on failure (including "couldn't find the function").
|
||||
|
||||
// Overrides a function by its address.
|
||||
bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func = 0);
|
||||
|
||||
// Overrides a function in a system DLL or DLL CRT by its exported name.
|
||||
bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func = 0);
|
||||
|
||||
// Windows-only replacement for GetProcAddress. Useful for some sanitizers.
|
||||
uptr InternalGetProcAddress(void *module, const char *func_name);
|
||||
|
||||
// Overrides a function only when it is called from a specific DLL. For example,
|
||||
// this is used to override calls to HeapAlloc/HeapFree from ucrtbase without
|
||||
// affecting other third party libraries.
|
||||
bool OverrideImportedFunction(const char *module_to_patch,
|
||||
const char *imported_module,
|
||||
const char *function_name, uptr new_function,
|
||||
uptr *orig_old_func);
|
||||
|
||||
#if !SANITIZER_WINDOWS64
|
||||
// Exposed for unittests
|
||||
bool OverrideFunctionWithDetour(
|
||||
uptr old_func, uptr new_func, uptr *orig_old_func);
|
||||
#endif
|
||||
|
||||
// Exposed for unittests
|
||||
bool OverrideFunctionWithRedirectJump(
|
||||
uptr old_func, uptr new_func, uptr *orig_old_func);
|
||||
bool OverrideFunctionWithHotPatch(
|
||||
uptr old_func, uptr new_func, uptr *orig_old_func);
|
||||
bool OverrideFunctionWithTrampoline(
|
||||
uptr old_func, uptr new_func, uptr *orig_old_func);
|
||||
|
||||
// Exposed for unittests
|
||||
void TestOnlyReleaseTrampolineRegions();
|
||||
|
||||
} // namespace __interception
|
||||
|
||||
#if defined(INTERCEPTION_DYNAMIC_CRT)
|
||||
#define INTERCEPT_FUNCTION_WIN(func) \
|
||||
::__interception::OverrideFunction(#func, \
|
||||
(::__interception::uptr)WRAP(func), \
|
||||
(::__interception::uptr *)&REAL(func))
|
||||
#else
|
||||
#define INTERCEPT_FUNCTION_WIN(func) \
|
||||
::__interception::OverrideFunction((::__interception::uptr)func, \
|
||||
(::__interception::uptr)WRAP(func), \
|
||||
(::__interception::uptr *)&REAL(func))
|
||||
#endif
|
||||
|
||||
#define INTERCEPT_FUNCTION_VER_WIN(func, symver) INTERCEPT_FUNCTION_WIN(func)
|
||||
|
||||
#define INTERCEPT_FUNCTION_DLLIMPORT(user_dll, provider_dll, func) \
|
||||
::__interception::OverrideImportedFunction( \
|
||||
user_dll, provider_dll, #func, (::__interception::uptr)WRAP(func), \
|
||||
(::__interception::uptr *)&REAL(func))
|
||||
|
||||
#endif // INTERCEPTION_WIN_H
|
||||
#endif // SANITIZER_WINDOWS
|
||||
39
lib/tsan/sanitizer_common/sancov_flags.h
Normal file
39
lib/tsan/sanitizer_common/sancov_flags.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//===-- sancov_flags.h ------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Sanitizer Coverage runtime flags.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANCOV_FLAGS_H
|
||||
#define SANCOV_FLAGS_H
|
||||
|
||||
#include "sanitizer_flag_parser.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sancov {
|
||||
|
||||
struct SancovFlags {
|
||||
#define SANCOV_FLAG(Type, Name, DefaultValue, Description) Type Name;
|
||||
#include "sancov_flags.inc"
|
||||
#undef SANCOV_FLAG
|
||||
|
||||
void SetDefaults();
|
||||
};
|
||||
|
||||
extern SancovFlags sancov_flags_dont_use_directly;
|
||||
|
||||
inline SancovFlags* sancov_flags() { return &sancov_flags_dont_use_directly; }
|
||||
|
||||
void InitializeSancovFlags();
|
||||
|
||||
} // namespace __sancov
|
||||
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char*
|
||||
__sancov_default_options();
|
||||
|
||||
#endif
|
||||
20
lib/tsan/sanitizer_common/sancov_flags.inc
Normal file
20
lib/tsan/sanitizer_common/sancov_flags.inc
Normal file
@@ -0,0 +1,20 @@
|
||||
//===-- sancov_flags.inc ----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Sanitizer Coverage runtime flags.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANCOV_FLAG
|
||||
#error "Defnine SANCOV_FLAG prior to including this file!"
|
||||
#endif
|
||||
|
||||
SANCOV_FLAG(bool, symbolize, true,
|
||||
"If set, converage information will be symbolized by sancov tool "
|
||||
"after dumping.")
|
||||
|
||||
SANCOV_FLAG(bool, help, false, "Print flags help.")
|
||||
353
lib/tsan/sanitizer_common/sanitizer_addrhashmap.h
Normal file
353
lib/tsan/sanitizer_common/sanitizer_addrhashmap.h
Normal file
@@ -0,0 +1,353 @@
|
||||
//===-- sanitizer_addrhashmap.h ---------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Concurrent uptr->T hashmap.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ADDRHASHMAP_H
|
||||
#define SANITIZER_ADDRHASHMAP_H
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
#include "sanitizer_atomic.h"
|
||||
#include "sanitizer_allocator_internal.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Concurrent uptr->T hashmap.
|
||||
// T must be a POD type, kSize is preferably a prime but can be any number.
|
||||
// Usage example:
|
||||
//
|
||||
// typedef AddrHashMap<uptr, 11> Map;
|
||||
// Map m;
|
||||
// {
|
||||
// Map::Handle h(&m, addr);
|
||||
// use h.operator->() to access the data
|
||||
// if h.created() then the element was just created, and the current thread
|
||||
// has exclusive access to it
|
||||
// otherwise the current thread has only read access to the data
|
||||
// }
|
||||
// {
|
||||
// Map::Handle h(&m, addr, true);
|
||||
// this will remove the data from the map in Handle dtor
|
||||
// the current thread has exclusive access to the data
|
||||
// if !h.exists() then the element never existed
|
||||
// }
|
||||
template<typename T, uptr kSize>
|
||||
class AddrHashMap {
|
||||
private:
|
||||
struct Cell {
|
||||
atomic_uintptr_t addr;
|
||||
T val;
|
||||
};
|
||||
|
||||
struct AddBucket {
|
||||
uptr cap;
|
||||
uptr size;
|
||||
Cell cells[1]; // variable len
|
||||
};
|
||||
|
||||
static const uptr kBucketSize = 3;
|
||||
|
||||
struct Bucket {
|
||||
RWMutex mtx;
|
||||
atomic_uintptr_t add;
|
||||
Cell cells[kBucketSize];
|
||||
};
|
||||
|
||||
public:
|
||||
AddrHashMap();
|
||||
|
||||
class Handle {
|
||||
public:
|
||||
Handle(AddrHashMap<T, kSize> *map, uptr addr);
|
||||
Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove);
|
||||
Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove, bool create);
|
||||
|
||||
~Handle();
|
||||
T *operator->();
|
||||
T &operator*();
|
||||
const T &operator*() const;
|
||||
bool created() const;
|
||||
bool exists() const;
|
||||
|
||||
private:
|
||||
friend AddrHashMap<T, kSize>;
|
||||
AddrHashMap<T, kSize> *map_;
|
||||
Bucket *bucket_;
|
||||
Cell *cell_;
|
||||
uptr addr_;
|
||||
uptr addidx_;
|
||||
bool created_;
|
||||
bool remove_;
|
||||
bool create_;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class Handle;
|
||||
Bucket *table_;
|
||||
|
||||
void acquire(Handle *h);
|
||||
void release(Handle *h);
|
||||
uptr calcHash(uptr addr);
|
||||
};
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr) {
|
||||
map_ = map;
|
||||
addr_ = addr;
|
||||
remove_ = false;
|
||||
create_ = true;
|
||||
map_->acquire(this);
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,
|
||||
bool remove) {
|
||||
map_ = map;
|
||||
addr_ = addr;
|
||||
remove_ = remove;
|
||||
create_ = true;
|
||||
map_->acquire(this);
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,
|
||||
bool remove, bool create) {
|
||||
map_ = map;
|
||||
addr_ = addr;
|
||||
remove_ = remove;
|
||||
create_ = create;
|
||||
map_->acquire(this);
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
AddrHashMap<T, kSize>::Handle::~Handle() {
|
||||
map_->release(this);
|
||||
}
|
||||
|
||||
template <typename T, uptr kSize>
|
||||
T *AddrHashMap<T, kSize>::Handle::operator->() {
|
||||
return &cell_->val;
|
||||
}
|
||||
|
||||
template <typename T, uptr kSize>
|
||||
const T &AddrHashMap<T, kSize>::Handle::operator*() const {
|
||||
return cell_->val;
|
||||
}
|
||||
|
||||
template <typename T, uptr kSize>
|
||||
T &AddrHashMap<T, kSize>::Handle::operator*() {
|
||||
return cell_->val;
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
bool AddrHashMap<T, kSize>::Handle::created() const {
|
||||
return created_;
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
bool AddrHashMap<T, kSize>::Handle::exists() const {
|
||||
return cell_ != nullptr;
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
AddrHashMap<T, kSize>::AddrHashMap() {
|
||||
table_ = (Bucket*)MmapOrDie(kSize * sizeof(table_[0]), "AddrHashMap");
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
void AddrHashMap<T, kSize>::acquire(Handle *h) {
|
||||
uptr addr = h->addr_;
|
||||
uptr hash = calcHash(addr);
|
||||
Bucket *b = &table_[hash];
|
||||
|
||||
h->created_ = false;
|
||||
h->addidx_ = -1U;
|
||||
h->bucket_ = b;
|
||||
h->cell_ = nullptr;
|
||||
|
||||
// If we want to remove the element, we need exclusive access to the bucket,
|
||||
// so skip the lock-free phase.
|
||||
if (h->remove_)
|
||||
goto locked;
|
||||
|
||||
retry:
|
||||
// First try to find an existing element w/o read mutex.
|
||||
CHECK(!h->remove_);
|
||||
// Check the embed cells.
|
||||
for (uptr i = 0; i < kBucketSize; i++) {
|
||||
Cell *c = &b->cells[i];
|
||||
uptr addr1 = atomic_load(&c->addr, memory_order_acquire);
|
||||
if (addr1 == addr) {
|
||||
h->cell_ = c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the add cells with read lock.
|
||||
if (atomic_load(&b->add, memory_order_relaxed)) {
|
||||
b->mtx.ReadLock();
|
||||
AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
|
||||
for (uptr i = 0; i < add->size; i++) {
|
||||
Cell *c = &add->cells[i];
|
||||
uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
|
||||
if (addr1 == addr) {
|
||||
h->addidx_ = i;
|
||||
h->cell_ = c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
b->mtx.ReadUnlock();
|
||||
}
|
||||
|
||||
locked:
|
||||
// Re-check existence under write lock.
|
||||
// Embed cells.
|
||||
b->mtx.Lock();
|
||||
for (uptr i = 0; i < kBucketSize; i++) {
|
||||
Cell *c = &b->cells[i];
|
||||
uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
|
||||
if (addr1 == addr) {
|
||||
if (h->remove_) {
|
||||
h->cell_ = c;
|
||||
return;
|
||||
}
|
||||
b->mtx.Unlock();
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
// Add cells.
|
||||
AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
|
||||
if (add) {
|
||||
for (uptr i = 0; i < add->size; i++) {
|
||||
Cell *c = &add->cells[i];
|
||||
uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
|
||||
if (addr1 == addr) {
|
||||
if (h->remove_) {
|
||||
h->addidx_ = i;
|
||||
h->cell_ = c;
|
||||
return;
|
||||
}
|
||||
b->mtx.Unlock();
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The element does not exist, no need to create it if we want to remove.
|
||||
if (h->remove_ || !h->create_) {
|
||||
b->mtx.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
// Now try to create it under the mutex.
|
||||
h->created_ = true;
|
||||
// See if we have a free embed cell.
|
||||
for (uptr i = 0; i < kBucketSize; i++) {
|
||||
Cell *c = &b->cells[i];
|
||||
uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
|
||||
if (addr1 == 0) {
|
||||
h->cell_ = c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Store in the add cells.
|
||||
if (!add) {
|
||||
// Allocate a new add array.
|
||||
const uptr kInitSize = 64;
|
||||
add = (AddBucket*)InternalAlloc(kInitSize);
|
||||
internal_memset(add, 0, kInitSize);
|
||||
add->cap = (kInitSize - sizeof(*add)) / sizeof(add->cells[0]) + 1;
|
||||
add->size = 0;
|
||||
atomic_store(&b->add, (uptr)add, memory_order_relaxed);
|
||||
}
|
||||
if (add->size == add->cap) {
|
||||
// Grow existing add array.
|
||||
uptr oldsize = sizeof(*add) + (add->cap - 1) * sizeof(add->cells[0]);
|
||||
uptr newsize = oldsize * 2;
|
||||
AddBucket *add1 = (AddBucket*)InternalAlloc(newsize);
|
||||
internal_memset(add1, 0, newsize);
|
||||
add1->cap = (newsize - sizeof(*add)) / sizeof(add->cells[0]) + 1;
|
||||
add1->size = add->size;
|
||||
internal_memcpy(add1->cells, add->cells, add->size * sizeof(add->cells[0]));
|
||||
InternalFree(add);
|
||||
atomic_store(&b->add, (uptr)add1, memory_order_relaxed);
|
||||
add = add1;
|
||||
}
|
||||
// Store.
|
||||
uptr i = add->size++;
|
||||
Cell *c = &add->cells[i];
|
||||
CHECK_EQ(atomic_load(&c->addr, memory_order_relaxed), 0);
|
||||
h->addidx_ = i;
|
||||
h->cell_ = c;
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
void AddrHashMap<T, kSize>::release(Handle *h) {
|
||||
if (!h->cell_)
|
||||
return;
|
||||
Bucket *b = h->bucket_;
|
||||
Cell *c = h->cell_;
|
||||
uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
|
||||
if (h->created_) {
|
||||
// Denote completion of insertion.
|
||||
CHECK_EQ(addr1, 0);
|
||||
// After the following store, the element becomes available
|
||||
// for lock-free reads.
|
||||
atomic_store(&c->addr, h->addr_, memory_order_release);
|
||||
b->mtx.Unlock();
|
||||
} else if (h->remove_) {
|
||||
// Denote that the cell is empty now.
|
||||
CHECK_EQ(addr1, h->addr_);
|
||||
atomic_store(&c->addr, 0, memory_order_release);
|
||||
// See if we need to compact the bucket.
|
||||
AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
|
||||
if (h->addidx_ == -1U) {
|
||||
// Removed from embed array, move an add element into the freed cell.
|
||||
if (add && add->size != 0) {
|
||||
uptr last = --add->size;
|
||||
Cell *c1 = &add->cells[last];
|
||||
c->val = c1->val;
|
||||
uptr addr1 = atomic_load(&c1->addr, memory_order_relaxed);
|
||||
atomic_store(&c->addr, addr1, memory_order_release);
|
||||
atomic_store(&c1->addr, 0, memory_order_release);
|
||||
}
|
||||
} else {
|
||||
// Removed from add array, compact it.
|
||||
uptr last = --add->size;
|
||||
Cell *c1 = &add->cells[last];
|
||||
if (c != c1) {
|
||||
*c = *c1;
|
||||
atomic_store(&c1->addr, 0, memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
if (add && add->size == 0) {
|
||||
// FIXME(dvyukov): free add?
|
||||
}
|
||||
b->mtx.Unlock();
|
||||
} else {
|
||||
CHECK_EQ(addr1, h->addr_);
|
||||
if (h->addidx_ != -1U)
|
||||
b->mtx.ReadUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
uptr AddrHashMap<T, kSize>::calcHash(uptr addr) {
|
||||
addr += addr << 10;
|
||||
addr ^= addr >> 6;
|
||||
return addr % kSize;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_ADDRHASHMAP_H
|
||||
267
lib/tsan/sanitizer_common/sanitizer_allocator.cpp
Normal file
267
lib/tsan/sanitizer_common/sanitizer_allocator.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
//===-- sanitizer_allocator.cpp -------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries.
|
||||
// This allocator is used inside run-times.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_allocator.h"
|
||||
|
||||
#include "sanitizer_allocator_checks.h"
|
||||
#include "sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_atomic.h"
|
||||
#include "sanitizer_common.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Default allocator names.
|
||||
const char *PrimaryAllocatorName = "SizeClassAllocator";
|
||||
const char *SecondaryAllocatorName = "LargeMmapAllocator";
|
||||
|
||||
// ThreadSanitizer for Go uses libc malloc/free.
|
||||
#if defined(SANITIZER_USE_MALLOC)
|
||||
# if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
extern "C" void *__libc_malloc(uptr size);
|
||||
# if !SANITIZER_GO
|
||||
extern "C" void *__libc_memalign(uptr alignment, uptr size);
|
||||
# endif
|
||||
extern "C" void *__libc_realloc(void *ptr, uptr size);
|
||||
extern "C" void __libc_free(void *ptr);
|
||||
# else
|
||||
# include <stdlib.h>
|
||||
# define __libc_malloc malloc
|
||||
# if !SANITIZER_GO
|
||||
static void *__libc_memalign(uptr alignment, uptr size) {
|
||||
void *p;
|
||||
uptr error = posix_memalign(&p, alignment, size);
|
||||
if (error) return nullptr;
|
||||
return p;
|
||||
}
|
||||
# endif
|
||||
# define __libc_realloc realloc
|
||||
# define __libc_free free
|
||||
# endif
|
||||
|
||||
static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache,
|
||||
uptr alignment) {
|
||||
(void)cache;
|
||||
#if !SANITIZER_GO
|
||||
if (alignment == 0)
|
||||
return __libc_malloc(size);
|
||||
else
|
||||
return __libc_memalign(alignment, size);
|
||||
#else
|
||||
// Windows does not provide __libc_memalign/posix_memalign. It provides
|
||||
// __aligned_malloc, but the allocated blocks can't be passed to free,
|
||||
// they need to be passed to __aligned_free. InternalAlloc interface does
|
||||
// not account for such requirement. Alignemnt does not seem to be used
|
||||
// anywhere in runtime, so just call __libc_malloc for now.
|
||||
DCHECK_EQ(alignment, 0);
|
||||
return __libc_malloc(size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *RawInternalRealloc(void *ptr, uptr size,
|
||||
InternalAllocatorCache *cache) {
|
||||
(void)cache;
|
||||
return __libc_realloc(ptr, size);
|
||||
}
|
||||
|
||||
static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
|
||||
(void)cache;
|
||||
__libc_free(ptr);
|
||||
}
|
||||
|
||||
InternalAllocator *internal_allocator() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
|
||||
|
||||
static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)];
|
||||
static atomic_uint8_t internal_allocator_initialized;
|
||||
static StaticSpinMutex internal_alloc_init_mu;
|
||||
|
||||
static InternalAllocatorCache internal_allocator_cache;
|
||||
static StaticSpinMutex internal_allocator_cache_mu;
|
||||
|
||||
InternalAllocator *internal_allocator() {
|
||||
InternalAllocator *internal_allocator_instance =
|
||||
reinterpret_cast<InternalAllocator *>(&internal_alloc_placeholder);
|
||||
if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) {
|
||||
SpinMutexLock l(&internal_alloc_init_mu);
|
||||
if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) ==
|
||||
0) {
|
||||
internal_allocator_instance->Init(kReleaseToOSIntervalNever);
|
||||
atomic_store(&internal_allocator_initialized, 1, memory_order_release);
|
||||
}
|
||||
}
|
||||
return internal_allocator_instance;
|
||||
}
|
||||
|
||||
static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache,
|
||||
uptr alignment) {
|
||||
if (alignment == 0) alignment = 8;
|
||||
if (cache == 0) {
|
||||
SpinMutexLock l(&internal_allocator_cache_mu);
|
||||
return internal_allocator()->Allocate(&internal_allocator_cache, size,
|
||||
alignment);
|
||||
}
|
||||
return internal_allocator()->Allocate(cache, size, alignment);
|
||||
}
|
||||
|
||||
static void *RawInternalRealloc(void *ptr, uptr size,
|
||||
InternalAllocatorCache *cache) {
|
||||
uptr alignment = 8;
|
||||
if (cache == 0) {
|
||||
SpinMutexLock l(&internal_allocator_cache_mu);
|
||||
return internal_allocator()->Reallocate(&internal_allocator_cache, ptr,
|
||||
size, alignment);
|
||||
}
|
||||
return internal_allocator()->Reallocate(cache, ptr, size, alignment);
|
||||
}
|
||||
|
||||
static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
|
||||
if (!cache) {
|
||||
SpinMutexLock l(&internal_allocator_cache_mu);
|
||||
return internal_allocator()->Deallocate(&internal_allocator_cache, ptr);
|
||||
}
|
||||
internal_allocator()->Deallocate(cache, ptr);
|
||||
}
|
||||
|
||||
#endif // SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
|
||||
|
||||
const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull;
|
||||
|
||||
static void NORETURN ReportInternalAllocatorOutOfMemory(uptr requested_size) {
|
||||
SetAllocatorOutOfMemory();
|
||||
Report("FATAL: %s: internal allocator is out of memory trying to allocate "
|
||||
"0x%zx bytes\n", SanitizerToolName, requested_size);
|
||||
Die();
|
||||
}
|
||||
|
||||
void *InternalAlloc(uptr size, InternalAllocatorCache *cache, uptr alignment) {
|
||||
if (size + sizeof(u64) < size)
|
||||
return nullptr;
|
||||
void *p = RawInternalAlloc(size + sizeof(u64), cache, alignment);
|
||||
if (UNLIKELY(!p))
|
||||
ReportInternalAllocatorOutOfMemory(size + sizeof(u64));
|
||||
((u64*)p)[0] = kBlockMagic;
|
||||
return (char*)p + sizeof(u64);
|
||||
}
|
||||
|
||||
void *InternalRealloc(void *addr, uptr size, InternalAllocatorCache *cache) {
|
||||
if (!addr)
|
||||
return InternalAlloc(size, cache);
|
||||
if (size + sizeof(u64) < size)
|
||||
return nullptr;
|
||||
addr = (char*)addr - sizeof(u64);
|
||||
size = size + sizeof(u64);
|
||||
CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
|
||||
void *p = RawInternalRealloc(addr, size, cache);
|
||||
if (UNLIKELY(!p))
|
||||
ReportInternalAllocatorOutOfMemory(size);
|
||||
return (char*)p + sizeof(u64);
|
||||
}
|
||||
|
||||
void *InternalReallocArray(void *addr, uptr count, uptr size,
|
||||
InternalAllocatorCache *cache) {
|
||||
if (UNLIKELY(CheckForCallocOverflow(count, size))) {
|
||||
Report(
|
||||
"FATAL: %s: reallocarray parameters overflow: count * size (%zd * %zd) "
|
||||
"cannot be represented in type size_t\n",
|
||||
SanitizerToolName, count, size);
|
||||
Die();
|
||||
}
|
||||
return InternalRealloc(addr, count * size, cache);
|
||||
}
|
||||
|
||||
void *InternalCalloc(uptr count, uptr size, InternalAllocatorCache *cache) {
|
||||
if (UNLIKELY(CheckForCallocOverflow(count, size))) {
|
||||
Report("FATAL: %s: calloc parameters overflow: count * size (%zd * %zd) "
|
||||
"cannot be represented in type size_t\n", SanitizerToolName, count,
|
||||
size);
|
||||
Die();
|
||||
}
|
||||
void *p = InternalAlloc(count * size, cache);
|
||||
if (LIKELY(p))
|
||||
internal_memset(p, 0, count * size);
|
||||
return p;
|
||||
}
|
||||
|
||||
void InternalFree(void *addr, InternalAllocatorCache *cache) {
|
||||
if (!addr)
|
||||
return;
|
||||
addr = (char*)addr - sizeof(u64);
|
||||
CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
|
||||
((u64*)addr)[0] = 0;
|
||||
RawInternalFree(addr, cache);
|
||||
}
|
||||
|
||||
// LowLevelAllocator
|
||||
constexpr uptr kLowLevelAllocatorDefaultAlignment = 8;
|
||||
static uptr low_level_alloc_min_alignment = kLowLevelAllocatorDefaultAlignment;
|
||||
static LowLevelAllocateCallback low_level_alloc_callback;
|
||||
|
||||
void *LowLevelAllocator::Allocate(uptr size) {
|
||||
// Align allocation size.
|
||||
size = RoundUpTo(size, low_level_alloc_min_alignment);
|
||||
if (allocated_end_ - allocated_current_ < (sptr)size) {
|
||||
uptr size_to_allocate = RoundUpTo(size, GetPageSizeCached());
|
||||
allocated_current_ =
|
||||
(char*)MmapOrDie(size_to_allocate, __func__);
|
||||
allocated_end_ = allocated_current_ + size_to_allocate;
|
||||
if (low_level_alloc_callback) {
|
||||
low_level_alloc_callback((uptr)allocated_current_,
|
||||
size_to_allocate);
|
||||
}
|
||||
}
|
||||
CHECK(allocated_end_ - allocated_current_ >= (sptr)size);
|
||||
void *res = allocated_current_;
|
||||
allocated_current_ += size;
|
||||
return res;
|
||||
}
|
||||
|
||||
void SetLowLevelAllocateMinAlignment(uptr alignment) {
|
||||
CHECK(IsPowerOfTwo(alignment));
|
||||
low_level_alloc_min_alignment = Max(alignment, low_level_alloc_min_alignment);
|
||||
}
|
||||
|
||||
void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) {
|
||||
low_level_alloc_callback = callback;
|
||||
}
|
||||
|
||||
// Allocator's OOM and other errors handling support.
|
||||
|
||||
static atomic_uint8_t allocator_out_of_memory = {0};
|
||||
static atomic_uint8_t allocator_may_return_null = {0};
|
||||
|
||||
bool IsAllocatorOutOfMemory() {
|
||||
return atomic_load_relaxed(&allocator_out_of_memory);
|
||||
}
|
||||
|
||||
void SetAllocatorOutOfMemory() {
|
||||
atomic_store_relaxed(&allocator_out_of_memory, 1);
|
||||
}
|
||||
|
||||
bool AllocatorMayReturnNull() {
|
||||
return atomic_load(&allocator_may_return_null, memory_order_relaxed);
|
||||
}
|
||||
|
||||
void SetAllocatorMayReturnNull(bool may_return_null) {
|
||||
atomic_store(&allocator_may_return_null, may_return_null,
|
||||
memory_order_relaxed);
|
||||
}
|
||||
|
||||
void PrintHintAllocatorCannotReturnNull() {
|
||||
Report("HINT: if you don't care about these errors you may set "
|
||||
"allocator_may_return_null=1\n");
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
81
lib/tsan/sanitizer_common/sanitizer_allocator.h
Normal file
81
lib/tsan/sanitizer_common/sanitizer_allocator.h
Normal file
@@ -0,0 +1,81 @@
|
||||
//===-- sanitizer_allocator.h -----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Specialized memory allocator for ThreadSanitizer, MemorySanitizer, etc.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ALLOCATOR_H
|
||||
#define SANITIZER_ALLOCATOR_H
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_lfstack.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_list.h"
|
||||
#include "sanitizer_local_address_space_view.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
#include "sanitizer_procmaps.h"
|
||||
#include "sanitizer_type_traits.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Allows the tools to name their allocations appropriately.
|
||||
extern const char *PrimaryAllocatorName;
|
||||
extern const char *SecondaryAllocatorName;
|
||||
|
||||
// Since flags are immutable and allocator behavior can be changed at runtime
|
||||
// (unit tests or ASan on Android are some examples), allocator_may_return_null
|
||||
// flag value is cached here and can be altered later.
|
||||
bool AllocatorMayReturnNull();
|
||||
void SetAllocatorMayReturnNull(bool may_return_null);
|
||||
|
||||
// Returns true if allocator detected OOM condition. Can be used to avoid memory
|
||||
// hungry operations.
|
||||
bool IsAllocatorOutOfMemory();
|
||||
// Should be called by a particular allocator when OOM is detected.
|
||||
void SetAllocatorOutOfMemory();
|
||||
|
||||
void PrintHintAllocatorCannotReturnNull();
|
||||
|
||||
// Allocators call these callbacks on mmap/munmap.
|
||||
struct NoOpMapUnmapCallback {
|
||||
void OnMap(uptr p, uptr size) const { }
|
||||
void OnUnmap(uptr p, uptr size) const { }
|
||||
};
|
||||
|
||||
// Callback type for iterating over chunks.
|
||||
typedef void (*ForEachChunkCallback)(uptr chunk, void *arg);
|
||||
|
||||
INLINE u32 Rand(u32 *state) { // ANSI C linear congruential PRNG.
|
||||
return (*state = *state * 1103515245 + 12345) >> 16;
|
||||
}
|
||||
|
||||
INLINE u32 RandN(u32 *state, u32 n) { return Rand(state) % n; } // [0, n)
|
||||
|
||||
template<typename T>
|
||||
INLINE void RandomShuffle(T *a, u32 n, u32 *rand_state) {
|
||||
if (n <= 1) return;
|
||||
u32 state = *rand_state;
|
||||
for (u32 i = n - 1; i > 0; i--)
|
||||
Swap(a[i], a[RandN(&state, i + 1)]);
|
||||
*rand_state = state;
|
||||
}
|
||||
|
||||
#include "sanitizer_allocator_size_class_map.h"
|
||||
#include "sanitizer_allocator_stats.h"
|
||||
#include "sanitizer_allocator_primary64.h"
|
||||
#include "sanitizer_allocator_bytemap.h"
|
||||
#include "sanitizer_allocator_primary32.h"
|
||||
#include "sanitizer_allocator_local_cache.h"
|
||||
#include "sanitizer_allocator_secondary.h"
|
||||
#include "sanitizer_allocator_combined.h"
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_ALLOCATOR_H
|
||||
107
lib/tsan/sanitizer_common/sanitizer_allocator_bytemap.h
Normal file
107
lib/tsan/sanitizer_common/sanitizer_allocator_bytemap.h
Normal file
@@ -0,0 +1,107 @@
|
||||
//===-- sanitizer_allocator_bytemap.h ---------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the Sanitizer Allocator.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_ALLOCATOR_H
|
||||
#error This file must be included inside sanitizer_allocator.h
|
||||
#endif
|
||||
|
||||
// Maps integers in rage [0, kSize) to u8 values.
|
||||
template <u64 kSize, typename AddressSpaceViewTy = LocalAddressSpaceView>
|
||||
class FlatByteMap {
|
||||
public:
|
||||
using AddressSpaceView = AddressSpaceViewTy;
|
||||
void Init() {
|
||||
internal_memset(map_, 0, sizeof(map_));
|
||||
}
|
||||
|
||||
void set(uptr idx, u8 val) {
|
||||
CHECK_LT(idx, kSize);
|
||||
CHECK_EQ(0U, map_[idx]);
|
||||
map_[idx] = val;
|
||||
}
|
||||
u8 operator[] (uptr idx) {
|
||||
CHECK_LT(idx, kSize);
|
||||
// FIXME: CHECK may be too expensive here.
|
||||
return map_[idx];
|
||||
}
|
||||
private:
|
||||
u8 map_[kSize];
|
||||
};
|
||||
|
||||
// TwoLevelByteMap maps integers in range [0, kSize1*kSize2) to u8 values.
|
||||
// It is implemented as a two-dimensional array: array of kSize1 pointers
|
||||
// to kSize2-byte arrays. The secondary arrays are mmaped on demand.
|
||||
// Each value is initially zero and can be set to something else only once.
|
||||
// Setting and getting values from multiple threads is safe w/o extra locking.
|
||||
template <u64 kSize1, u64 kSize2,
|
||||
typename AddressSpaceViewTy = LocalAddressSpaceView,
|
||||
class MapUnmapCallback = NoOpMapUnmapCallback>
|
||||
class TwoLevelByteMap {
|
||||
public:
|
||||
using AddressSpaceView = AddressSpaceViewTy;
|
||||
void Init() {
|
||||
internal_memset(map1_, 0, sizeof(map1_));
|
||||
mu_.Init();
|
||||
}
|
||||
|
||||
void TestOnlyUnmap() {
|
||||
for (uptr i = 0; i < kSize1; i++) {
|
||||
u8 *p = Get(i);
|
||||
if (!p) continue;
|
||||
MapUnmapCallback().OnUnmap(reinterpret_cast<uptr>(p), kSize2);
|
||||
UnmapOrDie(p, kSize2);
|
||||
}
|
||||
}
|
||||
|
||||
uptr size() const { return kSize1 * kSize2; }
|
||||
uptr size1() const { return kSize1; }
|
||||
uptr size2() const { return kSize2; }
|
||||
|
||||
void set(uptr idx, u8 val) {
|
||||
CHECK_LT(idx, kSize1 * kSize2);
|
||||
u8 *map2 = GetOrCreate(idx / kSize2);
|
||||
CHECK_EQ(0U, map2[idx % kSize2]);
|
||||
map2[idx % kSize2] = val;
|
||||
}
|
||||
|
||||
u8 operator[] (uptr idx) const {
|
||||
CHECK_LT(idx, kSize1 * kSize2);
|
||||
u8 *map2 = Get(idx / kSize2);
|
||||
if (!map2) return 0;
|
||||
auto value_ptr = AddressSpaceView::Load(&map2[idx % kSize2]);
|
||||
return *value_ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
u8 *Get(uptr idx) const {
|
||||
CHECK_LT(idx, kSize1);
|
||||
return reinterpret_cast<u8 *>(
|
||||
atomic_load(&map1_[idx], memory_order_acquire));
|
||||
}
|
||||
|
||||
u8 *GetOrCreate(uptr idx) {
|
||||
u8 *res = Get(idx);
|
||||
if (!res) {
|
||||
SpinMutexLock l(&mu_);
|
||||
if (!(res = Get(idx))) {
|
||||
res = (u8*)MmapOrDie(kSize2, "TwoLevelByteMap");
|
||||
MapUnmapCallback().OnMap(reinterpret_cast<uptr>(res), kSize2);
|
||||
atomic_store(&map1_[idx], reinterpret_cast<uptr>(res),
|
||||
memory_order_release);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
atomic_uintptr_t map1_[kSize1];
|
||||
StaticSpinMutex mu_;
|
||||
};
|
||||
|
||||
76
lib/tsan/sanitizer_common/sanitizer_allocator_checks.h
Normal file
76
lib/tsan/sanitizer_common/sanitizer_allocator_checks.h
Normal file
@@ -0,0 +1,76 @@
|
||||
//===-- sanitizer_allocator_checks.h ----------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Various checks shared between ThreadSanitizer, MemorySanitizer, etc. memory
|
||||
// allocators.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ALLOCATOR_CHECKS_H
|
||||
#define SANITIZER_ALLOCATOR_CHECKS_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// The following is defined in a separate compilation unit to avoid pulling in
|
||||
// sanitizer_errno.h in this header, which leads to conflicts when other system
|
||||
// headers include errno.h. This is usually the result of an unlikely event,
|
||||
// and as such we do not care as much about having it inlined.
|
||||
void SetErrnoToENOMEM();
|
||||
|
||||
// A common errno setting logic shared by almost all sanitizer allocator APIs.
|
||||
INLINE void *SetErrnoOnNull(void *ptr) {
|
||||
if (UNLIKELY(!ptr))
|
||||
SetErrnoToENOMEM();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// In case of the check failure, the caller of the following Check... functions
|
||||
// should "return POLICY::OnBadRequest();" where POLICY is the current allocator
|
||||
// failure handling policy.
|
||||
|
||||
// Checks aligned_alloc() parameters, verifies that the alignment is a power of
|
||||
// two and that the size is a multiple of alignment for POSIX implementation,
|
||||
// and a bit relaxed requirement for non-POSIX ones, that the size is a multiple
|
||||
// of alignment.
|
||||
INLINE bool CheckAlignedAllocAlignmentAndSize(uptr alignment, uptr size) {
|
||||
#if SANITIZER_POSIX
|
||||
return alignment != 0 && IsPowerOfTwo(alignment) &&
|
||||
(size & (alignment - 1)) == 0;
|
||||
#else
|
||||
return alignment != 0 && size % alignment == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Checks posix_memalign() parameters, verifies that alignment is a power of two
|
||||
// and a multiple of sizeof(void *).
|
||||
INLINE bool CheckPosixMemalignAlignment(uptr alignment) {
|
||||
return alignment != 0 && IsPowerOfTwo(alignment) &&
|
||||
(alignment % sizeof(void *)) == 0;
|
||||
}
|
||||
|
||||
// Returns true if calloc(size, n) call overflows on size*n calculation.
|
||||
INLINE bool CheckForCallocOverflow(uptr size, uptr n) {
|
||||
if (!size)
|
||||
return false;
|
||||
uptr max = (uptr)-1L;
|
||||
return (max / size) < n;
|
||||
}
|
||||
|
||||
// Returns true if the size passed to pvalloc overflows when rounded to the next
|
||||
// multiple of page_size.
|
||||
INLINE bool CheckForPvallocOverflow(uptr size, uptr page_size) {
|
||||
return RoundUpTo(size, page_size) < size;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_ALLOCATOR_CHECKS_H
|
||||
201
lib/tsan/sanitizer_common/sanitizer_allocator_combined.h
Normal file
201
lib/tsan/sanitizer_common/sanitizer_allocator_combined.h
Normal file
@@ -0,0 +1,201 @@
|
||||
//===-- sanitizer_allocator_combined.h --------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the Sanitizer Allocator.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_ALLOCATOR_H
|
||||
#error This file must be included inside sanitizer_allocator.h
|
||||
#endif
|
||||
|
||||
// This class implements a complete memory allocator by using two
|
||||
// internal allocators:
|
||||
// PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
|
||||
// When allocating 2^x bytes it should return 2^x aligned chunk.
|
||||
// PrimaryAllocator is used via a local AllocatorCache.
|
||||
// SecondaryAllocator can allocate anything, but is not efficient.
|
||||
template <class PrimaryAllocator,
|
||||
class LargeMmapAllocatorPtrArray = DefaultLargeMmapAllocatorPtrArray>
|
||||
class CombinedAllocator {
|
||||
public:
|
||||
using AllocatorCache = typename PrimaryAllocator::AllocatorCache;
|
||||
using SecondaryAllocator =
|
||||
LargeMmapAllocator<typename PrimaryAllocator::MapUnmapCallback,
|
||||
LargeMmapAllocatorPtrArray,
|
||||
typename PrimaryAllocator::AddressSpaceView>;
|
||||
|
||||
void InitLinkerInitialized(s32 release_to_os_interval_ms) {
|
||||
stats_.InitLinkerInitialized();
|
||||
primary_.Init(release_to_os_interval_ms);
|
||||
secondary_.InitLinkerInitialized();
|
||||
}
|
||||
|
||||
void Init(s32 release_to_os_interval_ms) {
|
||||
stats_.Init();
|
||||
primary_.Init(release_to_os_interval_ms);
|
||||
secondary_.Init();
|
||||
}
|
||||
|
||||
void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) {
|
||||
// Returning 0 on malloc(0) may break a lot of code.
|
||||
if (size == 0)
|
||||
size = 1;
|
||||
if (size + alignment < size) {
|
||||
Report("WARNING: %s: CombinedAllocator allocation overflow: "
|
||||
"0x%zx bytes with 0x%zx alignment requested\n",
|
||||
SanitizerToolName, size, alignment);
|
||||
return nullptr;
|
||||
}
|
||||
uptr original_size = size;
|
||||
// If alignment requirements are to be fulfilled by the frontend allocator
|
||||
// rather than by the primary or secondary, passing an alignment lower than
|
||||
// or equal to 8 will prevent any further rounding up, as well as the later
|
||||
// alignment check.
|
||||
if (alignment > 8)
|
||||
size = RoundUpTo(size, alignment);
|
||||
// The primary allocator should return a 2^x aligned allocation when
|
||||
// requested 2^x bytes, hence using the rounded up 'size' when being
|
||||
// serviced by the primary (this is no longer true when the primary is
|
||||
// using a non-fixed base address). The secondary takes care of the
|
||||
// alignment without such requirement, and allocating 'size' would use
|
||||
// extraneous memory, so we employ 'original_size'.
|
||||
void *res;
|
||||
if (primary_.CanAllocate(size, alignment))
|
||||
res = cache->Allocate(&primary_, primary_.ClassID(size));
|
||||
else
|
||||
res = secondary_.Allocate(&stats_, original_size, alignment);
|
||||
if (alignment > 8)
|
||||
CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
|
||||
return res;
|
||||
}
|
||||
|
||||
s32 ReleaseToOSIntervalMs() const {
|
||||
return primary_.ReleaseToOSIntervalMs();
|
||||
}
|
||||
|
||||
void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) {
|
||||
primary_.SetReleaseToOSIntervalMs(release_to_os_interval_ms);
|
||||
}
|
||||
|
||||
void ForceReleaseToOS() {
|
||||
primary_.ForceReleaseToOS();
|
||||
}
|
||||
|
||||
void Deallocate(AllocatorCache *cache, void *p) {
|
||||
if (!p) return;
|
||||
if (primary_.PointerIsMine(p))
|
||||
cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
|
||||
else
|
||||
secondary_.Deallocate(&stats_, p);
|
||||
}
|
||||
|
||||
void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
|
||||
uptr alignment) {
|
||||
if (!p)
|
||||
return Allocate(cache, new_size, alignment);
|
||||
if (!new_size) {
|
||||
Deallocate(cache, p);
|
||||
return nullptr;
|
||||
}
|
||||
CHECK(PointerIsMine(p));
|
||||
uptr old_size = GetActuallyAllocatedSize(p);
|
||||
uptr memcpy_size = Min(new_size, old_size);
|
||||
void *new_p = Allocate(cache, new_size, alignment);
|
||||
if (new_p)
|
||||
internal_memcpy(new_p, p, memcpy_size);
|
||||
Deallocate(cache, p);
|
||||
return new_p;
|
||||
}
|
||||
|
||||
bool PointerIsMine(void *p) {
|
||||
if (primary_.PointerIsMine(p))
|
||||
return true;
|
||||
return secondary_.PointerIsMine(p);
|
||||
}
|
||||
|
||||
bool FromPrimary(void *p) {
|
||||
return primary_.PointerIsMine(p);
|
||||
}
|
||||
|
||||
void *GetMetaData(const void *p) {
|
||||
if (primary_.PointerIsMine(p))
|
||||
return primary_.GetMetaData(p);
|
||||
return secondary_.GetMetaData(p);
|
||||
}
|
||||
|
||||
void *GetBlockBegin(const void *p) {
|
||||
if (primary_.PointerIsMine(p))
|
||||
return primary_.GetBlockBegin(p);
|
||||
return secondary_.GetBlockBegin(p);
|
||||
}
|
||||
|
||||
// This function does the same as GetBlockBegin, but is much faster.
|
||||
// Must be called with the allocator locked.
|
||||
void *GetBlockBeginFastLocked(void *p) {
|
||||
if (primary_.PointerIsMine(p))
|
||||
return primary_.GetBlockBegin(p);
|
||||
return secondary_.GetBlockBeginFastLocked(p);
|
||||
}
|
||||
|
||||
uptr GetActuallyAllocatedSize(void *p) {
|
||||
if (primary_.PointerIsMine(p))
|
||||
return primary_.GetActuallyAllocatedSize(p);
|
||||
return secondary_.GetActuallyAllocatedSize(p);
|
||||
}
|
||||
|
||||
uptr TotalMemoryUsed() {
|
||||
return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
|
||||
}
|
||||
|
||||
void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
|
||||
|
||||
void InitCache(AllocatorCache *cache) {
|
||||
cache->Init(&stats_);
|
||||
}
|
||||
|
||||
void DestroyCache(AllocatorCache *cache) {
|
||||
cache->Destroy(&primary_, &stats_);
|
||||
}
|
||||
|
||||
void SwallowCache(AllocatorCache *cache) {
|
||||
cache->Drain(&primary_);
|
||||
}
|
||||
|
||||
void GetStats(AllocatorStatCounters s) const {
|
||||
stats_.Get(s);
|
||||
}
|
||||
|
||||
void PrintStats() {
|
||||
primary_.PrintStats();
|
||||
secondary_.PrintStats();
|
||||
}
|
||||
|
||||
// ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
|
||||
// introspection API.
|
||||
void ForceLock() {
|
||||
primary_.ForceLock();
|
||||
secondary_.ForceLock();
|
||||
}
|
||||
|
||||
void ForceUnlock() {
|
||||
secondary_.ForceUnlock();
|
||||
primary_.ForceUnlock();
|
||||
}
|
||||
|
||||
// Iterate over all existing chunks.
|
||||
// The allocator must be locked when calling this function.
|
||||
void ForEachChunk(ForEachChunkCallback callback, void *arg) {
|
||||
primary_.ForEachChunk(callback, arg);
|
||||
secondary_.ForEachChunk(callback, arg);
|
||||
}
|
||||
|
||||
private:
|
||||
PrimaryAllocator primary_;
|
||||
SecondaryAllocator secondary_;
|
||||
AllocatorGlobalStats stats_;
|
||||
};
|
||||
47
lib/tsan/sanitizer_common/sanitizer_allocator_interface.h
Normal file
47
lib/tsan/sanitizer_common/sanitizer_allocator_interface.h
Normal file
@@ -0,0 +1,47 @@
|
||||
//===-- sanitizer_allocator_interface.h ------------------------- C++ -----===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Re-declaration of functions from public sanitizer allocator interface.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ALLOCATOR_INTERFACE_H
|
||||
#define SANITIZER_ALLOCATOR_INTERFACE_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
using __sanitizer::uptr;
|
||||
|
||||
extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __sanitizer_get_estimated_allocated_size(uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_get_ownership(const void *p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE uptr
|
||||
__sanitizer_get_allocated_size(const void *p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_current_allocated_bytes();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_heap_size();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_free_bytes();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_unmapped_bytes();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_install_malloc_and_free_hooks(
|
||||
void (*malloc_hook)(const void *, uptr),
|
||||
void (*free_hook)(const void *));
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_malloc_hook(void *ptr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_free_hook(void *ptr);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
|
||||
__sanitizer_purge_allocator();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
|
||||
__sanitizer_print_memory_profile(uptr top_percent, uptr max_number_of_contexts);
|
||||
} // extern "C"
|
||||
|
||||
#endif // SANITIZER_ALLOCATOR_INTERFACE_H
|
||||
55
lib/tsan/sanitizer_common/sanitizer_allocator_internal.h
Normal file
55
lib/tsan/sanitizer_common/sanitizer_allocator_internal.h
Normal file
@@ -0,0 +1,55 @@
|
||||
//===-- sanitizer_allocator_internal.h --------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This allocator is used inside run-times.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ALLOCATOR_INTERNAL_H
|
||||
#define SANITIZER_ALLOCATOR_INTERNAL_H
|
||||
|
||||
#include "sanitizer_allocator.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// FIXME: Check if we may use even more compact size class map for internal
|
||||
// purposes.
|
||||
typedef CompactSizeClassMap InternalSizeClassMap;
|
||||
|
||||
struct AP32 {
|
||||
static const uptr kSpaceBeg = 0;
|
||||
static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
|
||||
static const uptr kMetadataSize = 0;
|
||||
typedef InternalSizeClassMap SizeClassMap;
|
||||
static const uptr kRegionSizeLog = 20;
|
||||
using AddressSpaceView = LocalAddressSpaceView;
|
||||
typedef NoOpMapUnmapCallback MapUnmapCallback;
|
||||
static const uptr kFlags = 0;
|
||||
};
|
||||
typedef SizeClassAllocator32<AP32> PrimaryInternalAllocator;
|
||||
|
||||
typedef CombinedAllocator<PrimaryInternalAllocator,
|
||||
LargeMmapAllocatorPtrArrayStatic>
|
||||
InternalAllocator;
|
||||
typedef InternalAllocator::AllocatorCache InternalAllocatorCache;
|
||||
|
||||
void *InternalAlloc(uptr size, InternalAllocatorCache *cache = nullptr,
|
||||
uptr alignment = 0);
|
||||
void *InternalRealloc(void *p, uptr size,
|
||||
InternalAllocatorCache *cache = nullptr);
|
||||
void *InternalReallocArray(void *p, uptr count, uptr size,
|
||||
InternalAllocatorCache *cache = nullptr);
|
||||
void *InternalCalloc(uptr count, uptr size,
|
||||
InternalAllocatorCache *cache = nullptr);
|
||||
void InternalFree(void *p, InternalAllocatorCache *cache = nullptr);
|
||||
InternalAllocator *internal_allocator();
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_ALLOCATOR_INTERNAL_H
|
||||
264
lib/tsan/sanitizer_common/sanitizer_allocator_local_cache.h
Normal file
264
lib/tsan/sanitizer_common/sanitizer_allocator_local_cache.h
Normal file
@@ -0,0 +1,264 @@
|
||||
//===-- sanitizer_allocator_local_cache.h -----------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the Sanitizer Allocator.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_ALLOCATOR_H
|
||||
#error This file must be included inside sanitizer_allocator.h
|
||||
#endif
|
||||
|
||||
// Cache used by SizeClassAllocator64.
|
||||
template <class SizeClassAllocator>
|
||||
struct SizeClassAllocator64LocalCache {
|
||||
typedef SizeClassAllocator Allocator;
|
||||
|
||||
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);
|
||||
PerClass *c = &per_class_[class_id];
|
||||
if (UNLIKELY(c->count == 0)) {
|
||||
if (UNLIKELY(!Refill(c, allocator, class_id)))
|
||||
return nullptr;
|
||||
DCHECK_GT(c->count, 0);
|
||||
}
|
||||
CompactPtrT chunk = c->chunks[--c->count];
|
||||
stats_.Add(AllocatorStatAllocated, c->class_size);
|
||||
return reinterpret_cast<void *>(allocator->CompactPtrToPointer(
|
||||
allocator->GetRegionBeginBySizeClass(class_id), chunk));
|
||||
}
|
||||
|
||||
void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) {
|
||||
CHECK_NE(class_id, 0UL);
|
||||
CHECK_LT(class_id, kNumClasses);
|
||||
// If the first allocator call on a new thread is a deallocation, then
|
||||
// max_count will be zero, leading to check failure.
|
||||
PerClass *c = &per_class_[class_id];
|
||||
InitCache(c);
|
||||
if (UNLIKELY(c->count == c->max_count))
|
||||
Drain(c, allocator, class_id, c->max_count / 2);
|
||||
CompactPtrT chunk = allocator->PointerToCompactPtr(
|
||||
allocator->GetRegionBeginBySizeClass(class_id),
|
||||
reinterpret_cast<uptr>(p));
|
||||
c->chunks[c->count++] = chunk;
|
||||
stats_.Sub(AllocatorStatAllocated, c->class_size);
|
||||
}
|
||||
|
||||
void Drain(SizeClassAllocator *allocator) {
|
||||
for (uptr i = 1; i < kNumClasses; i++) {
|
||||
PerClass *c = &per_class_[i];
|
||||
while (c->count > 0)
|
||||
Drain(c, allocator, i, c->count);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef typename Allocator::SizeClassMapT SizeClassMap;
|
||||
static const uptr kNumClasses = SizeClassMap::kNumClasses;
|
||||
typedef typename Allocator::CompactPtrT CompactPtrT;
|
||||
|
||||
struct PerClass {
|
||||
u32 count;
|
||||
u32 max_count;
|
||||
uptr class_size;
|
||||
CompactPtrT chunks[2 * SizeClassMap::kMaxNumCachedHint];
|
||||
};
|
||||
PerClass per_class_[kNumClasses];
|
||||
AllocatorStats stats_;
|
||||
|
||||
void InitCache(PerClass *c) {
|
||||
if (LIKELY(c->max_count))
|
||||
return;
|
||||
for (uptr i = 1; i < kNumClasses; i++) {
|
||||
PerClass *c = &per_class_[i];
|
||||
const uptr size = Allocator::ClassIdToSize(i);
|
||||
c->max_count = 2 * SizeClassMap::MaxCachedHint(size);
|
||||
c->class_size = size;
|
||||
}
|
||||
DCHECK_NE(c->max_count, 0UL);
|
||||
}
|
||||
|
||||
NOINLINE bool Refill(PerClass *c, SizeClassAllocator *allocator,
|
||||
uptr class_id) {
|
||||
InitCache(c);
|
||||
const uptr num_requested_chunks = c->max_count / 2;
|
||||
if (UNLIKELY(!allocator->GetFromAllocator(&stats_, class_id, c->chunks,
|
||||
num_requested_chunks)))
|
||||
return false;
|
||||
c->count = num_requested_chunks;
|
||||
return true;
|
||||
}
|
||||
|
||||
NOINLINE void Drain(PerClass *c, SizeClassAllocator *allocator, uptr class_id,
|
||||
uptr count) {
|
||||
CHECK_GE(c->count, count);
|
||||
const uptr first_idx_to_drain = c->count - count;
|
||||
c->count -= count;
|
||||
allocator->ReturnToAllocator(&stats_, class_id,
|
||||
&c->chunks[first_idx_to_drain], count);
|
||||
}
|
||||
};
|
||||
|
||||
// Cache used by SizeClassAllocator32.
|
||||
template <class SizeClassAllocator>
|
||||
struct SizeClassAllocator32LocalCache {
|
||||
typedef SizeClassAllocator Allocator;
|
||||
typedef typename Allocator::TransferBatch TransferBatch;
|
||||
|
||||
void Init(AllocatorGlobalStats *s) {
|
||||
stats_.Init();
|
||||
if (s)
|
||||
s->Register(&stats_);
|
||||
}
|
||||
|
||||
// Returns a TransferBatch suitable for class_id.
|
||||
TransferBatch *CreateBatch(uptr class_id, SizeClassAllocator *allocator,
|
||||
TransferBatch *b) {
|
||||
if (uptr batch_class_id = per_class_[class_id].batch_class_id)
|
||||
return (TransferBatch*)Allocate(allocator, batch_class_id);
|
||||
return b;
|
||||
}
|
||||
|
||||
// Destroys TransferBatch b.
|
||||
void DestroyBatch(uptr class_id, SizeClassAllocator *allocator,
|
||||
TransferBatch *b) {
|
||||
if (uptr batch_class_id = per_class_[class_id].batch_class_id)
|
||||
Deallocate(allocator, batch_class_id, b);
|
||||
}
|
||||
|
||||
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);
|
||||
PerClass *c = &per_class_[class_id];
|
||||
if (UNLIKELY(c->count == 0)) {
|
||||
if (UNLIKELY(!Refill(c, allocator, class_id)))
|
||||
return nullptr;
|
||||
DCHECK_GT(c->count, 0);
|
||||
}
|
||||
void *res = c->batch[--c->count];
|
||||
PREFETCH(c->batch[c->count - 1]);
|
||||
stats_.Add(AllocatorStatAllocated, c->class_size);
|
||||
return res;
|
||||
}
|
||||
|
||||
void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) {
|
||||
CHECK_NE(class_id, 0UL);
|
||||
CHECK_LT(class_id, kNumClasses);
|
||||
// If the first allocator call on a new thread is a deallocation, then
|
||||
// max_count will be zero, leading to check failure.
|
||||
PerClass *c = &per_class_[class_id];
|
||||
InitCache(c);
|
||||
if (UNLIKELY(c->count == c->max_count))
|
||||
Drain(c, allocator, class_id);
|
||||
c->batch[c->count++] = p;
|
||||
stats_.Sub(AllocatorStatAllocated, c->class_size);
|
||||
}
|
||||
|
||||
void Drain(SizeClassAllocator *allocator) {
|
||||
for (uptr i = 1; i < kNumClasses; i++) {
|
||||
PerClass *c = &per_class_[i];
|
||||
while (c->count > 0)
|
||||
Drain(c, allocator, i);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef typename Allocator::SizeClassMapT SizeClassMap;
|
||||
static const uptr kBatchClassID = SizeClassMap::kBatchClassID;
|
||||
static const uptr kNumClasses = SizeClassMap::kNumClasses;
|
||||
// If kUseSeparateSizeClassForBatch is true, all TransferBatch objects are
|
||||
// allocated from kBatchClassID size class (except for those that are needed
|
||||
// for kBatchClassID itself). The goal is to have TransferBatches in a totally
|
||||
// different region of RAM to improve security.
|
||||
static const bool kUseSeparateSizeClassForBatch =
|
||||
Allocator::kUseSeparateSizeClassForBatch;
|
||||
|
||||
struct PerClass {
|
||||
uptr count;
|
||||
uptr max_count;
|
||||
uptr class_size;
|
||||
uptr batch_class_id;
|
||||
void *batch[2 * TransferBatch::kMaxNumCached];
|
||||
};
|
||||
PerClass per_class_[kNumClasses];
|
||||
AllocatorStats stats_;
|
||||
|
||||
void InitCache(PerClass *c) {
|
||||
if (LIKELY(c->max_count))
|
||||
return;
|
||||
const uptr batch_class_id = SizeClassMap::ClassID(sizeof(TransferBatch));
|
||||
for (uptr i = 1; i < kNumClasses; i++) {
|
||||
PerClass *c = &per_class_[i];
|
||||
const uptr size = Allocator::ClassIdToSize(i);
|
||||
const uptr max_cached = TransferBatch::MaxCached(size);
|
||||
c->max_count = 2 * max_cached;
|
||||
c->class_size = size;
|
||||
// Precompute the class id to use to store batches for the current class
|
||||
// id. 0 means the class size is large enough to store a batch within one
|
||||
// of the chunks. If using a separate size class, it will always be
|
||||
// kBatchClassID, except for kBatchClassID itself.
|
||||
if (kUseSeparateSizeClassForBatch) {
|
||||
c->batch_class_id = (i == kBatchClassID) ? 0 : kBatchClassID;
|
||||
} else {
|
||||
c->batch_class_id = (size <
|
||||
TransferBatch::AllocationSizeRequiredForNElements(max_cached)) ?
|
||||
batch_class_id : 0;
|
||||
}
|
||||
}
|
||||
DCHECK_NE(c->max_count, 0UL);
|
||||
}
|
||||
|
||||
NOINLINE bool Refill(PerClass *c, SizeClassAllocator *allocator,
|
||||
uptr class_id) {
|
||||
InitCache(c);
|
||||
TransferBatch *b = allocator->AllocateBatch(&stats_, this, class_id);
|
||||
if (UNLIKELY(!b))
|
||||
return false;
|
||||
CHECK_GT(b->Count(), 0);
|
||||
b->CopyToArray(c->batch);
|
||||
c->count = b->Count();
|
||||
DestroyBatch(class_id, allocator, b);
|
||||
return true;
|
||||
}
|
||||
|
||||
NOINLINE void Drain(PerClass *c, SizeClassAllocator *allocator,
|
||||
uptr class_id) {
|
||||
const uptr count = Min(c->max_count / 2, c->count);
|
||||
const uptr first_idx_to_drain = c->count - count;
|
||||
TransferBatch *b = CreateBatch(
|
||||
class_id, allocator, (TransferBatch *)c->batch[first_idx_to_drain]);
|
||||
// Failure to allocate a batch while releasing memory is non recoverable.
|
||||
// TODO(alekseys): Figure out how to do it without allocating a new batch.
|
||||
if (UNLIKELY(!b)) {
|
||||
Report("FATAL: Internal error: %s's allocator failed to allocate a "
|
||||
"transfer batch.\n", SanitizerToolName);
|
||||
Die();
|
||||
}
|
||||
b->SetFromArray(&c->batch[first_idx_to_drain], count);
|
||||
c->count -= count;
|
||||
allocator->DeallocateBatch(&stats_, class_id, b);
|
||||
}
|
||||
};
|
||||
380
lib/tsan/sanitizer_common/sanitizer_allocator_primary32.h
Normal file
380
lib/tsan/sanitizer_common/sanitizer_allocator_primary32.h
Normal file
@@ -0,0 +1,380 @@
|
||||
//===-- sanitizer_allocator_primary32.h -------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the Sanitizer Allocator.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_ALLOCATOR_H
|
||||
#error This file must be included inside sanitizer_allocator.h
|
||||
#endif
|
||||
|
||||
template<class SizeClassAllocator> struct SizeClassAllocator32LocalCache;
|
||||
|
||||
// SizeClassAllocator32 -- allocator for 32-bit address space.
|
||||
// This allocator can theoretically be used on 64-bit arch, but there it is less
|
||||
// efficient than SizeClassAllocator64.
|
||||
//
|
||||
// [kSpaceBeg, kSpaceBeg + kSpaceSize) is the range of addresses which can
|
||||
// be returned by MmapOrDie().
|
||||
//
|
||||
// Region:
|
||||
// a result of a single call to MmapAlignedOrDieOnFatalError(kRegionSize,
|
||||
// kRegionSize).
|
||||
// Since the regions are aligned by kRegionSize, there are exactly
|
||||
// kNumPossibleRegions possible regions in the address space and so we keep
|
||||
// a ByteMap possible_regions to store the size classes of each Region.
|
||||
// 0 size class means the region is not used by the allocator.
|
||||
//
|
||||
// One Region is used to allocate chunks of a single size class.
|
||||
// A Region looks like this:
|
||||
// UserChunk1 .. UserChunkN <gap> MetaChunkN .. MetaChunk1
|
||||
//
|
||||
// In order to avoid false sharing the objects of this class should be
|
||||
// chache-line aligned.
|
||||
|
||||
struct SizeClassAllocator32FlagMasks { // Bit masks.
|
||||
enum {
|
||||
kRandomShuffleChunks = 1,
|
||||
kUseSeparateSizeClassForBatch = 2,
|
||||
};
|
||||
};
|
||||
|
||||
template <class Params>
|
||||
class SizeClassAllocator32 {
|
||||
private:
|
||||
static const u64 kTwoLevelByteMapSize1 =
|
||||
(Params::kSpaceSize >> Params::kRegionSizeLog) >> 12;
|
||||
static const u64 kMinFirstMapSizeTwoLevelByteMap = 4;
|
||||
|
||||
public:
|
||||
using AddressSpaceView = typename Params::AddressSpaceView;
|
||||
static const uptr kSpaceBeg = Params::kSpaceBeg;
|
||||
static const u64 kSpaceSize = Params::kSpaceSize;
|
||||
static const uptr kMetadataSize = Params::kMetadataSize;
|
||||
typedef typename Params::SizeClassMap SizeClassMap;
|
||||
static const uptr kRegionSizeLog = Params::kRegionSizeLog;
|
||||
typedef typename Params::MapUnmapCallback MapUnmapCallback;
|
||||
using ByteMap = typename conditional<
|
||||
(kTwoLevelByteMapSize1 < kMinFirstMapSizeTwoLevelByteMap),
|
||||
FlatByteMap<(Params::kSpaceSize >> Params::kRegionSizeLog),
|
||||
AddressSpaceView>,
|
||||
TwoLevelByteMap<kTwoLevelByteMapSize1, 1 << 12, AddressSpaceView>>::type;
|
||||
|
||||
COMPILER_CHECK(!SANITIZER_SIGN_EXTENDED_ADDRESSES ||
|
||||
(kSpaceSize & (kSpaceSize - 1)) == 0);
|
||||
|
||||
static const bool kRandomShuffleChunks = Params::kFlags &
|
||||
SizeClassAllocator32FlagMasks::kRandomShuffleChunks;
|
||||
static const bool kUseSeparateSizeClassForBatch = Params::kFlags &
|
||||
SizeClassAllocator32FlagMasks::kUseSeparateSizeClassForBatch;
|
||||
|
||||
struct TransferBatch {
|
||||
static const uptr kMaxNumCached = SizeClassMap::kMaxNumCachedHint - 2;
|
||||
void SetFromArray(void *batch[], uptr count) {
|
||||
DCHECK_LE(count, kMaxNumCached);
|
||||
count_ = count;
|
||||
for (uptr i = 0; i < count; i++)
|
||||
batch_[i] = batch[i];
|
||||
}
|
||||
uptr Count() const { return count_; }
|
||||
void Clear() { count_ = 0; }
|
||||
void Add(void *ptr) {
|
||||
batch_[count_++] = ptr;
|
||||
DCHECK_LE(count_, kMaxNumCached);
|
||||
}
|
||||
void CopyToArray(void *to_batch[]) const {
|
||||
for (uptr i = 0, n = Count(); i < n; i++)
|
||||
to_batch[i] = batch_[i];
|
||||
}
|
||||
|
||||
// How much memory do we need for a batch containing n elements.
|
||||
static uptr AllocationSizeRequiredForNElements(uptr n) {
|
||||
return sizeof(uptr) * 2 + sizeof(void *) * n;
|
||||
}
|
||||
static uptr MaxCached(uptr size) {
|
||||
return Min(kMaxNumCached, SizeClassMap::MaxCachedHint(size));
|
||||
}
|
||||
|
||||
TransferBatch *next;
|
||||
|
||||
private:
|
||||
uptr count_;
|
||||
void *batch_[kMaxNumCached];
|
||||
};
|
||||
|
||||
static const uptr kBatchSize = sizeof(TransferBatch);
|
||||
COMPILER_CHECK((kBatchSize & (kBatchSize - 1)) == 0);
|
||||
COMPILER_CHECK(kBatchSize == SizeClassMap::kMaxNumCachedHint * sizeof(uptr));
|
||||
|
||||
static uptr ClassIdToSize(uptr class_id) {
|
||||
return (class_id == SizeClassMap::kBatchClassID) ?
|
||||
kBatchSize : SizeClassMap::Size(class_id);
|
||||
}
|
||||
|
||||
typedef SizeClassAllocator32<Params> ThisT;
|
||||
typedef SizeClassAllocator32LocalCache<ThisT> AllocatorCache;
|
||||
|
||||
void Init(s32 release_to_os_interval_ms) {
|
||||
possible_regions.Init();
|
||||
internal_memset(size_class_info_array, 0, sizeof(size_class_info_array));
|
||||
}
|
||||
|
||||
s32 ReleaseToOSIntervalMs() const {
|
||||
return kReleaseToOSIntervalNever;
|
||||
}
|
||||
|
||||
void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) {
|
||||
// This is empty here. Currently only implemented in 64-bit allocator.
|
||||
}
|
||||
|
||||
void ForceReleaseToOS() {
|
||||
// Currently implemented in 64-bit allocator only.
|
||||
}
|
||||
|
||||
void *MapWithCallback(uptr size) {
|
||||
void *res = MmapOrDie(size, PrimaryAllocatorName);
|
||||
MapUnmapCallback().OnMap((uptr)res, size);
|
||||
return res;
|
||||
}
|
||||
|
||||
void UnmapWithCallback(uptr beg, uptr size) {
|
||||
MapUnmapCallback().OnUnmap(beg, size);
|
||||
UnmapOrDie(reinterpret_cast<void *>(beg), size);
|
||||
}
|
||||
|
||||
static bool CanAllocate(uptr size, uptr alignment) {
|
||||
return size <= SizeClassMap::kMaxSize &&
|
||||
alignment <= SizeClassMap::kMaxSize;
|
||||
}
|
||||
|
||||
void *GetMetaData(const void *p) {
|
||||
CHECK(PointerIsMine(p));
|
||||
uptr mem = reinterpret_cast<uptr>(p);
|
||||
uptr beg = ComputeRegionBeg(mem);
|
||||
uptr size = ClassIdToSize(GetSizeClass(p));
|
||||
u32 offset = mem - beg;
|
||||
uptr n = offset / (u32)size; // 32-bit division
|
||||
uptr meta = (beg + kRegionSize) - (n + 1) * kMetadataSize;
|
||||
return reinterpret_cast<void*>(meta);
|
||||
}
|
||||
|
||||
NOINLINE TransferBatch *AllocateBatch(AllocatorStats *stat, AllocatorCache *c,
|
||||
uptr class_id) {
|
||||
DCHECK_LT(class_id, kNumClasses);
|
||||
SizeClassInfo *sci = GetSizeClassInfo(class_id);
|
||||
SpinMutexLock l(&sci->mutex);
|
||||
if (sci->free_list.empty()) {
|
||||
if (UNLIKELY(!PopulateFreeList(stat, c, sci, class_id)))
|
||||
return nullptr;
|
||||
DCHECK(!sci->free_list.empty());
|
||||
}
|
||||
TransferBatch *b = sci->free_list.front();
|
||||
sci->free_list.pop_front();
|
||||
return b;
|
||||
}
|
||||
|
||||
NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id,
|
||||
TransferBatch *b) {
|
||||
DCHECK_LT(class_id, kNumClasses);
|
||||
CHECK_GT(b->Count(), 0);
|
||||
SizeClassInfo *sci = GetSizeClassInfo(class_id);
|
||||
SpinMutexLock l(&sci->mutex);
|
||||
sci->free_list.push_front(b);
|
||||
}
|
||||
|
||||
bool PointerIsMine(const void *p) {
|
||||
uptr mem = reinterpret_cast<uptr>(p);
|
||||
if (SANITIZER_SIGN_EXTENDED_ADDRESSES)
|
||||
mem &= (kSpaceSize - 1);
|
||||
if (mem < kSpaceBeg || mem >= kSpaceBeg + kSpaceSize)
|
||||
return false;
|
||||
return GetSizeClass(p) != 0;
|
||||
}
|
||||
|
||||
uptr GetSizeClass(const void *p) {
|
||||
return possible_regions[ComputeRegionId(reinterpret_cast<uptr>(p))];
|
||||
}
|
||||
|
||||
void *GetBlockBegin(const void *p) {
|
||||
CHECK(PointerIsMine(p));
|
||||
uptr mem = reinterpret_cast<uptr>(p);
|
||||
uptr beg = ComputeRegionBeg(mem);
|
||||
uptr size = ClassIdToSize(GetSizeClass(p));
|
||||
u32 offset = mem - beg;
|
||||
u32 n = offset / (u32)size; // 32-bit division
|
||||
uptr res = beg + (n * (u32)size);
|
||||
return reinterpret_cast<void*>(res);
|
||||
}
|
||||
|
||||
uptr GetActuallyAllocatedSize(void *p) {
|
||||
CHECK(PointerIsMine(p));
|
||||
return ClassIdToSize(GetSizeClass(p));
|
||||
}
|
||||
|
||||
static uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
|
||||
|
||||
uptr TotalMemoryUsed() {
|
||||
// No need to lock here.
|
||||
uptr res = 0;
|
||||
for (uptr i = 0; i < kNumPossibleRegions; i++)
|
||||
if (possible_regions[i])
|
||||
res += kRegionSize;
|
||||
return res;
|
||||
}
|
||||
|
||||
void TestOnlyUnmap() {
|
||||
for (uptr i = 0; i < kNumPossibleRegions; i++)
|
||||
if (possible_regions[i])
|
||||
UnmapWithCallback((i * kRegionSize), kRegionSize);
|
||||
}
|
||||
|
||||
// ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
|
||||
// introspection API.
|
||||
void ForceLock() {
|
||||
for (uptr i = 0; i < kNumClasses; i++) {
|
||||
GetSizeClassInfo(i)->mutex.Lock();
|
||||
}
|
||||
}
|
||||
|
||||
void ForceUnlock() {
|
||||
for (int i = kNumClasses - 1; i >= 0; i--) {
|
||||
GetSizeClassInfo(i)->mutex.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over all existing chunks.
|
||||
// The allocator must be locked when calling this function.
|
||||
void ForEachChunk(ForEachChunkCallback callback, void *arg) {
|
||||
for (uptr region = 0; region < kNumPossibleRegions; region++)
|
||||
if (possible_regions[region]) {
|
||||
uptr chunk_size = ClassIdToSize(possible_regions[region]);
|
||||
uptr max_chunks_in_region = kRegionSize / (chunk_size + kMetadataSize);
|
||||
uptr region_beg = region * kRegionSize;
|
||||
for (uptr chunk = region_beg;
|
||||
chunk < region_beg + max_chunks_in_region * chunk_size;
|
||||
chunk += chunk_size) {
|
||||
// Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk));
|
||||
callback(chunk, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintStats() {}
|
||||
|
||||
static uptr AdditionalSize() { return 0; }
|
||||
|
||||
typedef SizeClassMap SizeClassMapT;
|
||||
static const uptr kNumClasses = SizeClassMap::kNumClasses;
|
||||
|
||||
private:
|
||||
static const uptr kRegionSize = 1 << kRegionSizeLog;
|
||||
static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize;
|
||||
|
||||
struct ALIGNED(SANITIZER_CACHE_LINE_SIZE) SizeClassInfo {
|
||||
StaticSpinMutex mutex;
|
||||
IntrusiveList<TransferBatch> free_list;
|
||||
u32 rand_state;
|
||||
};
|
||||
COMPILER_CHECK(sizeof(SizeClassInfo) % kCacheLineSize == 0);
|
||||
|
||||
uptr ComputeRegionId(uptr mem) const {
|
||||
if (SANITIZER_SIGN_EXTENDED_ADDRESSES)
|
||||
mem &= (kSpaceSize - 1);
|
||||
const uptr res = mem >> kRegionSizeLog;
|
||||
CHECK_LT(res, kNumPossibleRegions);
|
||||
return res;
|
||||
}
|
||||
|
||||
uptr ComputeRegionBeg(uptr mem) {
|
||||
return mem & ~(kRegionSize - 1);
|
||||
}
|
||||
|
||||
uptr AllocateRegion(AllocatorStats *stat, uptr class_id) {
|
||||
DCHECK_LT(class_id, kNumClasses);
|
||||
const uptr res = reinterpret_cast<uptr>(MmapAlignedOrDieOnFatalError(
|
||||
kRegionSize, kRegionSize, PrimaryAllocatorName));
|
||||
if (UNLIKELY(!res))
|
||||
return 0;
|
||||
MapUnmapCallback().OnMap(res, kRegionSize);
|
||||
stat->Add(AllocatorStatMapped, kRegionSize);
|
||||
CHECK(IsAligned(res, kRegionSize));
|
||||
possible_regions.set(ComputeRegionId(res), static_cast<u8>(class_id));
|
||||
return res;
|
||||
}
|
||||
|
||||
SizeClassInfo *GetSizeClassInfo(uptr class_id) {
|
||||
DCHECK_LT(class_id, kNumClasses);
|
||||
return &size_class_info_array[class_id];
|
||||
}
|
||||
|
||||
bool PopulateBatches(AllocatorCache *c, SizeClassInfo *sci, uptr class_id,
|
||||
TransferBatch **current_batch, uptr max_count,
|
||||
uptr *pointers_array, uptr count) {
|
||||
// If using a separate class for batches, we do not need to shuffle it.
|
||||
if (kRandomShuffleChunks && (!kUseSeparateSizeClassForBatch ||
|
||||
class_id != SizeClassMap::kBatchClassID))
|
||||
RandomShuffle(pointers_array, count, &sci->rand_state);
|
||||
TransferBatch *b = *current_batch;
|
||||
for (uptr i = 0; i < count; i++) {
|
||||
if (!b) {
|
||||
b = c->CreateBatch(class_id, this, (TransferBatch*)pointers_array[i]);
|
||||
if (UNLIKELY(!b))
|
||||
return false;
|
||||
b->Clear();
|
||||
}
|
||||
b->Add((void*)pointers_array[i]);
|
||||
if (b->Count() == max_count) {
|
||||
sci->free_list.push_back(b);
|
||||
b = nullptr;
|
||||
}
|
||||
}
|
||||
*current_batch = b;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PopulateFreeList(AllocatorStats *stat, AllocatorCache *c,
|
||||
SizeClassInfo *sci, uptr class_id) {
|
||||
const uptr region = AllocateRegion(stat, class_id);
|
||||
if (UNLIKELY(!region))
|
||||
return false;
|
||||
if (kRandomShuffleChunks)
|
||||
if (UNLIKELY(sci->rand_state == 0))
|
||||
// The random state is initialized from ASLR (PIE) and time.
|
||||
sci->rand_state = reinterpret_cast<uptr>(sci) ^ NanoTime();
|
||||
const uptr size = ClassIdToSize(class_id);
|
||||
const uptr n_chunks = kRegionSize / (size + kMetadataSize);
|
||||
const uptr max_count = TransferBatch::MaxCached(size);
|
||||
DCHECK_GT(max_count, 0);
|
||||
TransferBatch *b = nullptr;
|
||||
constexpr uptr kShuffleArraySize = 48;
|
||||
uptr shuffle_array[kShuffleArraySize];
|
||||
uptr count = 0;
|
||||
for (uptr i = region; i < region + n_chunks * size; i += size) {
|
||||
shuffle_array[count++] = i;
|
||||
if (count == kShuffleArraySize) {
|
||||
if (UNLIKELY(!PopulateBatches(c, sci, class_id, &b, max_count,
|
||||
shuffle_array, count)))
|
||||
return false;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
if (count) {
|
||||
if (UNLIKELY(!PopulateBatches(c, sci, class_id, &b, max_count,
|
||||
shuffle_array, count)))
|
||||
return false;
|
||||
}
|
||||
if (b) {
|
||||
CHECK_GT(b->Count(), 0);
|
||||
sci->free_list.push_back(b);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ByteMap possible_regions;
|
||||
SizeClassInfo size_class_info_array[kNumClasses];
|
||||
};
|
||||
863
lib/tsan/sanitizer_common/sanitizer_allocator_primary64.h
Normal file
863
lib/tsan/sanitizer_common/sanitizer_allocator_primary64.h
Normal file
@@ -0,0 +1,863 @@
|
||||
//===-- sanitizer_allocator_primary64.h -------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the Sanitizer Allocator.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_ALLOCATOR_H
|
||||
#error This file must be included inside sanitizer_allocator.h
|
||||
#endif
|
||||
|
||||
template<class SizeClassAllocator> struct SizeClassAllocator64LocalCache;
|
||||
|
||||
// SizeClassAllocator64 -- allocator for 64-bit address space.
|
||||
// The template parameter Params is a class containing the actual parameters.
|
||||
//
|
||||
// Space: a portion of address space of kSpaceSize bytes starting at SpaceBeg.
|
||||
// If kSpaceBeg is ~0 then SpaceBeg is chosen dynamically my mmap.
|
||||
// Otherwise SpaceBeg=kSpaceBeg (fixed address).
|
||||
// kSpaceSize is a power of two.
|
||||
// At the beginning the entire space is mprotect-ed, then small parts of it
|
||||
// are mapped on demand.
|
||||
//
|
||||
// Region: a part of Space dedicated to a single size class.
|
||||
// There are kNumClasses Regions of equal size.
|
||||
//
|
||||
// UserChunk: a piece of memory returned to user.
|
||||
// MetaChunk: kMetadataSize bytes of metadata associated with a UserChunk.
|
||||
|
||||
// FreeArray is an array free-d chunks (stored as 4-byte offsets)
|
||||
//
|
||||
// A Region looks like this:
|
||||
// UserChunk1 ... UserChunkN <gap> MetaChunkN ... MetaChunk1 FreeArray
|
||||
|
||||
struct SizeClassAllocator64FlagMasks { // Bit masks.
|
||||
enum {
|
||||
kRandomShuffleChunks = 1,
|
||||
};
|
||||
};
|
||||
|
||||
template <class Params>
|
||||
class SizeClassAllocator64 {
|
||||
public:
|
||||
using AddressSpaceView = typename Params::AddressSpaceView;
|
||||
static const uptr kSpaceBeg = Params::kSpaceBeg;
|
||||
static const uptr kSpaceSize = Params::kSpaceSize;
|
||||
static const uptr kMetadataSize = Params::kMetadataSize;
|
||||
typedef typename Params::SizeClassMap SizeClassMap;
|
||||
typedef typename Params::MapUnmapCallback MapUnmapCallback;
|
||||
|
||||
static const bool kRandomShuffleChunks =
|
||||
Params::kFlags & SizeClassAllocator64FlagMasks::kRandomShuffleChunks;
|
||||
|
||||
typedef SizeClassAllocator64<Params> ThisT;
|
||||
typedef SizeClassAllocator64LocalCache<ThisT> AllocatorCache;
|
||||
|
||||
// When we know the size class (the region base) we can represent a pointer
|
||||
// as a 4-byte integer (offset from the region start shifted right by 4).
|
||||
typedef u32 CompactPtrT;
|
||||
static const uptr kCompactPtrScale = 4;
|
||||
CompactPtrT PointerToCompactPtr(uptr base, uptr ptr) const {
|
||||
return static_cast<CompactPtrT>((ptr - base) >> kCompactPtrScale);
|
||||
}
|
||||
uptr CompactPtrToPointer(uptr base, CompactPtrT ptr32) const {
|
||||
return base + (static_cast<uptr>(ptr32) << kCompactPtrScale);
|
||||
}
|
||||
|
||||
void Init(s32 release_to_os_interval_ms) {
|
||||
uptr TotalSpaceSize = kSpaceSize + AdditionalSize();
|
||||
if (kUsingConstantSpaceBeg) {
|
||||
CHECK(IsAligned(kSpaceBeg, SizeClassMap::kMaxSize));
|
||||
CHECK_EQ(kSpaceBeg, address_range.Init(TotalSpaceSize,
|
||||
PrimaryAllocatorName, kSpaceBeg));
|
||||
} else {
|
||||
// Combined allocator expects that an 2^N allocation is always aligned to
|
||||
// 2^N. For this to work, the start of the space needs to be aligned as
|
||||
// high as the largest size class (which also needs to be a power of 2).
|
||||
NonConstSpaceBeg = address_range.InitAligned(
|
||||
TotalSpaceSize, SizeClassMap::kMaxSize, PrimaryAllocatorName);
|
||||
CHECK_NE(NonConstSpaceBeg, ~(uptr)0);
|
||||
}
|
||||
SetReleaseToOSIntervalMs(release_to_os_interval_ms);
|
||||
MapWithCallbackOrDie(SpaceEnd(), AdditionalSize(),
|
||||
"SizeClassAllocator: region info");
|
||||
// Check that the RegionInfo array is aligned on the CacheLine size.
|
||||
DCHECK_EQ(SpaceEnd() % kCacheLineSize, 0);
|
||||
}
|
||||
|
||||
s32 ReleaseToOSIntervalMs() const {
|
||||
return atomic_load(&release_to_os_interval_ms_, memory_order_relaxed);
|
||||
}
|
||||
|
||||
void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) {
|
||||
atomic_store(&release_to_os_interval_ms_, release_to_os_interval_ms,
|
||||
memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ForceReleaseToOS() {
|
||||
for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
|
||||
BlockingMutexLock l(&GetRegionInfo(class_id)->mutex);
|
||||
MaybeReleaseToOS(class_id, true /*force*/);
|
||||
}
|
||||
}
|
||||
|
||||
static bool CanAllocate(uptr size, uptr alignment) {
|
||||
return size <= SizeClassMap::kMaxSize &&
|
||||
alignment <= SizeClassMap::kMaxSize;
|
||||
}
|
||||
|
||||
NOINLINE void ReturnToAllocator(AllocatorStats *stat, uptr class_id,
|
||||
const CompactPtrT *chunks, uptr n_chunks) {
|
||||
RegionInfo *region = GetRegionInfo(class_id);
|
||||
uptr region_beg = GetRegionBeginBySizeClass(class_id);
|
||||
CompactPtrT *free_array = GetFreeArray(region_beg);
|
||||
|
||||
BlockingMutexLock l(®ion->mutex);
|
||||
uptr old_num_chunks = region->num_freed_chunks;
|
||||
uptr new_num_freed_chunks = old_num_chunks + n_chunks;
|
||||
// Failure to allocate free array space while releasing memory is non
|
||||
// recoverable.
|
||||
if (UNLIKELY(!EnsureFreeArraySpace(region, region_beg,
|
||||
new_num_freed_chunks))) {
|
||||
Report("FATAL: Internal error: %s's allocator exhausted the free list "
|
||||
"space for size class %zd (%zd bytes).\n", SanitizerToolName,
|
||||
class_id, ClassIdToSize(class_id));
|
||||
Die();
|
||||
}
|
||||
for (uptr i = 0; i < n_chunks; i++)
|
||||
free_array[old_num_chunks + i] = chunks[i];
|
||||
region->num_freed_chunks = new_num_freed_chunks;
|
||||
region->stats.n_freed += n_chunks;
|
||||
|
||||
MaybeReleaseToOS(class_id, false /*force*/);
|
||||
}
|
||||
|
||||
NOINLINE bool GetFromAllocator(AllocatorStats *stat, uptr class_id,
|
||||
CompactPtrT *chunks, uptr n_chunks) {
|
||||
RegionInfo *region = GetRegionInfo(class_id);
|
||||
uptr region_beg = GetRegionBeginBySizeClass(class_id);
|
||||
CompactPtrT *free_array = GetFreeArray(region_beg);
|
||||
|
||||
BlockingMutexLock l(®ion->mutex);
|
||||
if (UNLIKELY(region->num_freed_chunks < n_chunks)) {
|
||||
if (UNLIKELY(!PopulateFreeArray(stat, class_id, region,
|
||||
n_chunks - region->num_freed_chunks)))
|
||||
return false;
|
||||
CHECK_GE(region->num_freed_chunks, n_chunks);
|
||||
}
|
||||
region->num_freed_chunks -= n_chunks;
|
||||
uptr base_idx = region->num_freed_chunks;
|
||||
for (uptr i = 0; i < n_chunks; i++)
|
||||
chunks[i] = free_array[base_idx + i];
|
||||
region->stats.n_allocated += n_chunks;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PointerIsMine(const void *p) const {
|
||||
uptr P = reinterpret_cast<uptr>(p);
|
||||
if (kUsingConstantSpaceBeg && (kSpaceBeg % kSpaceSize) == 0)
|
||||
return P / kSpaceSize == kSpaceBeg / kSpaceSize;
|
||||
return P >= SpaceBeg() && P < SpaceEnd();
|
||||
}
|
||||
|
||||
uptr GetRegionBegin(const void *p) {
|
||||
if (kUsingConstantSpaceBeg)
|
||||
return reinterpret_cast<uptr>(p) & ~(kRegionSize - 1);
|
||||
uptr space_beg = SpaceBeg();
|
||||
return ((reinterpret_cast<uptr>(p) - space_beg) & ~(kRegionSize - 1)) +
|
||||
space_beg;
|
||||
}
|
||||
|
||||
uptr GetRegionBeginBySizeClass(uptr class_id) const {
|
||||
return SpaceBeg() + kRegionSize * class_id;
|
||||
}
|
||||
|
||||
uptr GetSizeClass(const void *p) {
|
||||
if (kUsingConstantSpaceBeg && (kSpaceBeg % kSpaceSize) == 0)
|
||||
return ((reinterpret_cast<uptr>(p)) / kRegionSize) % kNumClassesRounded;
|
||||
return ((reinterpret_cast<uptr>(p) - SpaceBeg()) / kRegionSize) %
|
||||
kNumClassesRounded;
|
||||
}
|
||||
|
||||
void *GetBlockBegin(const void *p) {
|
||||
uptr class_id = GetSizeClass(p);
|
||||
uptr size = ClassIdToSize(class_id);
|
||||
if (!size) return nullptr;
|
||||
uptr chunk_idx = GetChunkIdx((uptr)p, size);
|
||||
uptr reg_beg = GetRegionBegin(p);
|
||||
uptr beg = chunk_idx * size;
|
||||
uptr next_beg = beg + size;
|
||||
if (class_id >= kNumClasses) return nullptr;
|
||||
const RegionInfo *region = AddressSpaceView::Load(GetRegionInfo(class_id));
|
||||
if (region->mapped_user >= next_beg)
|
||||
return reinterpret_cast<void*>(reg_beg + beg);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uptr GetActuallyAllocatedSize(void *p) {
|
||||
CHECK(PointerIsMine(p));
|
||||
return ClassIdToSize(GetSizeClass(p));
|
||||
}
|
||||
|
||||
static uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
|
||||
|
||||
void *GetMetaData(const void *p) {
|
||||
uptr class_id = GetSizeClass(p);
|
||||
uptr size = ClassIdToSize(class_id);
|
||||
uptr chunk_idx = GetChunkIdx(reinterpret_cast<uptr>(p), size);
|
||||
uptr region_beg = GetRegionBeginBySizeClass(class_id);
|
||||
return reinterpret_cast<void *>(GetMetadataEnd(region_beg) -
|
||||
(1 + chunk_idx) * kMetadataSize);
|
||||
}
|
||||
|
||||
uptr TotalMemoryUsed() {
|
||||
uptr res = 0;
|
||||
for (uptr i = 0; i < kNumClasses; i++)
|
||||
res += GetRegionInfo(i)->allocated_user;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Test-only.
|
||||
void TestOnlyUnmap() {
|
||||
UnmapWithCallbackOrDie((uptr)address_range.base(), address_range.size());
|
||||
}
|
||||
|
||||
static void FillMemoryProfile(uptr start, uptr rss, bool file, uptr *stats,
|
||||
uptr stats_size) {
|
||||
for (uptr class_id = 0; class_id < stats_size; class_id++)
|
||||
if (stats[class_id] == start)
|
||||
stats[class_id] = rss;
|
||||
}
|
||||
|
||||
void PrintStats(uptr class_id, uptr rss) {
|
||||
RegionInfo *region = GetRegionInfo(class_id);
|
||||
if (region->mapped_user == 0) return;
|
||||
uptr in_use = region->stats.n_allocated - region->stats.n_freed;
|
||||
uptr avail_chunks = region->allocated_user / ClassIdToSize(class_id);
|
||||
Printf(
|
||||
"%s %02zd (%6zd): mapped: %6zdK allocs: %7zd frees: %7zd inuse: %6zd "
|
||||
"num_freed_chunks %7zd avail: %6zd rss: %6zdK releases: %6zd "
|
||||
"last released: %6zdK region: 0x%zx\n",
|
||||
region->exhausted ? "F" : " ", class_id, ClassIdToSize(class_id),
|
||||
region->mapped_user >> 10, region->stats.n_allocated,
|
||||
region->stats.n_freed, in_use, region->num_freed_chunks, avail_chunks,
|
||||
rss >> 10, region->rtoi.num_releases,
|
||||
region->rtoi.last_released_bytes >> 10,
|
||||
SpaceBeg() + kRegionSize * class_id);
|
||||
}
|
||||
|
||||
void PrintStats() {
|
||||
uptr rss_stats[kNumClasses];
|
||||
for (uptr class_id = 0; class_id < kNumClasses; class_id++)
|
||||
rss_stats[class_id] = SpaceBeg() + kRegionSize * class_id;
|
||||
GetMemoryProfile(FillMemoryProfile, rss_stats, kNumClasses);
|
||||
|
||||
uptr total_mapped = 0;
|
||||
uptr total_rss = 0;
|
||||
uptr n_allocated = 0;
|
||||
uptr n_freed = 0;
|
||||
for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
|
||||
RegionInfo *region = GetRegionInfo(class_id);
|
||||
if (region->mapped_user != 0) {
|
||||
total_mapped += region->mapped_user;
|
||||
total_rss += rss_stats[class_id];
|
||||
}
|
||||
n_allocated += region->stats.n_allocated;
|
||||
n_freed += region->stats.n_freed;
|
||||
}
|
||||
|
||||
Printf("Stats: SizeClassAllocator64: %zdM mapped (%zdM rss) in "
|
||||
"%zd allocations; remains %zd\n", total_mapped >> 20,
|
||||
total_rss >> 20, n_allocated, n_allocated - n_freed);
|
||||
for (uptr class_id = 1; class_id < kNumClasses; class_id++)
|
||||
PrintStats(class_id, rss_stats[class_id]);
|
||||
}
|
||||
|
||||
// ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
|
||||
// introspection API.
|
||||
void ForceLock() {
|
||||
for (uptr i = 0; i < kNumClasses; i++) {
|
||||
GetRegionInfo(i)->mutex.Lock();
|
||||
}
|
||||
}
|
||||
|
||||
void ForceUnlock() {
|
||||
for (int i = (int)kNumClasses - 1; i >= 0; i--) {
|
||||
GetRegionInfo(i)->mutex.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over all existing chunks.
|
||||
// The allocator must be locked when calling this function.
|
||||
void ForEachChunk(ForEachChunkCallback callback, void *arg) {
|
||||
for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
|
||||
RegionInfo *region = GetRegionInfo(class_id);
|
||||
uptr chunk_size = ClassIdToSize(class_id);
|
||||
uptr region_beg = SpaceBeg() + class_id * kRegionSize;
|
||||
uptr region_allocated_user_size =
|
||||
AddressSpaceView::Load(region)->allocated_user;
|
||||
for (uptr chunk = region_beg;
|
||||
chunk < region_beg + region_allocated_user_size;
|
||||
chunk += chunk_size) {
|
||||
// Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk));
|
||||
callback(chunk, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uptr ClassIdToSize(uptr class_id) {
|
||||
return SizeClassMap::Size(class_id);
|
||||
}
|
||||
|
||||
static uptr AdditionalSize() {
|
||||
return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded,
|
||||
GetPageSizeCached());
|
||||
}
|
||||
|
||||
typedef SizeClassMap SizeClassMapT;
|
||||
static const uptr kNumClasses = SizeClassMap::kNumClasses;
|
||||
static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded;
|
||||
|
||||
// A packed array of counters. Each counter occupies 2^n bits, enough to store
|
||||
// counter's max_value. Ctor will try to allocate the required buffer via
|
||||
// mapper->MapPackedCounterArrayBuffer and the caller is expected to check
|
||||
// whether the initialization was successful by checking IsAllocated() result.
|
||||
// For the performance sake, none of the accessors check the validity of the
|
||||
// arguments, it is assumed that index is always in [0, n) range and the value
|
||||
// is not incremented past max_value.
|
||||
template<class MemoryMapperT>
|
||||
class PackedCounterArray {
|
||||
public:
|
||||
PackedCounterArray(u64 num_counters, u64 max_value, MemoryMapperT *mapper)
|
||||
: n(num_counters), memory_mapper(mapper) {
|
||||
CHECK_GT(num_counters, 0);
|
||||
CHECK_GT(max_value, 0);
|
||||
constexpr u64 kMaxCounterBits = sizeof(*buffer) * 8ULL;
|
||||
// Rounding counter storage size up to the power of two allows for using
|
||||
// bit shifts calculating particular counter's index and offset.
|
||||
uptr counter_size_bits =
|
||||
RoundUpToPowerOfTwo(MostSignificantSetBitIndex(max_value) + 1);
|
||||
CHECK_LE(counter_size_bits, kMaxCounterBits);
|
||||
counter_size_bits_log = Log2(counter_size_bits);
|
||||
counter_mask = ~0ULL >> (kMaxCounterBits - counter_size_bits);
|
||||
|
||||
uptr packing_ratio = kMaxCounterBits >> counter_size_bits_log;
|
||||
CHECK_GT(packing_ratio, 0);
|
||||
packing_ratio_log = Log2(packing_ratio);
|
||||
bit_offset_mask = packing_ratio - 1;
|
||||
|
||||
buffer_size =
|
||||
(RoundUpTo(n, 1ULL << packing_ratio_log) >> packing_ratio_log) *
|
||||
sizeof(*buffer);
|
||||
buffer = reinterpret_cast<u64*>(
|
||||
memory_mapper->MapPackedCounterArrayBuffer(buffer_size));
|
||||
}
|
||||
~PackedCounterArray() {
|
||||
if (buffer) {
|
||||
memory_mapper->UnmapPackedCounterArrayBuffer(
|
||||
reinterpret_cast<uptr>(buffer), buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsAllocated() const {
|
||||
return !!buffer;
|
||||
}
|
||||
|
||||
u64 GetCount() const {
|
||||
return n;
|
||||
}
|
||||
|
||||
uptr Get(uptr i) const {
|
||||
DCHECK_LT(i, n);
|
||||
uptr index = i >> packing_ratio_log;
|
||||
uptr bit_offset = (i & bit_offset_mask) << counter_size_bits_log;
|
||||
return (buffer[index] >> bit_offset) & counter_mask;
|
||||
}
|
||||
|
||||
void Inc(uptr i) const {
|
||||
DCHECK_LT(Get(i), counter_mask);
|
||||
uptr index = i >> packing_ratio_log;
|
||||
uptr bit_offset = (i & bit_offset_mask) << counter_size_bits_log;
|
||||
buffer[index] += 1ULL << bit_offset;
|
||||
}
|
||||
|
||||
void IncRange(uptr from, uptr to) const {
|
||||
DCHECK_LE(from, to);
|
||||
for (uptr i = from; i <= to; i++)
|
||||
Inc(i);
|
||||
}
|
||||
|
||||
private:
|
||||
const u64 n;
|
||||
u64 counter_size_bits_log;
|
||||
u64 counter_mask;
|
||||
u64 packing_ratio_log;
|
||||
u64 bit_offset_mask;
|
||||
|
||||
MemoryMapperT* const memory_mapper;
|
||||
u64 buffer_size;
|
||||
u64* buffer;
|
||||
};
|
||||
|
||||
template<class MemoryMapperT>
|
||||
class FreePagesRangeTracker {
|
||||
public:
|
||||
explicit FreePagesRangeTracker(MemoryMapperT* mapper)
|
||||
: memory_mapper(mapper),
|
||||
page_size_scaled_log(Log2(GetPageSizeCached() >> kCompactPtrScale)),
|
||||
in_the_range(false), current_page(0), current_range_start_page(0) {}
|
||||
|
||||
void NextPage(bool freed) {
|
||||
if (freed) {
|
||||
if (!in_the_range) {
|
||||
current_range_start_page = current_page;
|
||||
in_the_range = true;
|
||||
}
|
||||
} else {
|
||||
CloseOpenedRange();
|
||||
}
|
||||
current_page++;
|
||||
}
|
||||
|
||||
void Done() {
|
||||
CloseOpenedRange();
|
||||
}
|
||||
|
||||
private:
|
||||
void CloseOpenedRange() {
|
||||
if (in_the_range) {
|
||||
memory_mapper->ReleasePageRangeToOS(
|
||||
current_range_start_page << page_size_scaled_log,
|
||||
current_page << page_size_scaled_log);
|
||||
in_the_range = false;
|
||||
}
|
||||
}
|
||||
|
||||
MemoryMapperT* const memory_mapper;
|
||||
const uptr page_size_scaled_log;
|
||||
bool in_the_range;
|
||||
uptr current_page;
|
||||
uptr current_range_start_page;
|
||||
};
|
||||
|
||||
// Iterates over the free_array to identify memory pages containing freed
|
||||
// chunks only and returns these pages back to OS.
|
||||
// allocated_pages_count is the total number of pages allocated for the
|
||||
// current bucket.
|
||||
template<class MemoryMapperT>
|
||||
static void ReleaseFreeMemoryToOS(CompactPtrT *free_array,
|
||||
uptr free_array_count, uptr chunk_size,
|
||||
uptr allocated_pages_count,
|
||||
MemoryMapperT *memory_mapper) {
|
||||
const uptr page_size = GetPageSizeCached();
|
||||
|
||||
// Figure out the number of chunks per page and whether we can take a fast
|
||||
// path (the number of chunks per page is the same for all pages).
|
||||
uptr full_pages_chunk_count_max;
|
||||
bool same_chunk_count_per_page;
|
||||
if (chunk_size <= page_size && page_size % chunk_size == 0) {
|
||||
// Same number of chunks per page, no cross overs.
|
||||
full_pages_chunk_count_max = page_size / chunk_size;
|
||||
same_chunk_count_per_page = true;
|
||||
} else if (chunk_size <= page_size && page_size % chunk_size != 0 &&
|
||||
chunk_size % (page_size % chunk_size) == 0) {
|
||||
// Some chunks are crossing page boundaries, which means that the page
|
||||
// contains one or two partial chunks, but all pages contain the same
|
||||
// number of chunks.
|
||||
full_pages_chunk_count_max = page_size / chunk_size + 1;
|
||||
same_chunk_count_per_page = true;
|
||||
} else if (chunk_size <= page_size) {
|
||||
// Some chunks are crossing page boundaries, which means that the page
|
||||
// contains one or two partial chunks.
|
||||
full_pages_chunk_count_max = page_size / chunk_size + 2;
|
||||
same_chunk_count_per_page = false;
|
||||
} else if (chunk_size > page_size && chunk_size % page_size == 0) {
|
||||
// One chunk covers multiple pages, no cross overs.
|
||||
full_pages_chunk_count_max = 1;
|
||||
same_chunk_count_per_page = true;
|
||||
} else if (chunk_size > page_size) {
|
||||
// One chunk covers multiple pages, Some chunks are crossing page
|
||||
// boundaries. Some pages contain one chunk, some contain two.
|
||||
full_pages_chunk_count_max = 2;
|
||||
same_chunk_count_per_page = false;
|
||||
} else {
|
||||
UNREACHABLE("All chunk_size/page_size ratios must be handled.");
|
||||
}
|
||||
|
||||
PackedCounterArray<MemoryMapperT> counters(allocated_pages_count,
|
||||
full_pages_chunk_count_max,
|
||||
memory_mapper);
|
||||
if (!counters.IsAllocated())
|
||||
return;
|
||||
|
||||
const uptr chunk_size_scaled = chunk_size >> kCompactPtrScale;
|
||||
const uptr page_size_scaled = page_size >> kCompactPtrScale;
|
||||
const uptr page_size_scaled_log = Log2(page_size_scaled);
|
||||
|
||||
// Iterate over free chunks and count how many free chunks affect each
|
||||
// allocated page.
|
||||
if (chunk_size <= page_size && page_size % chunk_size == 0) {
|
||||
// Each chunk affects one page only.
|
||||
for (uptr i = 0; i < free_array_count; i++)
|
||||
counters.Inc(free_array[i] >> page_size_scaled_log);
|
||||
} else {
|
||||
// In all other cases chunks might affect more than one page.
|
||||
for (uptr i = 0; i < free_array_count; i++) {
|
||||
counters.IncRange(
|
||||
free_array[i] >> page_size_scaled_log,
|
||||
(free_array[i] + chunk_size_scaled - 1) >> page_size_scaled_log);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over pages detecting ranges of pages with chunk counters equal
|
||||
// to the expected number of chunks for the particular page.
|
||||
FreePagesRangeTracker<MemoryMapperT> range_tracker(memory_mapper);
|
||||
if (same_chunk_count_per_page) {
|
||||
// Fast path, every page has the same number of chunks affecting it.
|
||||
for (uptr i = 0; i < counters.GetCount(); i++)
|
||||
range_tracker.NextPage(counters.Get(i) == full_pages_chunk_count_max);
|
||||
} else {
|
||||
// Show path, go through the pages keeping count how many chunks affect
|
||||
// each page.
|
||||
const uptr pn =
|
||||
chunk_size < page_size ? page_size_scaled / chunk_size_scaled : 1;
|
||||
const uptr pnc = pn * chunk_size_scaled;
|
||||
// The idea is to increment the current page pointer by the first chunk
|
||||
// size, middle portion size (the portion of the page covered by chunks
|
||||
// except the first and the last one) and then the last chunk size, adding
|
||||
// up the number of chunks on the current page and checking on every step
|
||||
// whether the page boundary was crossed.
|
||||
uptr prev_page_boundary = 0;
|
||||
uptr current_boundary = 0;
|
||||
for (uptr i = 0; i < counters.GetCount(); i++) {
|
||||
uptr page_boundary = prev_page_boundary + page_size_scaled;
|
||||
uptr chunks_per_page = pn;
|
||||
if (current_boundary < page_boundary) {
|
||||
if (current_boundary > prev_page_boundary)
|
||||
chunks_per_page++;
|
||||
current_boundary += pnc;
|
||||
if (current_boundary < page_boundary) {
|
||||
chunks_per_page++;
|
||||
current_boundary += chunk_size_scaled;
|
||||
}
|
||||
}
|
||||
prev_page_boundary = page_boundary;
|
||||
|
||||
range_tracker.NextPage(counters.Get(i) == chunks_per_page);
|
||||
}
|
||||
}
|
||||
range_tracker.Done();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class MemoryMapper;
|
||||
|
||||
ReservedAddressRange address_range;
|
||||
|
||||
static const uptr kRegionSize = kSpaceSize / kNumClassesRounded;
|
||||
// FreeArray is the array of free-d chunks (stored as 4-byte offsets).
|
||||
// In the worst case it may reguire kRegionSize/SizeClassMap::kMinSize
|
||||
// elements, but in reality this will not happen. For simplicity we
|
||||
// dedicate 1/8 of the region's virtual space to FreeArray.
|
||||
static const uptr kFreeArraySize = kRegionSize / 8;
|
||||
|
||||
static const bool kUsingConstantSpaceBeg = kSpaceBeg != ~(uptr)0;
|
||||
uptr NonConstSpaceBeg;
|
||||
uptr SpaceBeg() const {
|
||||
return kUsingConstantSpaceBeg ? kSpaceBeg : NonConstSpaceBeg;
|
||||
}
|
||||
uptr SpaceEnd() const { return SpaceBeg() + kSpaceSize; }
|
||||
// kRegionSize must be >= 2^32.
|
||||
COMPILER_CHECK((kRegionSize) >= (1ULL << (SANITIZER_WORDSIZE / 2)));
|
||||
// kRegionSize must be <= 2^36, see CompactPtrT.
|
||||
COMPILER_CHECK((kRegionSize) <= (1ULL << (SANITIZER_WORDSIZE / 2 + 4)));
|
||||
// Call mmap for user memory with at least this size.
|
||||
static const uptr kUserMapSize = 1 << 16;
|
||||
// Call mmap for metadata memory with at least this size.
|
||||
static const uptr kMetaMapSize = 1 << 16;
|
||||
// Call mmap for free array memory with at least this size.
|
||||
static const uptr kFreeArrayMapSize = 1 << 16;
|
||||
|
||||
atomic_sint32_t release_to_os_interval_ms_;
|
||||
|
||||
struct Stats {
|
||||
uptr n_allocated;
|
||||
uptr n_freed;
|
||||
};
|
||||
|
||||
struct ReleaseToOsInfo {
|
||||
uptr n_freed_at_last_release;
|
||||
uptr num_releases;
|
||||
u64 last_release_at_ns;
|
||||
u64 last_released_bytes;
|
||||
};
|
||||
|
||||
struct ALIGNED(SANITIZER_CACHE_LINE_SIZE) RegionInfo {
|
||||
BlockingMutex mutex;
|
||||
uptr num_freed_chunks; // Number of elements in the freearray.
|
||||
uptr mapped_free_array; // Bytes mapped for freearray.
|
||||
uptr allocated_user; // Bytes allocated for user memory.
|
||||
uptr allocated_meta; // Bytes allocated for metadata.
|
||||
uptr mapped_user; // Bytes mapped for user memory.
|
||||
uptr mapped_meta; // Bytes mapped for metadata.
|
||||
u32 rand_state; // Seed for random shuffle, used if kRandomShuffleChunks.
|
||||
bool exhausted; // Whether region is out of space for new chunks.
|
||||
Stats stats;
|
||||
ReleaseToOsInfo rtoi;
|
||||
};
|
||||
COMPILER_CHECK(sizeof(RegionInfo) % kCacheLineSize == 0);
|
||||
|
||||
RegionInfo *GetRegionInfo(uptr class_id) const {
|
||||
DCHECK_LT(class_id, kNumClasses);
|
||||
RegionInfo *regions = reinterpret_cast<RegionInfo *>(SpaceEnd());
|
||||
return ®ions[class_id];
|
||||
}
|
||||
|
||||
uptr GetMetadataEnd(uptr region_beg) const {
|
||||
return region_beg + kRegionSize - kFreeArraySize;
|
||||
}
|
||||
|
||||
uptr GetChunkIdx(uptr chunk, uptr size) const {
|
||||
if (!kUsingConstantSpaceBeg)
|
||||
chunk -= SpaceBeg();
|
||||
|
||||
uptr offset = chunk % kRegionSize;
|
||||
// Here we divide by a non-constant. This is costly.
|
||||
// size always fits into 32-bits. If the offset fits too, use 32-bit div.
|
||||
if (offset >> (SANITIZER_WORDSIZE / 2))
|
||||
return offset / size;
|
||||
return (u32)offset / (u32)size;
|
||||
}
|
||||
|
||||
CompactPtrT *GetFreeArray(uptr region_beg) const {
|
||||
return reinterpret_cast<CompactPtrT *>(GetMetadataEnd(region_beg));
|
||||
}
|
||||
|
||||
bool MapWithCallback(uptr beg, uptr size, const char *name) {
|
||||
uptr mapped = address_range.Map(beg, size, name);
|
||||
if (UNLIKELY(!mapped))
|
||||
return false;
|
||||
CHECK_EQ(beg, mapped);
|
||||
MapUnmapCallback().OnMap(beg, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MapWithCallbackOrDie(uptr beg, uptr size, const char *name) {
|
||||
CHECK_EQ(beg, address_range.MapOrDie(beg, size, name));
|
||||
MapUnmapCallback().OnMap(beg, size);
|
||||
}
|
||||
|
||||
void UnmapWithCallbackOrDie(uptr beg, uptr size) {
|
||||
MapUnmapCallback().OnUnmap(beg, size);
|
||||
address_range.Unmap(beg, size);
|
||||
}
|
||||
|
||||
bool EnsureFreeArraySpace(RegionInfo *region, uptr region_beg,
|
||||
uptr num_freed_chunks) {
|
||||
uptr needed_space = num_freed_chunks * sizeof(CompactPtrT);
|
||||
if (region->mapped_free_array < needed_space) {
|
||||
uptr new_mapped_free_array = RoundUpTo(needed_space, kFreeArrayMapSize);
|
||||
CHECK_LE(new_mapped_free_array, kFreeArraySize);
|
||||
uptr current_map_end = reinterpret_cast<uptr>(GetFreeArray(region_beg)) +
|
||||
region->mapped_free_array;
|
||||
uptr new_map_size = new_mapped_free_array - region->mapped_free_array;
|
||||
if (UNLIKELY(!MapWithCallback(current_map_end, new_map_size,
|
||||
"SizeClassAllocator: freearray")))
|
||||
return false;
|
||||
region->mapped_free_array = new_mapped_free_array;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check whether this size class is exhausted.
|
||||
bool IsRegionExhausted(RegionInfo *region, uptr class_id,
|
||||
uptr additional_map_size) {
|
||||
if (LIKELY(region->mapped_user + region->mapped_meta +
|
||||
additional_map_size <= kRegionSize - kFreeArraySize))
|
||||
return false;
|
||||
if (!region->exhausted) {
|
||||
region->exhausted = true;
|
||||
Printf("%s: Out of memory. ", SanitizerToolName);
|
||||
Printf("The process has exhausted %zuMB for size class %zu.\n",
|
||||
kRegionSize >> 20, ClassIdToSize(class_id));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
NOINLINE bool PopulateFreeArray(AllocatorStats *stat, uptr class_id,
|
||||
RegionInfo *region, uptr requested_count) {
|
||||
// region->mutex is held.
|
||||
const uptr region_beg = GetRegionBeginBySizeClass(class_id);
|
||||
const uptr size = ClassIdToSize(class_id);
|
||||
|
||||
const uptr total_user_bytes =
|
||||
region->allocated_user + requested_count * size;
|
||||
// Map more space for chunks, if necessary.
|
||||
if (LIKELY(total_user_bytes > region->mapped_user)) {
|
||||
if (UNLIKELY(region->mapped_user == 0)) {
|
||||
if (!kUsingConstantSpaceBeg && kRandomShuffleChunks)
|
||||
// The random state is initialized from ASLR.
|
||||
region->rand_state = static_cast<u32>(region_beg >> 12);
|
||||
// Postpone the first release to OS attempt for ReleaseToOSIntervalMs,
|
||||
// preventing just allocated memory from being released sooner than
|
||||
// necessary and also preventing extraneous ReleaseMemoryPagesToOS calls
|
||||
// for short lived processes.
|
||||
// Do it only when the feature is turned on, to avoid a potentially
|
||||
// extraneous syscall.
|
||||
if (ReleaseToOSIntervalMs() >= 0)
|
||||
region->rtoi.last_release_at_ns = MonotonicNanoTime();
|
||||
}
|
||||
// Do the mmap for the user memory.
|
||||
const uptr user_map_size =
|
||||
RoundUpTo(total_user_bytes - region->mapped_user, kUserMapSize);
|
||||
if (UNLIKELY(IsRegionExhausted(region, class_id, user_map_size)))
|
||||
return false;
|
||||
if (UNLIKELY(!MapWithCallback(region_beg + region->mapped_user,
|
||||
user_map_size,
|
||||
"SizeClassAllocator: region data")))
|
||||
return false;
|
||||
stat->Add(AllocatorStatMapped, user_map_size);
|
||||
region->mapped_user += user_map_size;
|
||||
}
|
||||
const uptr new_chunks_count =
|
||||
(region->mapped_user - region->allocated_user) / size;
|
||||
|
||||
if (kMetadataSize) {
|
||||
// Calculate the required space for metadata.
|
||||
const uptr total_meta_bytes =
|
||||
region->allocated_meta + new_chunks_count * kMetadataSize;
|
||||
const uptr meta_map_size = (total_meta_bytes > region->mapped_meta) ?
|
||||
RoundUpTo(total_meta_bytes - region->mapped_meta, kMetaMapSize) : 0;
|
||||
// Map more space for metadata, if necessary.
|
||||
if (meta_map_size) {
|
||||
if (UNLIKELY(IsRegionExhausted(region, class_id, meta_map_size)))
|
||||
return false;
|
||||
if (UNLIKELY(!MapWithCallback(
|
||||
GetMetadataEnd(region_beg) - region->mapped_meta - meta_map_size,
|
||||
meta_map_size, "SizeClassAllocator: region metadata")))
|
||||
return false;
|
||||
region->mapped_meta += meta_map_size;
|
||||
}
|
||||
}
|
||||
|
||||
// If necessary, allocate more space for the free array and populate it with
|
||||
// newly allocated chunks.
|
||||
const uptr total_freed_chunks = region->num_freed_chunks + new_chunks_count;
|
||||
if (UNLIKELY(!EnsureFreeArraySpace(region, region_beg, total_freed_chunks)))
|
||||
return false;
|
||||
CompactPtrT *free_array = GetFreeArray(region_beg);
|
||||
for (uptr i = 0, chunk = region->allocated_user; i < new_chunks_count;
|
||||
i++, chunk += size)
|
||||
free_array[total_freed_chunks - 1 - i] = PointerToCompactPtr(0, chunk);
|
||||
if (kRandomShuffleChunks)
|
||||
RandomShuffle(&free_array[region->num_freed_chunks], new_chunks_count,
|
||||
®ion->rand_state);
|
||||
|
||||
// All necessary memory is mapped and now it is safe to advance all
|
||||
// 'allocated_*' counters.
|
||||
region->num_freed_chunks += new_chunks_count;
|
||||
region->allocated_user += new_chunks_count * size;
|
||||
CHECK_LE(region->allocated_user, region->mapped_user);
|
||||
region->allocated_meta += new_chunks_count * kMetadataSize;
|
||||
CHECK_LE(region->allocated_meta, region->mapped_meta);
|
||||
region->exhausted = false;
|
||||
|
||||
// TODO(alekseyshl): Consider bumping last_release_at_ns here to prevent
|
||||
// MaybeReleaseToOS from releasing just allocated pages or protect these
|
||||
// not yet used chunks some other way.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class MemoryMapper {
|
||||
public:
|
||||
MemoryMapper(const ThisT& base_allocator, uptr class_id)
|
||||
: allocator(base_allocator),
|
||||
region_base(base_allocator.GetRegionBeginBySizeClass(class_id)),
|
||||
released_ranges_count(0),
|
||||
released_bytes(0) {
|
||||
}
|
||||
|
||||
uptr GetReleasedRangesCount() const {
|
||||
return released_ranges_count;
|
||||
}
|
||||
|
||||
uptr GetReleasedBytes() const {
|
||||
return released_bytes;
|
||||
}
|
||||
|
||||
uptr MapPackedCounterArrayBuffer(uptr buffer_size) {
|
||||
// TODO(alekseyshl): The idea to explore is to check if we have enough
|
||||
// space between num_freed_chunks*sizeof(CompactPtrT) and
|
||||
// mapped_free_array to fit buffer_size bytes and use that space instead
|
||||
// of mapping a temporary one.
|
||||
return reinterpret_cast<uptr>(
|
||||
MmapOrDieOnFatalError(buffer_size, "ReleaseToOSPageCounters"));
|
||||
}
|
||||
|
||||
void UnmapPackedCounterArrayBuffer(uptr buffer, uptr buffer_size) {
|
||||
UnmapOrDie(reinterpret_cast<void *>(buffer), buffer_size);
|
||||
}
|
||||
|
||||
// Releases [from, to) range of pages back to OS.
|
||||
void ReleasePageRangeToOS(CompactPtrT from, CompactPtrT to) {
|
||||
const uptr from_page = allocator.CompactPtrToPointer(region_base, from);
|
||||
const uptr to_page = allocator.CompactPtrToPointer(region_base, to);
|
||||
ReleaseMemoryPagesToOS(from_page, to_page);
|
||||
released_ranges_count++;
|
||||
released_bytes += to_page - from_page;
|
||||
}
|
||||
|
||||
private:
|
||||
const ThisT& allocator;
|
||||
const uptr region_base;
|
||||
uptr released_ranges_count;
|
||||
uptr released_bytes;
|
||||
};
|
||||
|
||||
// Attempts to release RAM occupied by freed chunks back to OS. The region is
|
||||
// expected to be locked.
|
||||
void MaybeReleaseToOS(uptr class_id, bool force) {
|
||||
RegionInfo *region = GetRegionInfo(class_id);
|
||||
const uptr chunk_size = ClassIdToSize(class_id);
|
||||
const uptr page_size = GetPageSizeCached();
|
||||
|
||||
uptr n = region->num_freed_chunks;
|
||||
if (n * chunk_size < page_size)
|
||||
return; // No chance to release anything.
|
||||
if ((region->stats.n_freed -
|
||||
region->rtoi.n_freed_at_last_release) * chunk_size < page_size) {
|
||||
return; // Nothing new to release.
|
||||
}
|
||||
|
||||
if (!force) {
|
||||
s32 interval_ms = ReleaseToOSIntervalMs();
|
||||
if (interval_ms < 0)
|
||||
return;
|
||||
|
||||
if (region->rtoi.last_release_at_ns + interval_ms * 1000000ULL >
|
||||
MonotonicNanoTime()) {
|
||||
return; // Memory was returned recently.
|
||||
}
|
||||
}
|
||||
|
||||
MemoryMapper memory_mapper(*this, class_id);
|
||||
|
||||
ReleaseFreeMemoryToOS<MemoryMapper>(
|
||||
GetFreeArray(GetRegionBeginBySizeClass(class_id)), n, chunk_size,
|
||||
RoundUpTo(region->allocated_user, page_size) / page_size,
|
||||
&memory_mapper);
|
||||
|
||||
if (memory_mapper.GetReleasedRangesCount() > 0) {
|
||||
region->rtoi.n_freed_at_last_release = region->stats.n_freed;
|
||||
region->rtoi.num_releases += memory_mapper.GetReleasedRangesCount();
|
||||
region->rtoi.last_released_bytes = memory_mapper.GetReleasedBytes();
|
||||
}
|
||||
region->rtoi.last_release_at_ns = MonotonicNanoTime();
|
||||
}
|
||||
};
|
||||
39
lib/tsan/sanitizer_common/sanitizer_allocator_report.h
Normal file
39
lib/tsan/sanitizer_common/sanitizer_allocator_report.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//===-- sanitizer_allocator_report.h ----------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// Shared allocator error reporting for ThreadSanitizer, MemorySanitizer, etc.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ALLOCATOR_REPORT_H
|
||||
#define SANITIZER_ALLOCATOR_REPORT_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_stacktrace.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
void NORETURN ReportCallocOverflow(uptr count, uptr size,
|
||||
const StackTrace *stack);
|
||||
void NORETURN ReportReallocArrayOverflow(uptr count, uptr size,
|
||||
const StackTrace *stack);
|
||||
void NORETURN ReportPvallocOverflow(uptr size, const StackTrace *stack);
|
||||
void NORETURN ReportInvalidAllocationAlignment(uptr alignment,
|
||||
const StackTrace *stack);
|
||||
void NORETURN ReportInvalidAlignedAllocAlignment(uptr size, uptr alignment,
|
||||
const StackTrace *stack);
|
||||
void NORETURN ReportInvalidPosixMemalignAlignment(uptr alignment,
|
||||
const StackTrace *stack);
|
||||
void NORETURN ReportAllocationSizeTooBig(uptr user_size, uptr max_size,
|
||||
const StackTrace *stack);
|
||||
void NORETURN ReportOutOfMemory(uptr requested_size, const StackTrace *stack);
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_ALLOCATOR_REPORT_H
|
||||
326
lib/tsan/sanitizer_common/sanitizer_allocator_secondary.h
Normal file
326
lib/tsan/sanitizer_common/sanitizer_allocator_secondary.h
Normal file
@@ -0,0 +1,326 @@
|
||||
//===-- sanitizer_allocator_secondary.h -------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the Sanitizer Allocator.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_ALLOCATOR_H
|
||||
#error This file must be included inside sanitizer_allocator.h
|
||||
#endif
|
||||
|
||||
// Fixed array to store LargeMmapAllocator chunks list, limited to 32K total
|
||||
// allocated chunks. To be used in memory constrained or not memory hungry cases
|
||||
// (currently, 32 bits and internal allocator).
|
||||
class LargeMmapAllocatorPtrArrayStatic {
|
||||
public:
|
||||
INLINE void *Init() { return &p_[0]; }
|
||||
INLINE void EnsureSpace(uptr n) { CHECK_LT(n, kMaxNumChunks); }
|
||||
private:
|
||||
static const int kMaxNumChunks = 1 << 15;
|
||||
uptr p_[kMaxNumChunks];
|
||||
};
|
||||
|
||||
// Much less restricted LargeMmapAllocator chunks list (comparing to
|
||||
// PtrArrayStatic). Backed by mmaped memory region and can hold up to 1M chunks.
|
||||
// ReservedAddressRange was used instead of just MAP_NORESERVE to achieve the
|
||||
// same functionality in Fuchsia case, which does not support MAP_NORESERVE.
|
||||
class LargeMmapAllocatorPtrArrayDynamic {
|
||||
public:
|
||||
INLINE void *Init() {
|
||||
uptr p = address_range_.Init(kMaxNumChunks * sizeof(uptr),
|
||||
SecondaryAllocatorName);
|
||||
CHECK(p);
|
||||
return reinterpret_cast<void*>(p);
|
||||
}
|
||||
|
||||
INLINE void EnsureSpace(uptr n) {
|
||||
CHECK_LT(n, kMaxNumChunks);
|
||||
DCHECK(n <= n_reserved_);
|
||||
if (UNLIKELY(n == n_reserved_)) {
|
||||
address_range_.MapOrDie(
|
||||
reinterpret_cast<uptr>(address_range_.base()) +
|
||||
n_reserved_ * sizeof(uptr),
|
||||
kChunksBlockCount * sizeof(uptr));
|
||||
n_reserved_ += kChunksBlockCount;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kMaxNumChunks = 1 << 20;
|
||||
static const int kChunksBlockCount = 1 << 14;
|
||||
ReservedAddressRange address_range_;
|
||||
uptr n_reserved_;
|
||||
};
|
||||
|
||||
#if SANITIZER_WORDSIZE == 32
|
||||
typedef LargeMmapAllocatorPtrArrayStatic DefaultLargeMmapAllocatorPtrArray;
|
||||
#else
|
||||
typedef LargeMmapAllocatorPtrArrayDynamic DefaultLargeMmapAllocatorPtrArray;
|
||||
#endif
|
||||
|
||||
// This class can (de)allocate only large chunks of memory using mmap/unmap.
|
||||
// The main purpose of this allocator is to cover large and rare allocation
|
||||
// sizes not covered by more efficient allocators (e.g. SizeClassAllocator64).
|
||||
template <class MapUnmapCallback = NoOpMapUnmapCallback,
|
||||
class PtrArrayT = DefaultLargeMmapAllocatorPtrArray,
|
||||
class AddressSpaceViewTy = LocalAddressSpaceView>
|
||||
class LargeMmapAllocator {
|
||||
public:
|
||||
using AddressSpaceView = AddressSpaceViewTy;
|
||||
void InitLinkerInitialized() {
|
||||
page_size_ = GetPageSizeCached();
|
||||
chunks_ = reinterpret_cast<Header**>(ptr_array_.Init());
|
||||
}
|
||||
|
||||
void Init() {
|
||||
internal_memset(this, 0, sizeof(*this));
|
||||
InitLinkerInitialized();
|
||||
}
|
||||
|
||||
void *Allocate(AllocatorStats *stat, uptr size, uptr alignment) {
|
||||
CHECK(IsPowerOfTwo(alignment));
|
||||
uptr map_size = RoundUpMapSize(size);
|
||||
if (alignment > page_size_)
|
||||
map_size += alignment;
|
||||
// Overflow.
|
||||
if (map_size < size) {
|
||||
Report("WARNING: %s: LargeMmapAllocator allocation overflow: "
|
||||
"0x%zx bytes with 0x%zx alignment requested\n",
|
||||
SanitizerToolName, map_size, alignment);
|
||||
return nullptr;
|
||||
}
|
||||
uptr map_beg = reinterpret_cast<uptr>(
|
||||
MmapOrDieOnFatalError(map_size, SecondaryAllocatorName));
|
||||
if (!map_beg)
|
||||
return nullptr;
|
||||
CHECK(IsAligned(map_beg, page_size_));
|
||||
MapUnmapCallback().OnMap(map_beg, map_size);
|
||||
uptr map_end = map_beg + map_size;
|
||||
uptr res = map_beg + page_size_;
|
||||
if (res & (alignment - 1)) // Align.
|
||||
res += alignment - (res & (alignment - 1));
|
||||
CHECK(IsAligned(res, alignment));
|
||||
CHECK(IsAligned(res, page_size_));
|
||||
CHECK_GE(res + size, map_beg);
|
||||
CHECK_LE(res + size, map_end);
|
||||
Header *h = GetHeader(res);
|
||||
h->size = size;
|
||||
h->map_beg = map_beg;
|
||||
h->map_size = map_size;
|
||||
uptr size_log = MostSignificantSetBitIndex(map_size);
|
||||
CHECK_LT(size_log, ARRAY_SIZE(stats.by_size_log));
|
||||
{
|
||||
SpinMutexLock l(&mutex_);
|
||||
ptr_array_.EnsureSpace(n_chunks_);
|
||||
uptr idx = n_chunks_++;
|
||||
h->chunk_idx = idx;
|
||||
chunks_[idx] = h;
|
||||
chunks_sorted_ = false;
|
||||
stats.n_allocs++;
|
||||
stats.currently_allocated += map_size;
|
||||
stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated);
|
||||
stats.by_size_log[size_log]++;
|
||||
stat->Add(AllocatorStatAllocated, map_size);
|
||||
stat->Add(AllocatorStatMapped, map_size);
|
||||
}
|
||||
return reinterpret_cast<void*>(res);
|
||||
}
|
||||
|
||||
void Deallocate(AllocatorStats *stat, void *p) {
|
||||
Header *h = GetHeader(p);
|
||||
{
|
||||
SpinMutexLock l(&mutex_);
|
||||
uptr idx = h->chunk_idx;
|
||||
CHECK_EQ(chunks_[idx], h);
|
||||
CHECK_LT(idx, n_chunks_);
|
||||
chunks_[idx] = chunks_[--n_chunks_];
|
||||
chunks_[idx]->chunk_idx = idx;
|
||||
chunks_sorted_ = false;
|
||||
stats.n_frees++;
|
||||
stats.currently_allocated -= h->map_size;
|
||||
stat->Sub(AllocatorStatAllocated, h->map_size);
|
||||
stat->Sub(AllocatorStatMapped, h->map_size);
|
||||
}
|
||||
MapUnmapCallback().OnUnmap(h->map_beg, h->map_size);
|
||||
UnmapOrDie(reinterpret_cast<void*>(h->map_beg), h->map_size);
|
||||
}
|
||||
|
||||
uptr TotalMemoryUsed() {
|
||||
SpinMutexLock l(&mutex_);
|
||||
uptr res = 0;
|
||||
for (uptr i = 0; i < n_chunks_; i++) {
|
||||
Header *h = chunks_[i];
|
||||
CHECK_EQ(h->chunk_idx, i);
|
||||
res += RoundUpMapSize(h->size);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool PointerIsMine(const void *p) {
|
||||
return GetBlockBegin(p) != nullptr;
|
||||
}
|
||||
|
||||
uptr GetActuallyAllocatedSize(void *p) {
|
||||
return RoundUpTo(GetHeader(p)->size, page_size_);
|
||||
}
|
||||
|
||||
// At least page_size_/2 metadata bytes is available.
|
||||
void *GetMetaData(const void *p) {
|
||||
// Too slow: CHECK_EQ(p, GetBlockBegin(p));
|
||||
if (!IsAligned(reinterpret_cast<uptr>(p), page_size_)) {
|
||||
Printf("%s: bad pointer %p\n", SanitizerToolName, p);
|
||||
CHECK(IsAligned(reinterpret_cast<uptr>(p), page_size_));
|
||||
}
|
||||
return GetHeader(p) + 1;
|
||||
}
|
||||
|
||||
void *GetBlockBegin(const void *ptr) {
|
||||
uptr p = reinterpret_cast<uptr>(ptr);
|
||||
SpinMutexLock l(&mutex_);
|
||||
uptr nearest_chunk = 0;
|
||||
Header *const *chunks = AddressSpaceView::Load(chunks_, n_chunks_);
|
||||
// Cache-friendly linear search.
|
||||
for (uptr i = 0; i < n_chunks_; i++) {
|
||||
uptr ch = reinterpret_cast<uptr>(chunks[i]);
|
||||
if (p < ch) continue; // p is at left to this chunk, skip it.
|
||||
if (p - ch < p - nearest_chunk)
|
||||
nearest_chunk = ch;
|
||||
}
|
||||
if (!nearest_chunk)
|
||||
return nullptr;
|
||||
const Header *h =
|
||||
AddressSpaceView::Load(reinterpret_cast<Header *>(nearest_chunk));
|
||||
Header *h_ptr = reinterpret_cast<Header *>(nearest_chunk);
|
||||
CHECK_GE(nearest_chunk, h->map_beg);
|
||||
CHECK_LT(nearest_chunk, h->map_beg + h->map_size);
|
||||
CHECK_LE(nearest_chunk, p);
|
||||
if (h->map_beg + h->map_size <= p)
|
||||
return nullptr;
|
||||
return GetUser(h_ptr);
|
||||
}
|
||||
|
||||
void EnsureSortedChunks() {
|
||||
if (chunks_sorted_) return;
|
||||
Header **chunks = AddressSpaceView::LoadWritable(chunks_, n_chunks_);
|
||||
Sort(reinterpret_cast<uptr *>(chunks), n_chunks_);
|
||||
for (uptr i = 0; i < n_chunks_; i++)
|
||||
AddressSpaceView::LoadWritable(chunks[i])->chunk_idx = i;
|
||||
chunks_sorted_ = true;
|
||||
}
|
||||
|
||||
// This function does the same as GetBlockBegin, but is much faster.
|
||||
// Must be called with the allocator locked.
|
||||
void *GetBlockBeginFastLocked(void *ptr) {
|
||||
mutex_.CheckLocked();
|
||||
uptr p = reinterpret_cast<uptr>(ptr);
|
||||
uptr n = n_chunks_;
|
||||
if (!n) return nullptr;
|
||||
EnsureSortedChunks();
|
||||
Header *const *chunks = AddressSpaceView::Load(chunks_, n_chunks_);
|
||||
auto min_mmap_ = reinterpret_cast<uptr>(chunks[0]);
|
||||
auto max_mmap_ = reinterpret_cast<uptr>(chunks[n - 1]) +
|
||||
AddressSpaceView::Load(chunks[n - 1])->map_size;
|
||||
if (p < min_mmap_ || p >= max_mmap_)
|
||||
return nullptr;
|
||||
uptr beg = 0, end = n - 1;
|
||||
// This loop is a log(n) lower_bound. It does not check for the exact match
|
||||
// to avoid expensive cache-thrashing loads.
|
||||
while (end - beg >= 2) {
|
||||
uptr mid = (beg + end) / 2; // Invariant: mid >= beg + 1
|
||||
if (p < reinterpret_cast<uptr>(chunks[mid]))
|
||||
end = mid - 1; // We are not interested in chunks[mid].
|
||||
else
|
||||
beg = mid; // chunks[mid] may still be what we want.
|
||||
}
|
||||
|
||||
if (beg < end) {
|
||||
CHECK_EQ(beg + 1, end);
|
||||
// There are 2 chunks left, choose one.
|
||||
if (p >= reinterpret_cast<uptr>(chunks[end]))
|
||||
beg = end;
|
||||
}
|
||||
|
||||
const Header *h = AddressSpaceView::Load(chunks[beg]);
|
||||
Header *h_ptr = chunks[beg];
|
||||
if (h->map_beg + h->map_size <= p || p < h->map_beg)
|
||||
return nullptr;
|
||||
return GetUser(h_ptr);
|
||||
}
|
||||
|
||||
void PrintStats() {
|
||||
Printf("Stats: LargeMmapAllocator: allocated %zd times, "
|
||||
"remains %zd (%zd K) max %zd M; by size logs: ",
|
||||
stats.n_allocs, stats.n_allocs - stats.n_frees,
|
||||
stats.currently_allocated >> 10, stats.max_allocated >> 20);
|
||||
for (uptr i = 0; i < ARRAY_SIZE(stats.by_size_log); i++) {
|
||||
uptr c = stats.by_size_log[i];
|
||||
if (!c) continue;
|
||||
Printf("%zd:%zd; ", i, c);
|
||||
}
|
||||
Printf("\n");
|
||||
}
|
||||
|
||||
// ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
|
||||
// introspection API.
|
||||
void ForceLock() {
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
void ForceUnlock() {
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
// Iterate over all existing chunks.
|
||||
// The allocator must be locked when calling this function.
|
||||
void ForEachChunk(ForEachChunkCallback callback, void *arg) {
|
||||
EnsureSortedChunks(); // Avoid doing the sort while iterating.
|
||||
const Header *const *chunks = AddressSpaceView::Load(chunks_, n_chunks_);
|
||||
for (uptr i = 0; i < n_chunks_; i++) {
|
||||
const Header *t = chunks[i];
|
||||
callback(reinterpret_cast<uptr>(GetUser(t)), arg);
|
||||
// Consistency check: verify that the array did not change.
|
||||
CHECK_EQ(chunks[i], t);
|
||||
CHECK_EQ(AddressSpaceView::Load(chunks[i])->chunk_idx, i);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct Header {
|
||||
uptr map_beg;
|
||||
uptr map_size;
|
||||
uptr size;
|
||||
uptr chunk_idx;
|
||||
};
|
||||
|
||||
Header *GetHeader(uptr p) {
|
||||
CHECK(IsAligned(p, page_size_));
|
||||
return reinterpret_cast<Header*>(p - page_size_);
|
||||
}
|
||||
Header *GetHeader(const void *p) {
|
||||
return GetHeader(reinterpret_cast<uptr>(p));
|
||||
}
|
||||
|
||||
void *GetUser(const Header *h) {
|
||||
CHECK(IsAligned((uptr)h, page_size_));
|
||||
return reinterpret_cast<void*>(reinterpret_cast<uptr>(h) + page_size_);
|
||||
}
|
||||
|
||||
uptr RoundUpMapSize(uptr size) {
|
||||
return RoundUpTo(size, page_size_) + page_size_;
|
||||
}
|
||||
|
||||
uptr page_size_;
|
||||
Header **chunks_;
|
||||
PtrArrayT ptr_array_;
|
||||
uptr n_chunks_;
|
||||
bool chunks_sorted_;
|
||||
struct Stats {
|
||||
uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64];
|
||||
} stats;
|
||||
StaticSpinMutex mutex_;
|
||||
};
|
||||
241
lib/tsan/sanitizer_common/sanitizer_allocator_size_class_map.h
Normal file
241
lib/tsan/sanitizer_common/sanitizer_allocator_size_class_map.h
Normal file
@@ -0,0 +1,241 @@
|
||||
//===-- sanitizer_allocator_size_class_map.h --------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the Sanitizer Allocator.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_ALLOCATOR_H
|
||||
#error This file must be included inside sanitizer_allocator.h
|
||||
#endif
|
||||
|
||||
// SizeClassMap maps allocation sizes into size classes and back.
|
||||
// Class 0 always corresponds to size 0.
|
||||
// The other sizes are controlled by the template parameters:
|
||||
// kMinSizeLog: defines the class 1 as 2^kMinSizeLog.
|
||||
// kMaxSizeLog: defines the last class as 2^kMaxSizeLog.
|
||||
// kMidSizeLog: the classes starting from 1 increase with step
|
||||
// 2^kMinSizeLog until 2^kMidSizeLog.
|
||||
// kNumBits: the number of non-zero bits in sizes after 2^kMidSizeLog.
|
||||
// E.g. with kNumBits==3 all size classes after 2^kMidSizeLog
|
||||
// look like 0b1xx0..0, where x is either 0 or 1.
|
||||
//
|
||||
// Example: kNumBits=3, kMidSizeLog=4, kMidSizeLog=8, kMaxSizeLog=17:
|
||||
//
|
||||
// Classes 1 - 16 correspond to sizes 16 to 256 (size = class_id * 16).
|
||||
// Next 4 classes: 256 + i * 64 (i = 1 to 4).
|
||||
// Next 4 classes: 512 + i * 128 (i = 1 to 4).
|
||||
// ...
|
||||
// Next 4 classes: 2^k + i * 2^(k-2) (i = 1 to 4).
|
||||
// Last class corresponds to kMaxSize = 1 << kMaxSizeLog.
|
||||
//
|
||||
// This structure of the size class map gives us:
|
||||
// - Efficient table-free class-to-size and size-to-class functions.
|
||||
// - Difference between two consequent size classes is between 14% and 25%
|
||||
//
|
||||
// This class also gives a hint to a thread-caching allocator about the amount
|
||||
// of chunks that need to be cached per-thread:
|
||||
// - kMaxNumCachedHint is a hint for maximal number of chunks per size class.
|
||||
// The actual number is computed in TransferBatch.
|
||||
// - (1 << kMaxBytesCachedLog) is the maximal number of bytes per size class.
|
||||
//
|
||||
// Part of output of SizeClassMap::Print():
|
||||
// c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0
|
||||
// c01 => s: 16 diff: +16 00% l 4 cached: 256 4096; id 1
|
||||
// c02 => s: 32 diff: +16 100% l 5 cached: 256 8192; id 2
|
||||
// c03 => s: 48 diff: +16 50% l 5 cached: 256 12288; id 3
|
||||
// c04 => s: 64 diff: +16 33% l 6 cached: 256 16384; id 4
|
||||
// c05 => s: 80 diff: +16 25% l 6 cached: 256 20480; id 5
|
||||
// c06 => s: 96 diff: +16 20% l 6 cached: 256 24576; id 6
|
||||
// c07 => s: 112 diff: +16 16% l 6 cached: 256 28672; id 7
|
||||
//
|
||||
// c08 => s: 128 diff: +16 14% l 7 cached: 256 32768; id 8
|
||||
// c09 => s: 144 diff: +16 12% l 7 cached: 256 36864; id 9
|
||||
// c10 => s: 160 diff: +16 11% l 7 cached: 256 40960; id 10
|
||||
// c11 => s: 176 diff: +16 10% l 7 cached: 256 45056; id 11
|
||||
// c12 => s: 192 diff: +16 09% l 7 cached: 256 49152; id 12
|
||||
// c13 => s: 208 diff: +16 08% l 7 cached: 256 53248; id 13
|
||||
// c14 => s: 224 diff: +16 07% l 7 cached: 256 57344; id 14
|
||||
// c15 => s: 240 diff: +16 07% l 7 cached: 256 61440; id 15
|
||||
//
|
||||
// c16 => s: 256 diff: +16 06% l 8 cached: 256 65536; id 16
|
||||
// c17 => s: 320 diff: +64 25% l 8 cached: 204 65280; id 17
|
||||
// c18 => s: 384 diff: +64 20% l 8 cached: 170 65280; id 18
|
||||
// c19 => s: 448 diff: +64 16% l 8 cached: 146 65408; id 19
|
||||
//
|
||||
// c20 => s: 512 diff: +64 14% l 9 cached: 128 65536; id 20
|
||||
// c21 => s: 640 diff: +128 25% l 9 cached: 102 65280; id 21
|
||||
// c22 => s: 768 diff: +128 20% l 9 cached: 85 65280; id 22
|
||||
// c23 => s: 896 diff: +128 16% l 9 cached: 73 65408; id 23
|
||||
//
|
||||
// c24 => s: 1024 diff: +128 14% l 10 cached: 64 65536; id 24
|
||||
// c25 => s: 1280 diff: +256 25% l 10 cached: 51 65280; id 25
|
||||
// c26 => s: 1536 diff: +256 20% l 10 cached: 42 64512; id 26
|
||||
// c27 => s: 1792 diff: +256 16% l 10 cached: 36 64512; id 27
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// c48 => s: 65536 diff: +8192 14% l 16 cached: 1 65536; id 48
|
||||
// c49 => s: 81920 diff: +16384 25% l 16 cached: 1 81920; id 49
|
||||
// c50 => s: 98304 diff: +16384 20% l 16 cached: 1 98304; id 50
|
||||
// c51 => s: 114688 diff: +16384 16% l 16 cached: 1 114688; id 51
|
||||
//
|
||||
// c52 => s: 131072 diff: +16384 14% l 17 cached: 1 131072; id 52
|
||||
//
|
||||
//
|
||||
// Another example (kNumBits=2):
|
||||
// c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0
|
||||
// c01 => s: 32 diff: +32 00% l 5 cached: 64 2048; id 1
|
||||
// c02 => s: 64 diff: +32 100% l 6 cached: 64 4096; id 2
|
||||
// c03 => s: 96 diff: +32 50% l 6 cached: 64 6144; id 3
|
||||
// c04 => s: 128 diff: +32 33% l 7 cached: 64 8192; id 4
|
||||
// c05 => s: 160 diff: +32 25% l 7 cached: 64 10240; id 5
|
||||
// c06 => s: 192 diff: +32 20% l 7 cached: 64 12288; id 6
|
||||
// c07 => s: 224 diff: +32 16% l 7 cached: 64 14336; id 7
|
||||
// c08 => s: 256 diff: +32 14% l 8 cached: 64 16384; id 8
|
||||
// c09 => s: 384 diff: +128 50% l 8 cached: 42 16128; id 9
|
||||
// c10 => s: 512 diff: +128 33% l 9 cached: 32 16384; id 10
|
||||
// c11 => s: 768 diff: +256 50% l 9 cached: 21 16128; id 11
|
||||
// c12 => s: 1024 diff: +256 33% l 10 cached: 16 16384; id 12
|
||||
// c13 => s: 1536 diff: +512 50% l 10 cached: 10 15360; id 13
|
||||
// c14 => s: 2048 diff: +512 33% l 11 cached: 8 16384; id 14
|
||||
// c15 => s: 3072 diff: +1024 50% l 11 cached: 5 15360; id 15
|
||||
// c16 => s: 4096 diff: +1024 33% l 12 cached: 4 16384; id 16
|
||||
// c17 => s: 6144 diff: +2048 50% l 12 cached: 2 12288; id 17
|
||||
// c18 => s: 8192 diff: +2048 33% l 13 cached: 2 16384; id 18
|
||||
// c19 => s: 12288 diff: +4096 50% l 13 cached: 1 12288; id 19
|
||||
// c20 => s: 16384 diff: +4096 33% l 14 cached: 1 16384; id 20
|
||||
// c21 => s: 24576 diff: +8192 50% l 14 cached: 1 24576; id 21
|
||||
// c22 => s: 32768 diff: +8192 33% l 15 cached: 1 32768; id 22
|
||||
// c23 => s: 49152 diff: +16384 50% l 15 cached: 1 49152; id 23
|
||||
// c24 => s: 65536 diff: +16384 33% l 16 cached: 1 65536; id 24
|
||||
// c25 => s: 98304 diff: +32768 50% l 16 cached: 1 98304; id 25
|
||||
// c26 => s: 131072 diff: +32768 33% l 17 cached: 1 131072; id 26
|
||||
|
||||
template <uptr kNumBits, uptr kMinSizeLog, uptr kMidSizeLog, uptr kMaxSizeLog,
|
||||
uptr kMaxNumCachedHintT, uptr kMaxBytesCachedLog>
|
||||
class SizeClassMap {
|
||||
static const uptr kMinSize = 1 << kMinSizeLog;
|
||||
static const uptr kMidSize = 1 << kMidSizeLog;
|
||||
static const uptr kMidClass = kMidSize / kMinSize;
|
||||
static const uptr S = kNumBits - 1;
|
||||
static const uptr M = (1 << S) - 1;
|
||||
|
||||
public:
|
||||
// kMaxNumCachedHintT is a power of two. It serves as a hint
|
||||
// for the size of TransferBatch, the actual size could be a bit smaller.
|
||||
static const uptr kMaxNumCachedHint = kMaxNumCachedHintT;
|
||||
COMPILER_CHECK((kMaxNumCachedHint & (kMaxNumCachedHint - 1)) == 0);
|
||||
|
||||
static const uptr kMaxSize = 1UL << kMaxSizeLog;
|
||||
static const uptr kNumClasses =
|
||||
kMidClass + ((kMaxSizeLog - kMidSizeLog) << S) + 1 + 1;
|
||||
static const uptr kLargestClassID = kNumClasses - 2;
|
||||
static const uptr kBatchClassID = kNumClasses - 1;
|
||||
COMPILER_CHECK(kNumClasses >= 16 && kNumClasses <= 256);
|
||||
static const uptr kNumClassesRounded =
|
||||
kNumClasses <= 32 ? 32 :
|
||||
kNumClasses <= 64 ? 64 :
|
||||
kNumClasses <= 128 ? 128 : 256;
|
||||
|
||||
static uptr Size(uptr class_id) {
|
||||
// Estimate the result for kBatchClassID because this class does not know
|
||||
// the exact size of TransferBatch. It's OK since we are using the actual
|
||||
// sizeof(TransferBatch) where it matters.
|
||||
if (UNLIKELY(class_id == kBatchClassID))
|
||||
return kMaxNumCachedHint * sizeof(uptr);
|
||||
if (class_id <= kMidClass)
|
||||
return kMinSize * class_id;
|
||||
class_id -= kMidClass;
|
||||
uptr t = kMidSize << (class_id >> S);
|
||||
return t + (t >> S) * (class_id & M);
|
||||
}
|
||||
|
||||
static uptr ClassID(uptr size) {
|
||||
if (UNLIKELY(size > kMaxSize))
|
||||
return 0;
|
||||
if (size <= kMidSize)
|
||||
return (size + kMinSize - 1) >> kMinSizeLog;
|
||||
const uptr l = MostSignificantSetBitIndex(size);
|
||||
const uptr hbits = (size >> (l - S)) & M;
|
||||
const uptr lbits = size & ((1U << (l - S)) - 1);
|
||||
const uptr l1 = l - kMidSizeLog;
|
||||
return kMidClass + (l1 << S) + hbits + (lbits > 0);
|
||||
}
|
||||
|
||||
static uptr MaxCachedHint(uptr size) {
|
||||
DCHECK_LE(size, kMaxSize);
|
||||
if (UNLIKELY(size == 0))
|
||||
return 0;
|
||||
uptr n;
|
||||
// Force a 32-bit division if the template parameters allow for it.
|
||||
if (kMaxBytesCachedLog > 31 || kMaxSizeLog > 31)
|
||||
n = (1UL << kMaxBytesCachedLog) / size;
|
||||
else
|
||||
n = (1U << kMaxBytesCachedLog) / static_cast<u32>(size);
|
||||
return Max<uptr>(1U, Min(kMaxNumCachedHint, n));
|
||||
}
|
||||
|
||||
static void Print() {
|
||||
uptr prev_s = 0;
|
||||
uptr total_cached = 0;
|
||||
for (uptr i = 0; i < kNumClasses; i++) {
|
||||
uptr s = Size(i);
|
||||
if (s >= kMidSize / 2 && (s & (s - 1)) == 0)
|
||||
Printf("\n");
|
||||
uptr d = s - prev_s;
|
||||
uptr p = prev_s ? (d * 100 / prev_s) : 0;
|
||||
uptr l = s ? MostSignificantSetBitIndex(s) : 0;
|
||||
uptr cached = MaxCachedHint(s) * s;
|
||||
if (i == kBatchClassID)
|
||||
d = p = l = 0;
|
||||
Printf("c%02zd => s: %zd diff: +%zd %02zd%% l %zd "
|
||||
"cached: %zd %zd; id %zd\n",
|
||||
i, Size(i), d, p, l, MaxCachedHint(s), cached, ClassID(s));
|
||||
total_cached += cached;
|
||||
prev_s = s;
|
||||
}
|
||||
Printf("Total cached: %zd\n", total_cached);
|
||||
}
|
||||
|
||||
static void Validate() {
|
||||
for (uptr c = 1; c < kNumClasses; c++) {
|
||||
// Printf("Validate: c%zd\n", c);
|
||||
uptr s = Size(c);
|
||||
CHECK_NE(s, 0U);
|
||||
if (c == kBatchClassID)
|
||||
continue;
|
||||
CHECK_EQ(ClassID(s), c);
|
||||
if (c < kLargestClassID)
|
||||
CHECK_EQ(ClassID(s + 1), c + 1);
|
||||
CHECK_EQ(ClassID(s - 1), c);
|
||||
CHECK_GT(Size(c), Size(c - 1));
|
||||
}
|
||||
CHECK_EQ(ClassID(kMaxSize + 1), 0);
|
||||
|
||||
for (uptr s = 1; s <= kMaxSize; s++) {
|
||||
uptr c = ClassID(s);
|
||||
// Printf("s%zd => c%zd\n", s, c);
|
||||
CHECK_LT(c, kNumClasses);
|
||||
CHECK_GE(Size(c), s);
|
||||
if (c > 0)
|
||||
CHECK_LT(Size(c - 1), s);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef SizeClassMap<3, 4, 8, 17, 128, 16> DefaultSizeClassMap;
|
||||
typedef SizeClassMap<3, 4, 8, 17, 64, 14> CompactSizeClassMap;
|
||||
typedef SizeClassMap<2, 5, 9, 16, 64, 14> VeryCompactSizeClassMap;
|
||||
|
||||
// The following SizeClassMap only holds a way small number of cached entries,
|
||||
// allowing for denser per-class arrays, smaller memory footprint and usually
|
||||
// better performances in threaded environments.
|
||||
typedef SizeClassMap<3, 4, 8, 17, 8, 10> DenseSizeClassMap;
|
||||
// Similar to VeryCompact map above, this one has a small number of different
|
||||
// size classes, and also reduced thread-local caches.
|
||||
typedef SizeClassMap<2, 5, 9, 16, 8, 10> VeryDenseSizeClassMap;
|
||||
106
lib/tsan/sanitizer_common/sanitizer_allocator_stats.h
Normal file
106
lib/tsan/sanitizer_common/sanitizer_allocator_stats.h
Normal file
@@ -0,0 +1,106 @@
|
||||
//===-- sanitizer_allocator_stats.h -----------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the Sanitizer Allocator.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_ALLOCATOR_H
|
||||
#error This file must be included inside sanitizer_allocator.h
|
||||
#endif
|
||||
|
||||
// Memory allocator statistics
|
||||
enum AllocatorStat {
|
||||
AllocatorStatAllocated,
|
||||
AllocatorStatMapped,
|
||||
AllocatorStatCount
|
||||
};
|
||||
|
||||
typedef uptr AllocatorStatCounters[AllocatorStatCount];
|
||||
|
||||
// Per-thread stats, live in per-thread cache.
|
||||
class AllocatorStats {
|
||||
public:
|
||||
void Init() {
|
||||
internal_memset(this, 0, sizeof(*this));
|
||||
}
|
||||
void InitLinkerInitialized() {}
|
||||
|
||||
void Add(AllocatorStat i, uptr v) {
|
||||
v += atomic_load(&stats_[i], memory_order_relaxed);
|
||||
atomic_store(&stats_[i], v, memory_order_relaxed);
|
||||
}
|
||||
|
||||
void Sub(AllocatorStat i, uptr v) {
|
||||
v = atomic_load(&stats_[i], memory_order_relaxed) - v;
|
||||
atomic_store(&stats_[i], v, memory_order_relaxed);
|
||||
}
|
||||
|
||||
void Set(AllocatorStat i, uptr v) {
|
||||
atomic_store(&stats_[i], v, memory_order_relaxed);
|
||||
}
|
||||
|
||||
uptr Get(AllocatorStat i) const {
|
||||
return atomic_load(&stats_[i], memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AllocatorGlobalStats;
|
||||
AllocatorStats *next_;
|
||||
AllocatorStats *prev_;
|
||||
atomic_uintptr_t stats_[AllocatorStatCount];
|
||||
};
|
||||
|
||||
// Global stats, used for aggregation and querying.
|
||||
class AllocatorGlobalStats : public AllocatorStats {
|
||||
public:
|
||||
void InitLinkerInitialized() {
|
||||
next_ = this;
|
||||
prev_ = this;
|
||||
}
|
||||
void Init() {
|
||||
internal_memset(this, 0, sizeof(*this));
|
||||
InitLinkerInitialized();
|
||||
}
|
||||
|
||||
void Register(AllocatorStats *s) {
|
||||
SpinMutexLock l(&mu_);
|
||||
s->next_ = next_;
|
||||
s->prev_ = this;
|
||||
next_->prev_ = s;
|
||||
next_ = s;
|
||||
}
|
||||
|
||||
void Unregister(AllocatorStats *s) {
|
||||
SpinMutexLock l(&mu_);
|
||||
s->prev_->next_ = s->next_;
|
||||
s->next_->prev_ = s->prev_;
|
||||
for (int i = 0; i < AllocatorStatCount; i++)
|
||||
Add(AllocatorStat(i), s->Get(AllocatorStat(i)));
|
||||
}
|
||||
|
||||
void Get(AllocatorStatCounters s) const {
|
||||
internal_memset(s, 0, AllocatorStatCount * sizeof(uptr));
|
||||
SpinMutexLock l(&mu_);
|
||||
const AllocatorStats *stats = this;
|
||||
for (;;) {
|
||||
for (int i = 0; i < AllocatorStatCount; i++)
|
||||
s[i] += stats->Get(AllocatorStat(i));
|
||||
stats = stats->next_;
|
||||
if (stats == this)
|
||||
break;
|
||||
}
|
||||
// All stats must be non-negative.
|
||||
for (int i = 0; i < AllocatorStatCount; i++)
|
||||
s[i] = ((sptr)s[i]) >= 0 ? s[i] : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable StaticSpinMutex mu_;
|
||||
};
|
||||
|
||||
|
||||
68
lib/tsan/sanitizer_common/sanitizer_asm.h
Normal file
68
lib/tsan/sanitizer_common/sanitizer_asm.h
Normal file
@@ -0,0 +1,68 @@
|
||||
//===-- sanitizer_asm.h -----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Various support for assemebler.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Some toolchains do not support .cfi asm directives, so we have to hide
|
||||
// them inside macros.
|
||||
#if defined(__clang__) || \
|
||||
(defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM))
|
||||
// GCC defined __GCC_HAVE_DWARF2_CFI_ASM if it supports CFI.
|
||||
// Clang seems to support CFI by default (or not?).
|
||||
// We need two versions of macros: for inline asm and standalone asm files.
|
||||
# define CFI_INL_ADJUST_CFA_OFFSET(n) ".cfi_adjust_cfa_offset " #n ";"
|
||||
|
||||
# define CFI_STARTPROC .cfi_startproc
|
||||
# define CFI_ENDPROC .cfi_endproc
|
||||
# define CFI_ADJUST_CFA_OFFSET(n) .cfi_adjust_cfa_offset n
|
||||
# define CFI_DEF_CFA_OFFSET(n) .cfi_def_cfa_offset n
|
||||
# define CFI_REL_OFFSET(reg, n) .cfi_rel_offset reg, n
|
||||
# define CFI_OFFSET(reg, n) .cfi_offset reg, n
|
||||
# define CFI_DEF_CFA_REGISTER(reg) .cfi_def_cfa_register reg
|
||||
# define CFI_DEF_CFA(reg, n) .cfi_def_cfa reg, n
|
||||
# define CFI_RESTORE(reg) .cfi_restore reg
|
||||
|
||||
#else // No CFI
|
||||
# define CFI_INL_ADJUST_CFA_OFFSET(n)
|
||||
# define CFI_STARTPROC
|
||||
# define CFI_ENDPROC
|
||||
# define CFI_ADJUST_CFA_OFFSET(n)
|
||||
# define CFI_DEF_CFA_OFFSET(n)
|
||||
# define CFI_REL_OFFSET(reg, n)
|
||||
# define CFI_OFFSET(reg, n)
|
||||
# define CFI_DEF_CFA_REGISTER(reg)
|
||||
# define CFI_DEF_CFA(reg, n)
|
||||
# define CFI_RESTORE(reg)
|
||||
#endif
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
# define ASM_HIDDEN(symbol) .hidden symbol
|
||||
# define ASM_TYPE_FUNCTION(symbol) .type symbol, %function
|
||||
# define ASM_SIZE(symbol) .size symbol, .-symbol
|
||||
# define ASM_SYMBOL(symbol) symbol
|
||||
# define ASM_SYMBOL_INTERCEPTOR(symbol) symbol
|
||||
# define ASM_WRAPPER_NAME(symbol) __interceptor_##symbol
|
||||
#else
|
||||
# define ASM_HIDDEN(symbol)
|
||||
# define ASM_TYPE_FUNCTION(symbol)
|
||||
# define ASM_SIZE(symbol)
|
||||
# define ASM_SYMBOL(symbol) _##symbol
|
||||
# define ASM_SYMBOL_INTERCEPTOR(symbol) _wrap_##symbol
|
||||
# define ASM_WRAPPER_NAME(symbol) __interceptor_##symbol
|
||||
#endif
|
||||
|
||||
#if defined(__ELF__) && (defined(__GNU__) || defined(__FreeBSD__) || \
|
||||
defined(__Fuchsia__) || defined(__linux__))
|
||||
// clang-format off
|
||||
#define NO_EXEC_STACK_DIRECTIVE .section .note.GNU-stack,"",%progbits // NOLINT
|
||||
// clang-format on
|
||||
#else
|
||||
#define NO_EXEC_STACK_DIRECTIVE
|
||||
#endif
|
||||
86
lib/tsan/sanitizer_common/sanitizer_atomic.h
Normal file
86
lib/tsan/sanitizer_common/sanitizer_atomic.h
Normal file
@@ -0,0 +1,86 @@
|
||||
//===-- sanitizer_atomic.h --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ATOMIC_H
|
||||
#define SANITIZER_ATOMIC_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
enum memory_order {
|
||||
memory_order_relaxed = 1 << 0,
|
||||
memory_order_consume = 1 << 1,
|
||||
memory_order_acquire = 1 << 2,
|
||||
memory_order_release = 1 << 3,
|
||||
memory_order_acq_rel = 1 << 4,
|
||||
memory_order_seq_cst = 1 << 5
|
||||
};
|
||||
|
||||
struct atomic_uint8_t {
|
||||
typedef u8 Type;
|
||||
volatile Type val_dont_use;
|
||||
};
|
||||
|
||||
struct atomic_uint16_t {
|
||||
typedef u16 Type;
|
||||
volatile Type val_dont_use;
|
||||
};
|
||||
|
||||
struct atomic_sint32_t {
|
||||
typedef s32 Type;
|
||||
volatile Type val_dont_use;
|
||||
};
|
||||
|
||||
struct atomic_uint32_t {
|
||||
typedef u32 Type;
|
||||
volatile Type val_dont_use;
|
||||
};
|
||||
|
||||
struct atomic_uint64_t {
|
||||
typedef u64 Type;
|
||||
// On 32-bit platforms u64 is not necessary aligned on 8 bytes.
|
||||
volatile ALIGNED(8) Type val_dont_use;
|
||||
};
|
||||
|
||||
struct atomic_uintptr_t {
|
||||
typedef uptr Type;
|
||||
volatile Type val_dont_use;
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
# include "sanitizer_atomic_clang.h"
|
||||
#elif defined(_MSC_VER)
|
||||
# include "sanitizer_atomic_msvc.h"
|
||||
#else
|
||||
# error "Unsupported compiler"
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Clutter-reducing helpers.
|
||||
|
||||
template<typename T>
|
||||
INLINE typename T::Type atomic_load_relaxed(const volatile T *a) {
|
||||
return atomic_load(a, memory_order_relaxed);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void atomic_store_relaxed(volatile T *a, typename T::Type v) {
|
||||
atomic_store(a, v, memory_order_relaxed);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_ATOMIC_H
|
||||
105
lib/tsan/sanitizer_common/sanitizer_atomic_clang.h
Normal file
105
lib/tsan/sanitizer_common/sanitizer_atomic_clang.h
Normal file
@@ -0,0 +1,105 @@
|
||||
//===-- sanitizer_atomic_clang.h --------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
// Not intended for direct inclusion. Include sanitizer_atomic.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ATOMIC_CLANG_H
|
||||
#define SANITIZER_ATOMIC_CLANG_H
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
# include "sanitizer_atomic_clang_x86.h"
|
||||
#else
|
||||
# include "sanitizer_atomic_clang_other.h"
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// We would like to just use compiler builtin atomic operations
|
||||
// for loads and stores, but they are mostly broken in clang:
|
||||
// - they lead to vastly inefficient code generation
|
||||
// (http://llvm.org/bugs/show_bug.cgi?id=17281)
|
||||
// - 64-bit atomic operations are not implemented on x86_32
|
||||
// (http://llvm.org/bugs/show_bug.cgi?id=15034)
|
||||
// - they are not implemented on ARM
|
||||
// error: undefined reference to '__atomic_load_4'
|
||||
|
||||
// See http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
|
||||
// for mappings of the memory model to different processors.
|
||||
|
||||
INLINE void atomic_signal_fence(memory_order) {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
}
|
||||
|
||||
INLINE void atomic_thread_fence(memory_order) {
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE typename T::Type atomic_fetch_add(volatile T *a,
|
||||
typename T::Type v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return __sync_fetch_and_add(&a->val_dont_use, v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE typename T::Type atomic_fetch_sub(volatile T *a,
|
||||
typename T::Type v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return __sync_fetch_and_add(&a->val_dont_use, -v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE typename T::Type atomic_exchange(volatile T *a,
|
||||
typename T::Type v, memory_order mo) {
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
if (mo & (memory_order_release | memory_order_acq_rel | memory_order_seq_cst))
|
||||
__sync_synchronize();
|
||||
v = __sync_lock_test_and_set(&a->val_dont_use, v);
|
||||
if (mo == memory_order_seq_cst)
|
||||
__sync_synchronize();
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
INLINE bool atomic_compare_exchange_strong(volatile T *a, typename T::Type *cmp,
|
||||
typename T::Type xchg,
|
||||
memory_order mo) {
|
||||
typedef typename T::Type Type;
|
||||
Type cmpv = *cmp;
|
||||
Type prev;
|
||||
prev = __sync_val_compare_and_swap(&a->val_dont_use, cmpv, xchg);
|
||||
if (prev == cmpv) return true;
|
||||
*cmp = prev;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE bool atomic_compare_exchange_weak(volatile T *a,
|
||||
typename T::Type *cmp,
|
||||
typename T::Type xchg,
|
||||
memory_order mo) {
|
||||
return atomic_compare_exchange_strong(a, cmp, xchg, mo);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
// This include provides explicit template instantiations for atomic_uint64_t
|
||||
// on MIPS32, which does not directly support 8 byte atomics. It has to
|
||||
// proceed the template definitions above.
|
||||
#if defined(_MIPS_SIM) && defined(_ABIO32)
|
||||
#include "sanitizer_atomic_clang_mips.h"
|
||||
#endif
|
||||
|
||||
#undef ATOMIC_ORDER
|
||||
|
||||
#endif // SANITIZER_ATOMIC_CLANG_H
|
||||
117
lib/tsan/sanitizer_common/sanitizer_atomic_clang_mips.h
Normal file
117
lib/tsan/sanitizer_common/sanitizer_atomic_clang_mips.h
Normal file
@@ -0,0 +1,117 @@
|
||||
//===-- sanitizer_atomic_clang_mips.h ---------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
// Not intended for direct inclusion. Include sanitizer_atomic.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ATOMIC_CLANG_MIPS_H
|
||||
#define SANITIZER_ATOMIC_CLANG_MIPS_H
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// MIPS32 does not support atomics > 4 bytes. To address this lack of
|
||||
// functionality, the sanitizer library provides helper methods which use an
|
||||
// internal spin lock mechanism to emulate atomic oprations when the size is
|
||||
// 8 bytes.
|
||||
static void __spin_lock(volatile int *lock) {
|
||||
while (__sync_lock_test_and_set(lock, 1))
|
||||
while (*lock) {
|
||||
}
|
||||
}
|
||||
|
||||
static void __spin_unlock(volatile int *lock) { __sync_lock_release(lock); }
|
||||
|
||||
// Make sure the lock is on its own cache line to prevent false sharing.
|
||||
// Put it inside a struct that is aligned and padded to the typical MIPS
|
||||
// cacheline which is 32 bytes.
|
||||
static struct {
|
||||
int lock;
|
||||
char pad[32 - sizeof(int)];
|
||||
} __attribute__((aligned(32))) lock = {0, {0}};
|
||||
|
||||
template <>
|
||||
INLINE atomic_uint64_t::Type atomic_fetch_add(volatile atomic_uint64_t *ptr,
|
||||
atomic_uint64_t::Type val,
|
||||
memory_order mo) {
|
||||
DCHECK(mo &
|
||||
(memory_order_relaxed | memory_order_releasae | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)ptr % sizeof(*ptr)));
|
||||
|
||||
atomic_uint64_t::Type ret;
|
||||
|
||||
__spin_lock(&lock.lock);
|
||||
ret = *(const_cast<atomic_uint64_t::Type volatile *>(&ptr->val_dont_use));
|
||||
ptr->val_dont_use = ret + val;
|
||||
__spin_unlock(&lock.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <>
|
||||
INLINE atomic_uint64_t::Type atomic_fetch_sub(volatile atomic_uint64_t *ptr,
|
||||
atomic_uint64_t::Type val,
|
||||
memory_order mo) {
|
||||
return atomic_fetch_add(ptr, -val, mo);
|
||||
}
|
||||
|
||||
template <>
|
||||
INLINE bool atomic_compare_exchange_strong(volatile atomic_uint64_t *ptr,
|
||||
atomic_uint64_t::Type *cmp,
|
||||
atomic_uint64_t::Type xchg,
|
||||
memory_order mo) {
|
||||
DCHECK(mo &
|
||||
(memory_order_relaxed | memory_order_releasae | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)ptr % sizeof(*ptr)));
|
||||
|
||||
typedef atomic_uint64_t::Type Type;
|
||||
Type cmpv = *cmp;
|
||||
Type prev;
|
||||
bool ret = false;
|
||||
|
||||
__spin_lock(&lock.lock);
|
||||
prev = *(const_cast<Type volatile *>(&ptr->val_dont_use));
|
||||
if (prev == cmpv) {
|
||||
ret = true;
|
||||
ptr->val_dont_use = xchg;
|
||||
}
|
||||
__spin_unlock(&lock.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <>
|
||||
INLINE atomic_uint64_t::Type atomic_load(const volatile atomic_uint64_t *ptr,
|
||||
memory_order mo) {
|
||||
DCHECK(mo &
|
||||
(memory_order_relaxed | memory_order_releasae | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)ptr % sizeof(*ptr)));
|
||||
|
||||
atomic_uint64_t::Type zero = 0;
|
||||
volatile atomic_uint64_t *Newptr =
|
||||
const_cast<volatile atomic_uint64_t *>(ptr);
|
||||
return atomic_fetch_add(Newptr, zero, mo);
|
||||
}
|
||||
|
||||
template <>
|
||||
INLINE void atomic_store(volatile atomic_uint64_t *ptr, atomic_uint64_t::Type v,
|
||||
memory_order mo) {
|
||||
DCHECK(mo &
|
||||
(memory_order_relaxed | memory_order_releasae | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)ptr % sizeof(*ptr)));
|
||||
|
||||
__spin_lock(&lock.lock);
|
||||
ptr->val_dont_use = v;
|
||||
__spin_unlock(&lock.lock);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_ATOMIC_CLANG_MIPS_H
|
||||
|
||||
97
lib/tsan/sanitizer_common/sanitizer_atomic_clang_other.h
Normal file
97
lib/tsan/sanitizer_common/sanitizer_atomic_clang_other.h
Normal file
@@ -0,0 +1,97 @@
|
||||
//===-- sanitizer_atomic_clang_other.h --------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
// Not intended for direct inclusion. Include sanitizer_atomic.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
|
||||
#define SANITIZER_ATOMIC_CLANG_OTHER_H
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
|
||||
INLINE void proc_yield(int cnt) {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE typename T::Type atomic_load(
|
||||
const volatile T *a, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_consume
|
||||
| memory_order_acquire | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
typename T::Type v;
|
||||
|
||||
if (sizeof(*a) < 8 || sizeof(void*) == 8) {
|
||||
// Assume that aligned loads are atomic.
|
||||
if (mo == memory_order_relaxed) {
|
||||
v = a->val_dont_use;
|
||||
} else if (mo == memory_order_consume) {
|
||||
// Assume that processor respects data dependencies
|
||||
// (and that compiler won't break them).
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
v = a->val_dont_use;
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
} else if (mo == memory_order_acquire) {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
v = a->val_dont_use;
|
||||
__sync_synchronize();
|
||||
} else { // seq_cst
|
||||
// E.g. on POWER we need a hw fence even before the store.
|
||||
__sync_synchronize();
|
||||
v = a->val_dont_use;
|
||||
__sync_synchronize();
|
||||
}
|
||||
} else {
|
||||
// 64-bit load on 32-bit platform.
|
||||
// Gross, but simple and reliable.
|
||||
// Assume that it is not in read-only memory.
|
||||
v = __sync_fetch_and_add(
|
||||
const_cast<typename T::Type volatile *>(&a->val_dont_use), 0);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_release
|
||||
| memory_order_seq_cst));
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
|
||||
if (sizeof(*a) < 8 || sizeof(void*) == 8) {
|
||||
// Assume that aligned loads are atomic.
|
||||
if (mo == memory_order_relaxed) {
|
||||
a->val_dont_use = v;
|
||||
} else if (mo == memory_order_release) {
|
||||
__sync_synchronize();
|
||||
a->val_dont_use = v;
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
} else { // seq_cst
|
||||
__sync_synchronize();
|
||||
a->val_dont_use = v;
|
||||
__sync_synchronize();
|
||||
}
|
||||
} else {
|
||||
// 64-bit store on 32-bit platform.
|
||||
// Gross, but simple and reliable.
|
||||
typename T::Type cmp = a->val_dont_use;
|
||||
typename T::Type cur;
|
||||
for (;;) {
|
||||
cur = __sync_val_compare_and_swap(&a->val_dont_use, cmp, v);
|
||||
if (cur == cmp || cur == v)
|
||||
break;
|
||||
cmp = cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // #ifndef SANITIZER_ATOMIC_CLANG_OTHER_H
|
||||
113
lib/tsan/sanitizer_common/sanitizer_atomic_clang_x86.h
Normal file
113
lib/tsan/sanitizer_common/sanitizer_atomic_clang_x86.h
Normal file
@@ -0,0 +1,113 @@
|
||||
//===-- sanitizer_atomic_clang_x86.h ----------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
// Not intended for direct inclusion. Include sanitizer_atomic.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ATOMIC_CLANG_X86_H
|
||||
#define SANITIZER_ATOMIC_CLANG_X86_H
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
INLINE void proc_yield(int cnt) {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
for (int i = 0; i < cnt; i++)
|
||||
__asm__ __volatile__("pause");
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE typename T::Type atomic_load(
|
||||
const volatile T *a, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_consume
|
||||
| memory_order_acquire | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
typename T::Type v;
|
||||
|
||||
if (sizeof(*a) < 8 || sizeof(void*) == 8) {
|
||||
// Assume that aligned loads are atomic.
|
||||
if (mo == memory_order_relaxed) {
|
||||
v = a->val_dont_use;
|
||||
} else if (mo == memory_order_consume) {
|
||||
// Assume that processor respects data dependencies
|
||||
// (and that compiler won't break them).
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
v = a->val_dont_use;
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
} else if (mo == memory_order_acquire) {
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
v = a->val_dont_use;
|
||||
// On x86 loads are implicitly acquire.
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
} else { // seq_cst
|
||||
// On x86 plain MOV is enough for seq_cst store.
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
v = a->val_dont_use;
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
}
|
||||
} else {
|
||||
// 64-bit load on 32-bit platform.
|
||||
__asm__ __volatile__(
|
||||
"movq %1, %%mm0;" // Use mmx reg for 64-bit atomic moves
|
||||
"movq %%mm0, %0;" // (ptr could be read-only)
|
||||
"emms;" // Empty mmx state/Reset FP regs
|
||||
: "=m" (v)
|
||||
: "m" (a->val_dont_use)
|
||||
: // mark the mmx registers as clobbered
|
||||
#ifdef __MMX__
|
||||
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
|
||||
#endif // #ifdef __MMX__
|
||||
"memory");
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_release
|
||||
| memory_order_seq_cst));
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
|
||||
if (sizeof(*a) < 8 || sizeof(void*) == 8) {
|
||||
// Assume that aligned loads are atomic.
|
||||
if (mo == memory_order_relaxed) {
|
||||
a->val_dont_use = v;
|
||||
} else if (mo == memory_order_release) {
|
||||
// On x86 stores are implicitly release.
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
a->val_dont_use = v;
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
} else { // seq_cst
|
||||
// On x86 stores are implicitly release.
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
a->val_dont_use = v;
|
||||
__sync_synchronize();
|
||||
}
|
||||
} else {
|
||||
// 64-bit store on 32-bit platform.
|
||||
__asm__ __volatile__(
|
||||
"movq %1, %%mm0;" // Use mmx reg for 64-bit atomic moves
|
||||
"movq %%mm0, %0;"
|
||||
"emms;" // Empty mmx state/Reset FP regs
|
||||
: "=m" (a->val_dont_use)
|
||||
: "m" (v)
|
||||
: // mark the mmx registers as clobbered
|
||||
#ifdef __MMX__
|
||||
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
|
||||
#endif // #ifdef __MMX__
|
||||
"memory");
|
||||
if (mo == memory_order_seq_cst)
|
||||
__sync_synchronize();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // #ifndef SANITIZER_ATOMIC_CLANG_X86_H
|
||||
256
lib/tsan/sanitizer_common/sanitizer_atomic_msvc.h
Normal file
256
lib/tsan/sanitizer_common/sanitizer_atomic_msvc.h
Normal file
@@ -0,0 +1,256 @@
|
||||
//===-- sanitizer_atomic_msvc.h ---------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
// Not intended for direct inclusion. Include sanitizer_atomic.h.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ATOMIC_MSVC_H
|
||||
#define SANITIZER_ATOMIC_MSVC_H
|
||||
|
||||
extern "C" void _ReadWriteBarrier();
|
||||
#pragma intrinsic(_ReadWriteBarrier)
|
||||
extern "C" void _mm_mfence();
|
||||
#pragma intrinsic(_mm_mfence)
|
||||
extern "C" void _mm_pause();
|
||||
#pragma intrinsic(_mm_pause)
|
||||
extern "C" char _InterlockedExchange8(char volatile *Addend, char Value);
|
||||
#pragma intrinsic(_InterlockedExchange8)
|
||||
extern "C" short _InterlockedExchange16(short volatile *Addend, short Value);
|
||||
#pragma intrinsic(_InterlockedExchange16)
|
||||
extern "C" long _InterlockedExchange(long volatile *Addend, long Value);
|
||||
#pragma intrinsic(_InterlockedExchange)
|
||||
extern "C" long _InterlockedExchangeAdd(long volatile *Addend, long Value);
|
||||
#pragma intrinsic(_InterlockedExchangeAdd)
|
||||
extern "C" char _InterlockedCompareExchange8(char volatile *Destination,
|
||||
char Exchange, char Comparand);
|
||||
#pragma intrinsic(_InterlockedCompareExchange8)
|
||||
extern "C" short _InterlockedCompareExchange16(short volatile *Destination,
|
||||
short Exchange, short Comparand);
|
||||
#pragma intrinsic(_InterlockedCompareExchange16)
|
||||
extern "C" long long _InterlockedCompareExchange64(
|
||||
long long volatile *Destination, long long Exchange, long long Comparand);
|
||||
#pragma intrinsic(_InterlockedCompareExchange64)
|
||||
extern "C" void *_InterlockedCompareExchangePointer(
|
||||
void *volatile *Destination,
|
||||
void *Exchange, void *Comparand);
|
||||
#pragma intrinsic(_InterlockedCompareExchangePointer)
|
||||
extern "C" long __cdecl _InterlockedCompareExchange(long volatile *Destination,
|
||||
long Exchange,
|
||||
long Comparand);
|
||||
#pragma intrinsic(_InterlockedCompareExchange)
|
||||
|
||||
#ifdef _WIN64
|
||||
extern "C" long long _InterlockedExchangeAdd64(long long volatile *Addend,
|
||||
long long Value);
|
||||
#pragma intrinsic(_InterlockedExchangeAdd64)
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
INLINE void atomic_signal_fence(memory_order) {
|
||||
_ReadWriteBarrier();
|
||||
}
|
||||
|
||||
INLINE void atomic_thread_fence(memory_order) {
|
||||
_mm_mfence();
|
||||
}
|
||||
|
||||
INLINE void proc_yield(int cnt) {
|
||||
for (int i = 0; i < cnt; i++)
|
||||
_mm_pause();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE typename T::Type atomic_load(
|
||||
const volatile T *a, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_consume
|
||||
| memory_order_acquire | memory_order_seq_cst));
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
typename T::Type v;
|
||||
// FIXME(dvyukov): 64-bit load is not atomic on 32-bits.
|
||||
if (mo == memory_order_relaxed) {
|
||||
v = a->val_dont_use;
|
||||
} else {
|
||||
atomic_signal_fence(memory_order_seq_cst);
|
||||
v = a->val_dont_use;
|
||||
atomic_signal_fence(memory_order_seq_cst);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
|
||||
DCHECK(mo & (memory_order_relaxed | memory_order_release
|
||||
| memory_order_seq_cst));
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
// FIXME(dvyukov): 64-bit store is not atomic on 32-bits.
|
||||
if (mo == memory_order_relaxed) {
|
||||
a->val_dont_use = v;
|
||||
} else {
|
||||
atomic_signal_fence(memory_order_seq_cst);
|
||||
a->val_dont_use = v;
|
||||
atomic_signal_fence(memory_order_seq_cst);
|
||||
}
|
||||
if (mo == memory_order_seq_cst)
|
||||
atomic_thread_fence(memory_order_seq_cst);
|
||||
}
|
||||
|
||||
INLINE u32 atomic_fetch_add(volatile atomic_uint32_t *a,
|
||||
u32 v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return (u32)_InterlockedExchangeAdd((volatile long *)&a->val_dont_use,
|
||||
(long)v);
|
||||
}
|
||||
|
||||
INLINE uptr atomic_fetch_add(volatile atomic_uintptr_t *a,
|
||||
uptr v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
#ifdef _WIN64
|
||||
return (uptr)_InterlockedExchangeAdd64((volatile long long *)&a->val_dont_use,
|
||||
(long long)v);
|
||||
#else
|
||||
return (uptr)_InterlockedExchangeAdd((volatile long *)&a->val_dont_use,
|
||||
(long)v);
|
||||
#endif
|
||||
}
|
||||
|
||||
INLINE u32 atomic_fetch_sub(volatile atomic_uint32_t *a,
|
||||
u32 v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return (u32)_InterlockedExchangeAdd((volatile long *)&a->val_dont_use,
|
||||
-(long)v);
|
||||
}
|
||||
|
||||
INLINE uptr atomic_fetch_sub(volatile atomic_uintptr_t *a,
|
||||
uptr v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
#ifdef _WIN64
|
||||
return (uptr)_InterlockedExchangeAdd64((volatile long long *)&a->val_dont_use,
|
||||
-(long long)v);
|
||||
#else
|
||||
return (uptr)_InterlockedExchangeAdd((volatile long *)&a->val_dont_use,
|
||||
-(long)v);
|
||||
#endif
|
||||
}
|
||||
|
||||
INLINE u8 atomic_exchange(volatile atomic_uint8_t *a,
|
||||
u8 v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return (u8)_InterlockedExchange8((volatile char*)&a->val_dont_use, v);
|
||||
}
|
||||
|
||||
INLINE u16 atomic_exchange(volatile atomic_uint16_t *a,
|
||||
u16 v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return (u16)_InterlockedExchange16((volatile short*)&a->val_dont_use, v);
|
||||
}
|
||||
|
||||
INLINE u32 atomic_exchange(volatile atomic_uint32_t *a,
|
||||
u32 v, memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
return (u32)_InterlockedExchange((volatile long*)&a->val_dont_use, v);
|
||||
}
|
||||
|
||||
INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a,
|
||||
u8 *cmp,
|
||||
u8 xchgv,
|
||||
memory_order mo) {
|
||||
(void)mo;
|
||||
DCHECK(!((uptr)a % sizeof(*a)));
|
||||
u8 cmpv = *cmp;
|
||||
#ifdef _WIN64
|
||||
u8 prev = (u8)_InterlockedCompareExchange8(
|
||||
(volatile char*)&a->val_dont_use, (char)xchgv, (char)cmpv);
|
||||
#else
|
||||
u8 prev;
|
||||
__asm {
|
||||
mov al, cmpv
|
||||
mov ecx, a
|
||||
mov dl, xchgv
|
||||
lock cmpxchg [ecx], dl
|
||||
mov prev, al
|
||||
}
|
||||
#endif
|
||||
if (prev == cmpv)
|
||||
return true;
|
||||
*cmp = prev;
|
||||
return false;
|
||||
}
|
||||
|
||||
INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a,
|
||||
uptr *cmp,
|
||||
uptr xchg,
|
||||
memory_order mo) {
|
||||
uptr cmpv = *cmp;
|
||||
uptr prev = (uptr)_InterlockedCompareExchangePointer(
|
||||
(void*volatile*)&a->val_dont_use, (void*)xchg, (void*)cmpv);
|
||||
if (prev == cmpv)
|
||||
return true;
|
||||
*cmp = prev;
|
||||
return false;
|
||||
}
|
||||
|
||||
INLINE bool atomic_compare_exchange_strong(volatile atomic_uint16_t *a,
|
||||
u16 *cmp,
|
||||
u16 xchg,
|
||||
memory_order mo) {
|
||||
u16 cmpv = *cmp;
|
||||
u16 prev = (u16)_InterlockedCompareExchange16(
|
||||
(volatile short*)&a->val_dont_use, (short)xchg, (short)cmpv);
|
||||
if (prev == cmpv)
|
||||
return true;
|
||||
*cmp = prev;
|
||||
return false;
|
||||
}
|
||||
|
||||
INLINE bool atomic_compare_exchange_strong(volatile atomic_uint32_t *a,
|
||||
u32 *cmp,
|
||||
u32 xchg,
|
||||
memory_order mo) {
|
||||
u32 cmpv = *cmp;
|
||||
u32 prev = (u32)_InterlockedCompareExchange(
|
||||
(volatile long*)&a->val_dont_use, (long)xchg, (long)cmpv);
|
||||
if (prev == cmpv)
|
||||
return true;
|
||||
*cmp = prev;
|
||||
return false;
|
||||
}
|
||||
|
||||
INLINE bool atomic_compare_exchange_strong(volatile atomic_uint64_t *a,
|
||||
u64 *cmp,
|
||||
u64 xchg,
|
||||
memory_order mo) {
|
||||
u64 cmpv = *cmp;
|
||||
u64 prev = (u64)_InterlockedCompareExchange64(
|
||||
(volatile long long*)&a->val_dont_use, (long long)xchg, (long long)cmpv);
|
||||
if (prev == cmpv)
|
||||
return true;
|
||||
*cmp = prev;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE bool atomic_compare_exchange_weak(volatile T *a,
|
||||
typename T::Type *cmp,
|
||||
typename T::Type xchg,
|
||||
memory_order mo) {
|
||||
return atomic_compare_exchange_strong(a, cmp, xchg, mo);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_ATOMIC_CLANG_H
|
||||
350
lib/tsan/sanitizer_common/sanitizer_bitvector.h
Normal file
350
lib/tsan/sanitizer_common/sanitizer_bitvector.h
Normal file
@@ -0,0 +1,350 @@
|
||||
//===-- sanitizer_bitvector.h -----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Specializer BitVector implementation.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_BITVECTOR_H
|
||||
#define SANITIZER_BITVECTOR_H
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Fixed size bit vector based on a single basic integer.
|
||||
template <class basic_int_t = uptr>
|
||||
class BasicBitVector {
|
||||
public:
|
||||
enum SizeEnum : uptr { kSize = sizeof(basic_int_t) * 8 };
|
||||
|
||||
uptr size() const { return kSize; }
|
||||
// No CTOR.
|
||||
void clear() { bits_ = 0; }
|
||||
void setAll() { bits_ = ~(basic_int_t)0; }
|
||||
bool empty() const { return bits_ == 0; }
|
||||
|
||||
// Returns true if the bit has changed from 0 to 1.
|
||||
bool setBit(uptr idx) {
|
||||
basic_int_t old = bits_;
|
||||
bits_ |= mask(idx);
|
||||
return bits_ != old;
|
||||
}
|
||||
|
||||
// Returns true if the bit has changed from 1 to 0.
|
||||
bool clearBit(uptr idx) {
|
||||
basic_int_t old = bits_;
|
||||
bits_ &= ~mask(idx);
|
||||
return bits_ != old;
|
||||
}
|
||||
|
||||
bool getBit(uptr idx) const { return (bits_ & mask(idx)) != 0; }
|
||||
|
||||
uptr getAndClearFirstOne() {
|
||||
CHECK(!empty());
|
||||
uptr idx = LeastSignificantSetBitIndex(bits_);
|
||||
clearBit(idx);
|
||||
return idx;
|
||||
}
|
||||
|
||||
// Do "this |= v" and return whether new bits have been added.
|
||||
bool setUnion(const BasicBitVector &v) {
|
||||
basic_int_t old = bits_;
|
||||
bits_ |= v.bits_;
|
||||
return bits_ != old;
|
||||
}
|
||||
|
||||
// Do "this &= v" and return whether any bits have been removed.
|
||||
bool setIntersection(const BasicBitVector &v) {
|
||||
basic_int_t old = bits_;
|
||||
bits_ &= v.bits_;
|
||||
return bits_ != old;
|
||||
}
|
||||
|
||||
// Do "this &= ~v" and return whether any bits have been removed.
|
||||
bool setDifference(const BasicBitVector &v) {
|
||||
basic_int_t old = bits_;
|
||||
bits_ &= ~v.bits_;
|
||||
return bits_ != old;
|
||||
}
|
||||
|
||||
void copyFrom(const BasicBitVector &v) { bits_ = v.bits_; }
|
||||
|
||||
// Returns true if 'this' intersects with 'v'.
|
||||
bool intersectsWith(const BasicBitVector &v) const {
|
||||
return (bits_ & v.bits_) != 0;
|
||||
}
|
||||
|
||||
// for (BasicBitVector<>::Iterator it(bv); it.hasNext();) {
|
||||
// uptr idx = it.next();
|
||||
// use(idx);
|
||||
// }
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator() { }
|
||||
explicit Iterator(const BasicBitVector &bv) : bv_(bv) {}
|
||||
bool hasNext() const { return !bv_.empty(); }
|
||||
uptr next() { return bv_.getAndClearFirstOne(); }
|
||||
void clear() { bv_.clear(); }
|
||||
private:
|
||||
BasicBitVector bv_;
|
||||
};
|
||||
|
||||
private:
|
||||
basic_int_t mask(uptr idx) const {
|
||||
CHECK_LT(idx, size());
|
||||
return (basic_int_t)1UL << idx;
|
||||
}
|
||||
basic_int_t bits_;
|
||||
};
|
||||
|
||||
// Fixed size bit vector of (kLevel1Size*BV::kSize**2) bits.
|
||||
// The implementation is optimized for better performance on
|
||||
// sparse bit vectors, i.e. the those with few set bits.
|
||||
template <uptr kLevel1Size = 1, class BV = BasicBitVector<> >
|
||||
class TwoLevelBitVector {
|
||||
// This is essentially a 2-level bit vector.
|
||||
// Set bit in the first level BV indicates that there are set bits
|
||||
// in the corresponding BV of the second level.
|
||||
// This structure allows O(kLevel1Size) time for clear() and empty(),
|
||||
// as well fast handling of sparse BVs.
|
||||
public:
|
||||
enum SizeEnum : uptr { kSize = BV::kSize * BV::kSize * kLevel1Size };
|
||||
// No CTOR.
|
||||
|
||||
uptr size() const { return kSize; }
|
||||
|
||||
void clear() {
|
||||
for (uptr i = 0; i < kLevel1Size; i++)
|
||||
l1_[i].clear();
|
||||
}
|
||||
|
||||
void setAll() {
|
||||
for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
|
||||
l1_[i0].setAll();
|
||||
for (uptr i1 = 0; i1 < BV::kSize; i1++)
|
||||
l2_[i0][i1].setAll();
|
||||
}
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
for (uptr i = 0; i < kLevel1Size; i++)
|
||||
if (!l1_[i].empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if the bit has changed from 0 to 1.
|
||||
bool setBit(uptr idx) {
|
||||
check(idx);
|
||||
uptr i0 = idx0(idx);
|
||||
uptr i1 = idx1(idx);
|
||||
uptr i2 = idx2(idx);
|
||||
if (!l1_[i0].getBit(i1)) {
|
||||
l1_[i0].setBit(i1);
|
||||
l2_[i0][i1].clear();
|
||||
}
|
||||
bool res = l2_[i0][i1].setBit(i2);
|
||||
// Printf("%s: %zd => %zd %zd %zd; %d\n", __func__,
|
||||
// idx, i0, i1, i2, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool clearBit(uptr idx) {
|
||||
check(idx);
|
||||
uptr i0 = idx0(idx);
|
||||
uptr i1 = idx1(idx);
|
||||
uptr i2 = idx2(idx);
|
||||
bool res = false;
|
||||
if (l1_[i0].getBit(i1)) {
|
||||
res = l2_[i0][i1].clearBit(i2);
|
||||
if (l2_[i0][i1].empty())
|
||||
l1_[i0].clearBit(i1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool getBit(uptr idx) const {
|
||||
check(idx);
|
||||
uptr i0 = idx0(idx);
|
||||
uptr i1 = idx1(idx);
|
||||
uptr i2 = idx2(idx);
|
||||
// Printf("%s: %zd => %zd %zd %zd\n", __func__, idx, i0, i1, i2);
|
||||
return l1_[i0].getBit(i1) && l2_[i0][i1].getBit(i2);
|
||||
}
|
||||
|
||||
uptr getAndClearFirstOne() {
|
||||
for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
|
||||
if (l1_[i0].empty()) continue;
|
||||
uptr i1 = l1_[i0].getAndClearFirstOne();
|
||||
uptr i2 = l2_[i0][i1].getAndClearFirstOne();
|
||||
if (!l2_[i0][i1].empty())
|
||||
l1_[i0].setBit(i1);
|
||||
uptr res = i0 * BV::kSize * BV::kSize + i1 * BV::kSize + i2;
|
||||
// Printf("getAndClearFirstOne: %zd %zd %zd => %zd\n", i0, i1, i2, res);
|
||||
return res;
|
||||
}
|
||||
CHECK(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Do "this |= v" and return whether new bits have been added.
|
||||
bool setUnion(const TwoLevelBitVector &v) {
|
||||
bool res = false;
|
||||
for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
|
||||
BV t = v.l1_[i0];
|
||||
while (!t.empty()) {
|
||||
uptr i1 = t.getAndClearFirstOne();
|
||||
if (l1_[i0].setBit(i1))
|
||||
l2_[i0][i1].clear();
|
||||
if (l2_[i0][i1].setUnion(v.l2_[i0][i1]))
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Do "this &= v" and return whether any bits have been removed.
|
||||
bool setIntersection(const TwoLevelBitVector &v) {
|
||||
bool res = false;
|
||||
for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
|
||||
if (l1_[i0].setIntersection(v.l1_[i0]))
|
||||
res = true;
|
||||
if (!l1_[i0].empty()) {
|
||||
BV t = l1_[i0];
|
||||
while (!t.empty()) {
|
||||
uptr i1 = t.getAndClearFirstOne();
|
||||
if (l2_[i0][i1].setIntersection(v.l2_[i0][i1]))
|
||||
res = true;
|
||||
if (l2_[i0][i1].empty())
|
||||
l1_[i0].clearBit(i1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Do "this &= ~v" and return whether any bits have been removed.
|
||||
bool setDifference(const TwoLevelBitVector &v) {
|
||||
bool res = false;
|
||||
for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
|
||||
BV t = l1_[i0];
|
||||
t.setIntersection(v.l1_[i0]);
|
||||
while (!t.empty()) {
|
||||
uptr i1 = t.getAndClearFirstOne();
|
||||
if (l2_[i0][i1].setDifference(v.l2_[i0][i1]))
|
||||
res = true;
|
||||
if (l2_[i0][i1].empty())
|
||||
l1_[i0].clearBit(i1);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void copyFrom(const TwoLevelBitVector &v) {
|
||||
clear();
|
||||
setUnion(v);
|
||||
}
|
||||
|
||||
// Returns true if 'this' intersects with 'v'.
|
||||
bool intersectsWith(const TwoLevelBitVector &v) const {
|
||||
for (uptr i0 = 0; i0 < kLevel1Size; i0++) {
|
||||
BV t = l1_[i0];
|
||||
t.setIntersection(v.l1_[i0]);
|
||||
while (!t.empty()) {
|
||||
uptr i1 = t.getAndClearFirstOne();
|
||||
if (!v.l1_[i0].getBit(i1)) continue;
|
||||
if (l2_[i0][i1].intersectsWith(v.l2_[i0][i1]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// for (TwoLevelBitVector<>::Iterator it(bv); it.hasNext();) {
|
||||
// uptr idx = it.next();
|
||||
// use(idx);
|
||||
// }
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator() { }
|
||||
explicit Iterator(const TwoLevelBitVector &bv) : bv_(bv), i0_(0), i1_(0) {
|
||||
it1_.clear();
|
||||
it2_.clear();
|
||||
}
|
||||
|
||||
bool hasNext() const {
|
||||
if (it1_.hasNext()) return true;
|
||||
for (uptr i = i0_; i < kLevel1Size; i++)
|
||||
if (!bv_.l1_[i].empty()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uptr next() {
|
||||
// Printf("++++: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
|
||||
// it2_.hasNext(), kSize);
|
||||
if (!it1_.hasNext() && !it2_.hasNext()) {
|
||||
for (; i0_ < kLevel1Size; i0_++) {
|
||||
if (bv_.l1_[i0_].empty()) continue;
|
||||
it1_ = typename BV::Iterator(bv_.l1_[i0_]);
|
||||
// Printf("+i0: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
|
||||
// it2_.hasNext(), kSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!it2_.hasNext()) {
|
||||
CHECK(it1_.hasNext());
|
||||
i1_ = it1_.next();
|
||||
it2_ = typename BV::Iterator(bv_.l2_[i0_][i1_]);
|
||||
// Printf("++i1: %zd %zd; %d %d; size %zd\n", i0_, i1_, it1_.hasNext(),
|
||||
// it2_.hasNext(), kSize);
|
||||
}
|
||||
CHECK(it2_.hasNext());
|
||||
uptr i2 = it2_.next();
|
||||
uptr res = i0_ * BV::kSize * BV::kSize + i1_ * BV::kSize + i2;
|
||||
// Printf("+ret: %zd %zd; %d %d; size %zd; res: %zd\n", i0_, i1_,
|
||||
// it1_.hasNext(), it2_.hasNext(), kSize, res);
|
||||
if (!it1_.hasNext() && !it2_.hasNext())
|
||||
i0_++;
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
const TwoLevelBitVector &bv_;
|
||||
uptr i0_, i1_;
|
||||
typename BV::Iterator it1_, it2_;
|
||||
};
|
||||
|
||||
private:
|
||||
void check(uptr idx) const { CHECK_LE(idx, size()); }
|
||||
|
||||
uptr idx0(uptr idx) const {
|
||||
uptr res = idx / (BV::kSize * BV::kSize);
|
||||
CHECK_LE(res, kLevel1Size);
|
||||
return res;
|
||||
}
|
||||
|
||||
uptr idx1(uptr idx) const {
|
||||
uptr res = (idx / BV::kSize) % BV::kSize;
|
||||
CHECK_LE(res, BV::kSize);
|
||||
return res;
|
||||
}
|
||||
|
||||
uptr idx2(uptr idx) const {
|
||||
uptr res = idx % BV::kSize;
|
||||
CHECK_LE(res, BV::kSize);
|
||||
return res;
|
||||
}
|
||||
|
||||
BV l1_[kLevel1Size];
|
||||
BV l2_[kLevel1Size][BV::kSize];
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_BITVECTOR_H
|
||||
164
lib/tsan/sanitizer_common/sanitizer_bvgraph.h
Normal file
164
lib/tsan/sanitizer_common/sanitizer_bvgraph.h
Normal file
@@ -0,0 +1,164 @@
|
||||
//===-- sanitizer_bvgraph.h -------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer runtime.
|
||||
// BVGraph -- a directed graph.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_BVGRAPH_H
|
||||
#define SANITIZER_BVGRAPH_H
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_bitvector.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Directed graph of fixed size implemented as an array of bit vectors.
|
||||
// Not thread-safe, all accesses should be protected by an external lock.
|
||||
template<class BV>
|
||||
class BVGraph {
|
||||
public:
|
||||
enum SizeEnum : uptr { kSize = BV::kSize };
|
||||
uptr size() const { return kSize; }
|
||||
// No CTOR.
|
||||
void clear() {
|
||||
for (uptr i = 0; i < size(); i++)
|
||||
v[i].clear();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
for (uptr i = 0; i < size(); i++)
|
||||
if (!v[i].empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if a new edge was added.
|
||||
bool addEdge(uptr from, uptr to) {
|
||||
check(from, to);
|
||||
return v[from].setBit(to);
|
||||
}
|
||||
|
||||
// Returns true if at least one new edge was added.
|
||||
uptr addEdges(const BV &from, uptr to, uptr added_edges[],
|
||||
uptr max_added_edges) {
|
||||
uptr res = 0;
|
||||
t1.copyFrom(from);
|
||||
while (!t1.empty()) {
|
||||
uptr node = t1.getAndClearFirstOne();
|
||||
if (v[node].setBit(to))
|
||||
if (res < max_added_edges)
|
||||
added_edges[res++] = node;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// *EXPERIMENTAL*
|
||||
// Returns true if an edge from=>to exist.
|
||||
// This function does not use any global state except for 'this' itself,
|
||||
// and thus can be called from different threads w/o locking.
|
||||
// This would be racy.
|
||||
// FIXME: investigate how much we can prove about this race being "benign".
|
||||
bool hasEdge(uptr from, uptr to) { return v[from].getBit(to); }
|
||||
|
||||
// Returns true if the edge from=>to was removed.
|
||||
bool removeEdge(uptr from, uptr to) {
|
||||
return v[from].clearBit(to);
|
||||
}
|
||||
|
||||
// Returns true if at least one edge *=>to was removed.
|
||||
bool removeEdgesTo(const BV &to) {
|
||||
bool res = 0;
|
||||
for (uptr from = 0; from < size(); from++) {
|
||||
if (v[from].setDifference(to))
|
||||
res = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Returns true if at least one edge from=>* was removed.
|
||||
bool removeEdgesFrom(const BV &from) {
|
||||
bool res = false;
|
||||
t1.copyFrom(from);
|
||||
while (!t1.empty()) {
|
||||
uptr idx = t1.getAndClearFirstOne();
|
||||
if (!v[idx].empty()) {
|
||||
v[idx].clear();
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void removeEdgesFrom(uptr from) {
|
||||
return v[from].clear();
|
||||
}
|
||||
|
||||
bool hasEdge(uptr from, uptr to) const {
|
||||
check(from, to);
|
||||
return v[from].getBit(to);
|
||||
}
|
||||
|
||||
// Returns true if there is a path from the node 'from'
|
||||
// to any of the nodes in 'targets'.
|
||||
bool isReachable(uptr from, const BV &targets) {
|
||||
BV &to_visit = t1,
|
||||
&visited = t2;
|
||||
to_visit.copyFrom(v[from]);
|
||||
visited.clear();
|
||||
visited.setBit(from);
|
||||
while (!to_visit.empty()) {
|
||||
uptr idx = to_visit.getAndClearFirstOne();
|
||||
if (visited.setBit(idx))
|
||||
to_visit.setUnion(v[idx]);
|
||||
}
|
||||
return targets.intersectsWith(visited);
|
||||
}
|
||||
|
||||
// Finds a path from 'from' to one of the nodes in 'target',
|
||||
// stores up to 'path_size' items of the path into 'path',
|
||||
// returns the path length, or 0 if there is no path of size 'path_size'.
|
||||
uptr findPath(uptr from, const BV &targets, uptr *path, uptr path_size) {
|
||||
if (path_size == 0)
|
||||
return 0;
|
||||
path[0] = from;
|
||||
if (targets.getBit(from))
|
||||
return 1;
|
||||
// The function is recursive, so we don't want to create BV on stack.
|
||||
// Instead of a getAndClearFirstOne loop we use the slower iterator.
|
||||
for (typename BV::Iterator it(v[from]); it.hasNext(); ) {
|
||||
uptr idx = it.next();
|
||||
if (uptr res = findPath(idx, targets, path + 1, path_size - 1))
|
||||
return res + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Same as findPath, but finds a shortest path.
|
||||
uptr findShortestPath(uptr from, const BV &targets, uptr *path,
|
||||
uptr path_size) {
|
||||
for (uptr p = 1; p <= path_size; p++)
|
||||
if (findPath(from, targets, path, p) == p)
|
||||
return p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void check(uptr idx1, uptr idx2) const {
|
||||
CHECK_LT(idx1, size());
|
||||
CHECK_LT(idx2, size());
|
||||
}
|
||||
BV v[kSize];
|
||||
// Keep temporary vectors here since we can not create large objects on stack.
|
||||
BV t1, t2;
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_BVGRAPH_H
|
||||
348
lib/tsan/sanitizer_common/sanitizer_common.cpp
Normal file
348
lib/tsan/sanitizer_common/sanitizer_common.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
//===-- sanitizer_common.cpp ----------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_allocator_interface.h"
|
||||
#include "sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_atomic.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 PageSizeCached;
|
||||
u32 NumberOfCPUsCached;
|
||||
|
||||
// PID of the tracer task in StopTheWorld. It shares the address space with the
|
||||
// main process, but has a different PID and thus requires special handling.
|
||||
uptr stoptheworld_tracer_pid = 0;
|
||||
// Cached pid of parent process - if the parent process dies, we want to keep
|
||||
// writing to the same log file.
|
||||
uptr stoptheworld_tracer_ppid = 0;
|
||||
|
||||
void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
|
||||
const char *mmap_type, error_t err,
|
||||
bool raw_report) {
|
||||
static int recursion_count;
|
||||
if (SANITIZER_RTEMS || raw_report || recursion_count) {
|
||||
// If we are on RTEMS or raw report is requested or we went into recursion,
|
||||
// just die. The Report() and CHECK calls below may call mmap recursively
|
||||
// and fail.
|
||||
RawWrite("ERROR: Failed to mmap\n");
|
||||
Die();
|
||||
}
|
||||
recursion_count++;
|
||||
Report("ERROR: %s failed to "
|
||||
"%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
|
||||
SanitizerToolName, mmap_type, size, size, mem_type, err);
|
||||
#if !SANITIZER_GO
|
||||
DumpProcessMap();
|
||||
#endif
|
||||
UNREACHABLE("unable to mmap");
|
||||
}
|
||||
|
||||
typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
|
||||
typedef bool U32ComparisonFunction(const u32 &a, const u32 &b);
|
||||
|
||||
const char *StripPathPrefix(const char *filepath,
|
||||
const char *strip_path_prefix) {
|
||||
if (!filepath) return nullptr;
|
||||
if (!strip_path_prefix) return filepath;
|
||||
const char *res = filepath;
|
||||
if (const char *pos = internal_strstr(filepath, strip_path_prefix))
|
||||
res = pos + internal_strlen(strip_path_prefix);
|
||||
if (res[0] == '.' && res[1] == '/')
|
||||
res += 2;
|
||||
return res;
|
||||
}
|
||||
|
||||
const char *StripModuleName(const char *module) {
|
||||
if (!module)
|
||||
return nullptr;
|
||||
if (SANITIZER_WINDOWS) {
|
||||
// On Windows, both slash and backslash are possible.
|
||||
// Pick the one that goes last.
|
||||
if (const char *bslash_pos = internal_strrchr(module, '\\'))
|
||||
return StripModuleName(bslash_pos + 1);
|
||||
}
|
||||
if (const char *slash_pos = internal_strrchr(module, '/')) {
|
||||
return slash_pos + 1;
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
void ReportErrorSummary(const char *error_message, const char *alt_tool_name) {
|
||||
if (!common_flags()->print_summary)
|
||||
return;
|
||||
InternalScopedString buff(kMaxSummaryLength);
|
||||
buff.append("SUMMARY: %s: %s",
|
||||
alt_tool_name ? alt_tool_name : SanitizerToolName, error_message);
|
||||
__sanitizer_report_error_summary(buff.data());
|
||||
}
|
||||
|
||||
// Removes the ANSI escape sequences from the input string (in-place).
|
||||
void RemoveANSIEscapeSequencesFromString(char *str) {
|
||||
if (!str)
|
||||
return;
|
||||
|
||||
// We are going to remove the escape sequences in place.
|
||||
char *s = str;
|
||||
char *z = str;
|
||||
while (*s != '\0') {
|
||||
CHECK_GE(s, z);
|
||||
// Skip over ANSI escape sequences with pointer 's'.
|
||||
if (*s == '\033' && *(s + 1) == '[') {
|
||||
s = internal_strchrnul(s, 'm');
|
||||
if (*s == '\0') {
|
||||
break;
|
||||
}
|
||||
s++;
|
||||
continue;
|
||||
}
|
||||
// 's' now points at a character we want to keep. Copy over the buffer
|
||||
// content if the escape sequence has been perviously skipped andadvance
|
||||
// both pointers.
|
||||
if (s != z)
|
||||
*z = *s;
|
||||
|
||||
// If we have not seen an escape sequence, just advance both pointers.
|
||||
z++;
|
||||
s++;
|
||||
}
|
||||
|
||||
// Null terminate the string.
|
||||
*z = '\0';
|
||||
}
|
||||
|
||||
void LoadedModule::set(const char *module_name, uptr base_address) {
|
||||
clear();
|
||||
full_name_ = internal_strdup(module_name);
|
||||
base_address_ = base_address;
|
||||
}
|
||||
|
||||
void LoadedModule::set(const char *module_name, uptr base_address,
|
||||
ModuleArch arch, u8 uuid[kModuleUUIDSize],
|
||||
bool instrumented) {
|
||||
set(module_name, base_address);
|
||||
arch_ = arch;
|
||||
internal_memcpy(uuid_, uuid, sizeof(uuid_));
|
||||
instrumented_ = instrumented;
|
||||
}
|
||||
|
||||
void LoadedModule::clear() {
|
||||
InternalFree(full_name_);
|
||||
base_address_ = 0;
|
||||
max_executable_address_ = 0;
|
||||
full_name_ = nullptr;
|
||||
arch_ = kModuleArchUnknown;
|
||||
internal_memset(uuid_, 0, kModuleUUIDSize);
|
||||
instrumented_ = false;
|
||||
while (!ranges_.empty()) {
|
||||
AddressRange *r = ranges_.front();
|
||||
ranges_.pop_front();
|
||||
InternalFree(r);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable,
|
||||
bool writable, const char *name) {
|
||||
void *mem = InternalAlloc(sizeof(AddressRange));
|
||||
AddressRange *r =
|
||||
new(mem) AddressRange(beg, end, executable, writable, name);
|
||||
ranges_.push_back(r);
|
||||
if (executable && end > max_executable_address_)
|
||||
max_executable_address_ = end;
|
||||
}
|
||||
|
||||
bool LoadedModule::containsAddress(uptr address) const {
|
||||
for (const AddressRange &r : ranges()) {
|
||||
if (r.beg <= address && address < r.end)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static atomic_uintptr_t g_total_mmaped;
|
||||
|
||||
void IncreaseTotalMmap(uptr size) {
|
||||
if (!common_flags()->mmap_limit_mb) return;
|
||||
uptr total_mmaped =
|
||||
atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
|
||||
// Since for now mmap_limit_mb is not a user-facing flag, just kill
|
||||
// a program. Use RAW_CHECK to avoid extra mmaps in reporting.
|
||||
RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
|
||||
}
|
||||
|
||||
void DecreaseTotalMmap(uptr size) {
|
||||
if (!common_flags()->mmap_limit_mb) return;
|
||||
atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool TemplateMatch(const char *templ, const char *str) {
|
||||
if ((!str) || str[0] == 0)
|
||||
return false;
|
||||
bool start = false;
|
||||
if (templ && templ[0] == '^') {
|
||||
start = true;
|
||||
templ++;
|
||||
}
|
||||
bool asterisk = false;
|
||||
while (templ && templ[0]) {
|
||||
if (templ[0] == '*') {
|
||||
templ++;
|
||||
start = false;
|
||||
asterisk = true;
|
||||
continue;
|
||||
}
|
||||
if (templ[0] == '$')
|
||||
return str[0] == 0 || asterisk;
|
||||
if (str[0] == 0)
|
||||
return false;
|
||||
char *tpos = (char*)internal_strchr(templ, '*');
|
||||
char *tpos1 = (char*)internal_strchr(templ, '$');
|
||||
if ((!tpos) || (tpos1 && tpos1 < tpos))
|
||||
tpos = tpos1;
|
||||
if (tpos)
|
||||
tpos[0] = 0;
|
||||
const char *str0 = str;
|
||||
const char *spos = internal_strstr(str, templ);
|
||||
str = spos + internal_strlen(templ);
|
||||
templ = tpos;
|
||||
if (tpos)
|
||||
tpos[0] = tpos == tpos1 ? '$' : '*';
|
||||
if (!spos)
|
||||
return false;
|
||||
if (start && spos != str0)
|
||||
return false;
|
||||
start = false;
|
||||
asterisk = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static char binary_name_cache_str[kMaxPathLength];
|
||||
static char process_name_cache_str[kMaxPathLength];
|
||||
|
||||
const char *GetProcessName() {
|
||||
return process_name_cache_str;
|
||||
}
|
||||
|
||||
static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
|
||||
ReadLongProcessName(buf, buf_len);
|
||||
char *s = const_cast<char *>(StripModuleName(buf));
|
||||
uptr len = internal_strlen(s);
|
||||
if (s != buf) {
|
||||
internal_memmove(buf, s, len);
|
||||
buf[len] = '\0';
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void UpdateProcessName() {
|
||||
ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
|
||||
}
|
||||
|
||||
// Call once to make sure that binary_name_cache_str is initialized
|
||||
void CacheBinaryName() {
|
||||
if (binary_name_cache_str[0] != '\0')
|
||||
return;
|
||||
ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
|
||||
ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
|
||||
}
|
||||
|
||||
uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
|
||||
CacheBinaryName();
|
||||
uptr name_len = internal_strlen(binary_name_cache_str);
|
||||
name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
|
||||
if (buf_len == 0)
|
||||
return 0;
|
||||
internal_memcpy(buf, binary_name_cache_str, name_len);
|
||||
buf[name_len] = '\0';
|
||||
return name_len;
|
||||
}
|
||||
|
||||
#if !SANITIZER_GO
|
||||
void PrintCmdline() {
|
||||
char **argv = GetArgv();
|
||||
if (!argv) return;
|
||||
Printf("\nCommand: ");
|
||||
for (uptr i = 0; argv[i]; ++i)
|
||||
Printf("%s ", argv[i]);
|
||||
Printf("\n\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Malloc hooks.
|
||||
static const int kMaxMallocFreeHooks = 5;
|
||||
struct MallocFreeHook {
|
||||
void (*malloc_hook)(const void *, uptr);
|
||||
void (*free_hook)(const void *);
|
||||
};
|
||||
|
||||
static MallocFreeHook MFHooks[kMaxMallocFreeHooks];
|
||||
|
||||
void RunMallocHooks(const void *ptr, uptr size) {
|
||||
for (int i = 0; i < kMaxMallocFreeHooks; i++) {
|
||||
auto hook = MFHooks[i].malloc_hook;
|
||||
if (!hook) return;
|
||||
hook(ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
void RunFreeHooks(const void *ptr) {
|
||||
for (int i = 0; i < kMaxMallocFreeHooks; i++) {
|
||||
auto hook = MFHooks[i].free_hook;
|
||||
if (!hook) return;
|
||||
hook(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr),
|
||||
void (*free_hook)(const void *)) {
|
||||
if (!malloc_hook || !free_hook) return 0;
|
||||
for (int i = 0; i < kMaxMallocFreeHooks; i++) {
|
||||
if (MFHooks[i].malloc_hook == nullptr) {
|
||||
MFHooks[i].malloc_hook = malloc_hook;
|
||||
MFHooks[i].free_hook = free_hook;
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
extern "C" {
|
||||
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_report_error_summary,
|
||||
const char *error_summary) {
|
||||
Printf("%s\n", error_summary);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __sanitizer_acquire_crash_state() {
|
||||
static atomic_uint8_t in_crash_state = {};
|
||||
return !atomic_exchange(&in_crash_state, 1, memory_order_relaxed);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *,
|
||||
uptr),
|
||||
void (*free_hook)(const void *)) {
|
||||
return InstallMallocFreeHooks(malloc_hook, free_hook);
|
||||
}
|
||||
} // extern "C"
|
||||
1002
lib/tsan/sanitizer_common/sanitizer_common.h
Normal file
1002
lib/tsan/sanitizer_common/sanitizer_common.h
Normal file
File diff suppressed because it is too large
Load Diff
10175
lib/tsan/sanitizer_common/sanitizer_common_interceptors.inc
Normal file
10175
lib/tsan/sanitizer_common/sanitizer_common_interceptors.inc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,562 @@
|
||||
//===-- sanitizer_common_interceptors_format.inc ----------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Scanf/printf implementation for use in *Sanitizer interceptors.
|
||||
// Follows http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html
|
||||
// and http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html
|
||||
// with a few common GNU extensions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
static const char *parse_number(const char *p, int *out) {
|
||||
*out = internal_atoll(p);
|
||||
while (*p >= '0' && *p <= '9')
|
||||
++p;
|
||||
return p;
|
||||
}
|
||||
|
||||
static const char *maybe_parse_param_index(const char *p, int *out) {
|
||||
// n$
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
int number;
|
||||
const char *q = parse_number(p, &number);
|
||||
CHECK(q);
|
||||
if (*q == '$') {
|
||||
*out = number;
|
||||
p = q + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, do not change p. This will be re-parsed later as the field
|
||||
// width.
|
||||
return p;
|
||||
}
|
||||
|
||||
static bool char_is_one_of(char c, const char *s) {
|
||||
return !!internal_strchr(s, c);
|
||||
}
|
||||
|
||||
static const char *maybe_parse_length_modifier(const char *p, char ll[2]) {
|
||||
if (char_is_one_of(*p, "jztLq")) {
|
||||
ll[0] = *p;
|
||||
++p;
|
||||
} else if (*p == 'h') {
|
||||
ll[0] = 'h';
|
||||
++p;
|
||||
if (*p == 'h') {
|
||||
ll[1] = 'h';
|
||||
++p;
|
||||
}
|
||||
} else if (*p == 'l') {
|
||||
ll[0] = 'l';
|
||||
++p;
|
||||
if (*p == 'l') {
|
||||
ll[1] = 'l';
|
||||
++p;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// Returns true if the character is an integer conversion specifier.
|
||||
static bool format_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 format_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 format_get_char_size(char convSpecifier,
|
||||
const char lengthModifier[2]) {
|
||||
if (char_is_one_of(convSpecifier, "CS")) {
|
||||
return sizeof(wchar_t);
|
||||
}
|
||||
|
||||
if (char_is_one_of(convSpecifier, "cs[")) {
|
||||
if (lengthModifier[0] == 'l' && lengthModifier[1] == '\0')
|
||||
return sizeof(wchar_t);
|
||||
else if (lengthModifier[0] == '\0')
|
||||
return sizeof(char);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum FormatStoreSize {
|
||||
// Store size not known in advance; can be calculated as wcslen() of the
|
||||
// destination buffer.
|
||||
FSS_WCSLEN = -2,
|
||||
// Store size not known in advance; can be calculated as strlen() of the
|
||||
// destination buffer.
|
||||
FSS_STRLEN = -1,
|
||||
// Invalid conversion specifier.
|
||||
FSS_INVALID = 0
|
||||
};
|
||||
|
||||
// Returns the memory size of a format directive (if >0), or a value of
|
||||
// FormatStoreSize.
|
||||
static int format_get_value_size(char convSpecifier,
|
||||
const char lengthModifier[2],
|
||||
bool promote_float) {
|
||||
if (format_is_integer_conv(convSpecifier)) {
|
||||
switch (lengthModifier[0]) {
|
||||
case 'h':
|
||||
return lengthModifier[1] == 'h' ? sizeof(char) : sizeof(short);
|
||||
case 'l':
|
||||
return lengthModifier[1] == 'l' ? sizeof(long long) : sizeof(long);
|
||||
case 'q':
|
||||
return sizeof(long 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 FSS_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
if (format_is_float_conv(convSpecifier)) {
|
||||
switch (lengthModifier[0]) {
|
||||
case 'L':
|
||||
case 'q':
|
||||
return sizeof(long double);
|
||||
case 'l':
|
||||
return lengthModifier[1] == 'l' ? sizeof(long double)
|
||||
: sizeof(double);
|
||||
case 0:
|
||||
// Printf promotes floats to doubles but scanf does not
|
||||
return promote_float ? sizeof(double) : sizeof(float);
|
||||
default:
|
||||
return FSS_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
if (convSpecifier == 'p') {
|
||||
if (lengthModifier[0] != 0)
|
||||
return FSS_INVALID;
|
||||
return sizeof(void *);
|
||||
}
|
||||
|
||||
return FSS_INVALID;
|
||||
}
|
||||
|
||||
struct ScanfDirective {
|
||||
int argIdx; // argument index, or -1 if not specified ("%n$")
|
||||
int fieldWidth;
|
||||
const char *begin;
|
||||
const char *end;
|
||||
bool suppressed; // suppress assignment ("*")
|
||||
bool allocate; // allocate space ("m")
|
||||
char lengthModifier[2];
|
||||
char convSpecifier;
|
||||
bool maybeGnuMalloc;
|
||||
};
|
||||
|
||||
// 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 != '%') {
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
dir->begin = p;
|
||||
++p;
|
||||
// %%
|
||||
if (*p == '%') {
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
if (*p == '\0') {
|
||||
return nullptr;
|
||||
}
|
||||
// %n$
|
||||
p = maybe_parse_param_index(p, &dir->argIdx);
|
||||
CHECK(p);
|
||||
// *
|
||||
if (*p == '*') {
|
||||
dir->suppressed = true;
|
||||
++p;
|
||||
}
|
||||
// Field width
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
p = parse_number(p, &dir->fieldWidth);
|
||||
CHECK(p);
|
||||
if (dir->fieldWidth <= 0) // Width if at all must be non-zero
|
||||
return nullptr;
|
||||
}
|
||||
// m
|
||||
if (*p == 'm') {
|
||||
dir->allocate = true;
|
||||
++p;
|
||||
}
|
||||
// Length modifier.
|
||||
p = maybe_parse_length_modifier(p, dir->lengthModifier);
|
||||
// Conversion specifier.
|
||||
dir->convSpecifier = *p++;
|
||||
// Consume %[...] expression.
|
||||
if (dir->convSpecifier == '[') {
|
||||
if (*p == '^')
|
||||
++p;
|
||||
if (*p == ']')
|
||||
++p;
|
||||
while (*p && *p != ']')
|
||||
++p;
|
||||
if (*p == 0)
|
||||
return nullptr; // 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 nullptr;
|
||||
p = q + 1; // Consume the closing ']'.
|
||||
dir->maybeGnuMalloc = true;
|
||||
}
|
||||
}
|
||||
dir->end = p;
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static int scanf_get_value_size(ScanfDirective *dir) {
|
||||
if (dir->allocate) {
|
||||
if (!char_is_one_of(dir->convSpecifier, "cCsS["))
|
||||
return FSS_INVALID;
|
||||
return sizeof(char *);
|
||||
}
|
||||
|
||||
if (dir->maybeGnuMalloc) {
|
||||
if (dir->convSpecifier != 'a' || dir->lengthModifier[0])
|
||||
return FSS_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 (char_is_one_of(dir->convSpecifier, "cCsS[")) {
|
||||
bool needsTerminator = char_is_one_of(dir->convSpecifier, "sS[");
|
||||
unsigned charSize =
|
||||
format_get_char_size(dir->convSpecifier, dir->lengthModifier);
|
||||
if (charSize == 0)
|
||||
return FSS_INVALID;
|
||||
if (dir->fieldWidth == 0) {
|
||||
if (!needsTerminator)
|
||||
return charSize;
|
||||
return (charSize == sizeof(char)) ? FSS_STRLEN : FSS_WCSLEN;
|
||||
}
|
||||
return (dir->fieldWidth + needsTerminator) * charSize;
|
||||
}
|
||||
|
||||
return format_get_value_size(dir->convSpecifier, dir->lengthModifier, false);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1);
|
||||
|
||||
while (*p) {
|
||||
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_value_size(&dir);
|
||||
if (size == FSS_INVALID) {
|
||||
Report("%s: WARNING: unexpected format specifier in scanf interceptor: ",
|
||||
SanitizerToolName, "%.*s\n", dir.end - dir.begin, dir.begin);
|
||||
break;
|
||||
}
|
||||
void *argp = va_arg(aq, void *);
|
||||
if (dir.convSpecifier != 'n')
|
||||
--n_inputs;
|
||||
if (n_inputs < 0)
|
||||
break;
|
||||
if (size == FSS_STRLEN) {
|
||||
size = internal_strlen((const char *)argp) + 1;
|
||||
} else if (size == FSS_WCSLEN) {
|
||||
// FIXME: actually use wcslen() to calculate it.
|
||||
size = 0;
|
||||
}
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size);
|
||||
}
|
||||
}
|
||||
|
||||
#if SANITIZER_INTERCEPT_PRINTF
|
||||
|
||||
struct PrintfDirective {
|
||||
int fieldWidth;
|
||||
int fieldPrecision;
|
||||
int argIdx; // width argument index, or -1 if not specified ("%*n$")
|
||||
int precisionIdx; // precision argument index, or -1 if not specified (".*n$")
|
||||
const char *begin;
|
||||
const char *end;
|
||||
bool starredWidth;
|
||||
bool starredPrecision;
|
||||
char lengthModifier[2];
|
||||
char convSpecifier;
|
||||
};
|
||||
|
||||
static const char *maybe_parse_number(const char *p, int *out) {
|
||||
if (*p >= '0' && *p <= '9')
|
||||
p = parse_number(p, out);
|
||||
return p;
|
||||
}
|
||||
|
||||
static const char *maybe_parse_number_or_star(const char *p, int *out,
|
||||
bool *star) {
|
||||
if (*p == '*') {
|
||||
*star = true;
|
||||
++p;
|
||||
} else {
|
||||
*star = false;
|
||||
p = maybe_parse_number(p, out);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// Parse printf format string. Same as scanf_parse_next.
|
||||
static const char *printf_parse_next(const char *p, PrintfDirective *dir) {
|
||||
internal_memset(dir, 0, sizeof(*dir));
|
||||
dir->argIdx = -1;
|
||||
dir->precisionIdx = -1;
|
||||
|
||||
while (*p) {
|
||||
if (*p != '%') {
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
dir->begin = p;
|
||||
++p;
|
||||
// %%
|
||||
if (*p == '%') {
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
if (*p == '\0') {
|
||||
return nullptr;
|
||||
}
|
||||
// %n$
|
||||
p = maybe_parse_param_index(p, &dir->precisionIdx);
|
||||
CHECK(p);
|
||||
// Flags
|
||||
while (char_is_one_of(*p, "'-+ #0")) {
|
||||
++p;
|
||||
}
|
||||
// Field width
|
||||
p = maybe_parse_number_or_star(p, &dir->fieldWidth,
|
||||
&dir->starredWidth);
|
||||
if (!p)
|
||||
return nullptr;
|
||||
// Precision
|
||||
if (*p == '.') {
|
||||
++p;
|
||||
// Actual precision is optional (surprise!)
|
||||
p = maybe_parse_number_or_star(p, &dir->fieldPrecision,
|
||||
&dir->starredPrecision);
|
||||
if (!p)
|
||||
return nullptr;
|
||||
// m$
|
||||
if (dir->starredPrecision) {
|
||||
p = maybe_parse_param_index(p, &dir->precisionIdx);
|
||||
CHECK(p);
|
||||
}
|
||||
}
|
||||
// Length modifier.
|
||||
p = maybe_parse_length_modifier(p, dir->lengthModifier);
|
||||
// Conversion specifier.
|
||||
dir->convSpecifier = *p++;
|
||||
dir->end = p;
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static int printf_get_value_size(PrintfDirective *dir) {
|
||||
if (char_is_one_of(dir->convSpecifier, "cCsS")) {
|
||||
unsigned charSize =
|
||||
format_get_char_size(dir->convSpecifier, dir->lengthModifier);
|
||||
if (charSize == 0)
|
||||
return FSS_INVALID;
|
||||
if (char_is_one_of(dir->convSpecifier, "sS")) {
|
||||
return (charSize == sizeof(char)) ? FSS_STRLEN : FSS_WCSLEN;
|
||||
}
|
||||
return charSize;
|
||||
}
|
||||
|
||||
return format_get_value_size(dir->convSpecifier, dir->lengthModifier, true);
|
||||
}
|
||||
|
||||
#define SKIP_SCALAR_ARG(aq, convSpecifier, size) \
|
||||
do { \
|
||||
if (format_is_float_conv(convSpecifier)) { \
|
||||
switch (size) { \
|
||||
case 8: \
|
||||
va_arg(*aq, double); \
|
||||
break; \
|
||||
case 12: \
|
||||
va_arg(*aq, long double); \
|
||||
break; \
|
||||
case 16: \
|
||||
va_arg(*aq, long double); \
|
||||
break; \
|
||||
default: \
|
||||
Report("WARNING: unexpected floating-point arg size" \
|
||||
" in printf interceptor: %d\n", size); \
|
||||
return; \
|
||||
} \
|
||||
} else { \
|
||||
switch (size) { \
|
||||
case 1: \
|
||||
case 2: \
|
||||
case 4: \
|
||||
va_arg(*aq, u32); \
|
||||
break; \
|
||||
case 8: \
|
||||
va_arg(*aq, u64); \
|
||||
break; \
|
||||
default: \
|
||||
Report("WARNING: unexpected arg size" \
|
||||
" in printf interceptor: %d\n", size); \
|
||||
return; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Common part of *printf interceptors.
|
||||
// Process format string and va_list, and report all load ranges.
|
||||
static void printf_common(void *ctx, const char *format, va_list aq) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, format, internal_strlen(format) + 1);
|
||||
|
||||
const char *p = format;
|
||||
|
||||
while (*p) {
|
||||
PrintfDirective dir;
|
||||
p = printf_parse_next(p, &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 || dir.precisionIdx != -1) {
|
||||
// Unsupported.
|
||||
break;
|
||||
}
|
||||
if (dir.starredWidth) {
|
||||
// Dynamic width
|
||||
SKIP_SCALAR_ARG(&aq, 'd', sizeof(int));
|
||||
}
|
||||
if (dir.starredPrecision) {
|
||||
// Dynamic precision
|
||||
SKIP_SCALAR_ARG(&aq, 'd', sizeof(int));
|
||||
}
|
||||
// %m does not require an argument: strlen(errno).
|
||||
if (dir.convSpecifier == 'm')
|
||||
continue;
|
||||
int size = printf_get_value_size(&dir);
|
||||
if (size == FSS_INVALID) {
|
||||
static int ReportedOnce;
|
||||
if (!ReportedOnce++)
|
||||
Report(
|
||||
"%s: WARNING: unexpected format specifier in printf "
|
||||
"interceptor: %.*s (reported once per process)\n",
|
||||
SanitizerToolName, dir.end - dir.begin, dir.begin);
|
||||
break;
|
||||
}
|
||||
if (dir.convSpecifier == 'n') {
|
||||
void *argp = va_arg(aq, void *);
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size);
|
||||
continue;
|
||||
} else if (size == FSS_STRLEN) {
|
||||
if (void *argp = va_arg(aq, void *)) {
|
||||
if (dir.starredPrecision) {
|
||||
// FIXME: properly support starred precision for strings.
|
||||
size = 0;
|
||||
} else if (dir.fieldPrecision > 0) {
|
||||
// Won't read more than "precision" symbols.
|
||||
size = internal_strnlen((const char *)argp, dir.fieldPrecision);
|
||||
if (size < dir.fieldPrecision) size++;
|
||||
} else {
|
||||
// Whole string will be accessed.
|
||||
size = internal_strlen((const char *)argp) + 1;
|
||||
}
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size);
|
||||
}
|
||||
} else if (size == FSS_WCSLEN) {
|
||||
if (void *argp = va_arg(aq, void *)) {
|
||||
// FIXME: Properly support wide-character strings (via wcsrtombs).
|
||||
size = 0;
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, argp, size);
|
||||
}
|
||||
} else {
|
||||
// Skip non-pointer args
|
||||
SKIP_SCALAR_ARG(&aq, dir.convSpecifier, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SANITIZER_INTERCEPT_PRINTF
|
||||
@@ -0,0 +1,609 @@
|
||||
//===-- sanitizer_common_interceptors_ioctl.inc -----------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Ioctl handling in common sanitizer interceptors.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if !SANITIZER_NETBSD
|
||||
|
||||
#include "sanitizer_flags.h"
|
||||
|
||||
struct ioctl_desc {
|
||||
unsigned req;
|
||||
// FIXME: support read+write arguments. Currently READWRITE and WRITE do the
|
||||
// same thing.
|
||||
// XXX: The declarations below may use WRITE instead of READWRITE, unless
|
||||
// explicitly noted.
|
||||
enum {
|
||||
NONE,
|
||||
READ,
|
||||
WRITE,
|
||||
READWRITE,
|
||||
CUSTOM
|
||||
} type : 3;
|
||||
unsigned size : 29;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
const unsigned ioctl_table_max = 500;
|
||||
static ioctl_desc ioctl_table[ioctl_table_max];
|
||||
static unsigned ioctl_table_size = 0;
|
||||
|
||||
// This can not be declared as a global, because references to struct_*_sz
|
||||
// require a global initializer. And this table must be available before global
|
||||
// initializers are run.
|
||||
static void ioctl_table_fill() {
|
||||
#define _(rq, tp, sz) \
|
||||
if (IOCTL_##rq != IOCTL_NOT_PRESENT) { \
|
||||
CHECK(ioctl_table_size < ioctl_table_max); \
|
||||
ioctl_table[ioctl_table_size].req = IOCTL_##rq; \
|
||||
ioctl_table[ioctl_table_size].type = ioctl_desc::tp; \
|
||||
ioctl_table[ioctl_table_size].size = sz; \
|
||||
ioctl_table[ioctl_table_size].name = #rq; \
|
||||
++ioctl_table_size; \
|
||||
}
|
||||
|
||||
_(FIOASYNC, READ, sizeof(int));
|
||||
_(FIOCLEX, NONE, 0);
|
||||
_(FIOGETOWN, WRITE, sizeof(int));
|
||||
_(FIONBIO, READ, sizeof(int));
|
||||
_(FIONCLEX, NONE, 0);
|
||||
_(FIOSETOWN, READ, sizeof(int));
|
||||
_(SIOCATMARK, WRITE, sizeof(int));
|
||||
_(SIOCGIFCONF, CUSTOM, 0);
|
||||
_(SIOCGPGRP, WRITE, sizeof(int));
|
||||
_(SIOCSPGRP, READ, sizeof(int));
|
||||
#if !SANITIZER_SOLARIS
|
||||
_(TIOCCONS, NONE, 0);
|
||||
#endif
|
||||
_(TIOCEXCL, NONE, 0);
|
||||
_(TIOCGETD, WRITE, sizeof(int));
|
||||
_(TIOCGPGRP, WRITE, pid_t_sz);
|
||||
_(TIOCGWINSZ, WRITE, struct_winsize_sz);
|
||||
_(TIOCMBIC, READ, sizeof(int));
|
||||
_(TIOCMBIS, READ, sizeof(int));
|
||||
_(TIOCMGET, WRITE, sizeof(int));
|
||||
_(TIOCMSET, READ, sizeof(int));
|
||||
_(TIOCNOTTY, NONE, 0);
|
||||
_(TIOCNXCL, NONE, 0);
|
||||
_(TIOCOUTQ, WRITE, sizeof(int));
|
||||
_(TIOCPKT, READ, sizeof(int));
|
||||
_(TIOCSCTTY, NONE, 0);
|
||||
_(TIOCSETD, READ, sizeof(int));
|
||||
_(TIOCSPGRP, READ, pid_t_sz);
|
||||
_(TIOCSTI, READ, sizeof(char));
|
||||
_(TIOCSWINSZ, READ, struct_winsize_sz);
|
||||
|
||||
#if !SANITIZER_IOS
|
||||
_(SIOCADDMULTI, READ, struct_ifreq_sz);
|
||||
_(SIOCDELMULTI, READ, struct_ifreq_sz);
|
||||
_(SIOCGIFADDR, WRITE, struct_ifreq_sz);
|
||||
_(SIOCGIFBRDADDR, WRITE, struct_ifreq_sz);
|
||||
_(SIOCGIFDSTADDR, WRITE, struct_ifreq_sz);
|
||||
_(SIOCGIFFLAGS, WRITE, struct_ifreq_sz);
|
||||
_(SIOCGIFMETRIC, WRITE, struct_ifreq_sz);
|
||||
_(SIOCGIFMTU, WRITE, struct_ifreq_sz);
|
||||
_(SIOCGIFNETMASK, WRITE, struct_ifreq_sz);
|
||||
_(SIOCSIFADDR, READ, struct_ifreq_sz);
|
||||
_(SIOCSIFBRDADDR, READ, struct_ifreq_sz);
|
||||
_(SIOCSIFDSTADDR, READ, struct_ifreq_sz);
|
||||
_(SIOCSIFFLAGS, READ, struct_ifreq_sz);
|
||||
_(SIOCSIFMETRIC, READ, struct_ifreq_sz);
|
||||
_(SIOCSIFMTU, READ, struct_ifreq_sz);
|
||||
_(SIOCSIFNETMASK, READ, struct_ifreq_sz);
|
||||
#endif
|
||||
|
||||
#if (SANITIZER_LINUX && !SANITIZER_ANDROID)
|
||||
_(SIOCGETSGCNT, WRITE, struct_sioc_sg_req_sz);
|
||||
_(SIOCGETVIFCNT, WRITE, struct_sioc_vif_req_sz);
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
// Conflicting request ids.
|
||||
// _(CDROMAUDIOBUFSIZ, NONE, 0);
|
||||
// _(SNDCTL_TMR_CONTINUE, NONE, 0);
|
||||
// _(SNDCTL_TMR_START, NONE, 0);
|
||||
// _(SNDCTL_TMR_STOP, NONE, 0);
|
||||
// _(SOUND_MIXER_READ_LOUD, WRITE, sizeof(int)); // same as ...READ_ENHANCE
|
||||
// _(SOUND_MIXER_READ_MUTE, WRITE, sizeof(int)); // same as ...READ_ENHANCE
|
||||
// _(SOUND_MIXER_WRITE_LOUD, WRITE, sizeof(int)); // same as ...WRITE_ENHANCE
|
||||
// _(SOUND_MIXER_WRITE_MUTE, WRITE, sizeof(int)); // same as ...WRITE_ENHANCE
|
||||
_(BLKFLSBUF, NONE, 0);
|
||||
_(BLKGETSIZE, WRITE, sizeof(uptr));
|
||||
_(BLKRAGET, WRITE, sizeof(int));
|
||||
_(BLKRASET, NONE, 0);
|
||||
_(BLKROGET, WRITE, sizeof(int));
|
||||
_(BLKROSET, READ, sizeof(int));
|
||||
_(BLKRRPART, NONE, 0);
|
||||
_(CDROMEJECT, NONE, 0);
|
||||
_(CDROMEJECT_SW, NONE, 0);
|
||||
_(CDROMMULTISESSION, WRITE, struct_cdrom_multisession_sz);
|
||||
_(CDROMPAUSE, NONE, 0);
|
||||
_(CDROMPLAYMSF, READ, struct_cdrom_msf_sz);
|
||||
_(CDROMPLAYTRKIND, READ, struct_cdrom_ti_sz);
|
||||
_(CDROMREADAUDIO, READ, struct_cdrom_read_audio_sz);
|
||||
_(CDROMREADCOOKED, READ, struct_cdrom_msf_sz);
|
||||
_(CDROMREADMODE1, READ, struct_cdrom_msf_sz);
|
||||
_(CDROMREADMODE2, READ, struct_cdrom_msf_sz);
|
||||
_(CDROMREADRAW, READ, struct_cdrom_msf_sz);
|
||||
_(CDROMREADTOCENTRY, WRITE, struct_cdrom_tocentry_sz);
|
||||
_(CDROMREADTOCHDR, WRITE, struct_cdrom_tochdr_sz);
|
||||
_(CDROMRESET, NONE, 0);
|
||||
_(CDROMRESUME, NONE, 0);
|
||||
_(CDROMSEEK, READ, struct_cdrom_msf_sz);
|
||||
_(CDROMSTART, NONE, 0);
|
||||
_(CDROMSTOP, NONE, 0);
|
||||
_(CDROMSUBCHNL, WRITE, struct_cdrom_subchnl_sz);
|
||||
_(CDROMVOLCTRL, READ, struct_cdrom_volctrl_sz);
|
||||
_(CDROMVOLREAD, WRITE, struct_cdrom_volctrl_sz);
|
||||
_(CDROM_GET_UPC, WRITE, 8);
|
||||
_(EVIOCGABS, WRITE, struct_input_absinfo_sz); // fixup
|
||||
_(EVIOCGBIT, WRITE, struct_input_id_sz); // fixup
|
||||
_(EVIOCGEFFECTS, WRITE, sizeof(int));
|
||||
_(EVIOCGID, WRITE, struct_input_id_sz);
|
||||
_(EVIOCGKEY, WRITE, 0);
|
||||
_(EVIOCGKEYCODE, WRITE, sizeof(int) * 2);
|
||||
_(EVIOCGLED, WRITE, 0);
|
||||
_(EVIOCGNAME, WRITE, 0);
|
||||
_(EVIOCGPHYS, WRITE, 0);
|
||||
_(EVIOCGRAB, READ, sizeof(int));
|
||||
_(EVIOCGREP, WRITE, sizeof(int) * 2);
|
||||
_(EVIOCGSND, WRITE, 0);
|
||||
_(EVIOCGSW, WRITE, 0);
|
||||
_(EVIOCGUNIQ, WRITE, 0);
|
||||
_(EVIOCGVERSION, WRITE, sizeof(int));
|
||||
_(EVIOCRMFF, READ, sizeof(int));
|
||||
_(EVIOCSABS, READ, struct_input_absinfo_sz); // fixup
|
||||
_(EVIOCSFF, READ, struct_ff_effect_sz);
|
||||
_(EVIOCSKEYCODE, READ, sizeof(int) * 2);
|
||||
_(EVIOCSREP, READ, sizeof(int) * 2);
|
||||
_(FDCLRPRM, NONE, 0);
|
||||
_(FDDEFPRM, READ, struct_floppy_struct_sz);
|
||||
_(FDFLUSH, NONE, 0);
|
||||
_(FDFMTBEG, NONE, 0);
|
||||
_(FDFMTEND, NONE, 0);
|
||||
_(FDFMTTRK, READ, struct_format_descr_sz);
|
||||
_(FDGETDRVPRM, WRITE, struct_floppy_drive_params_sz);
|
||||
_(FDGETDRVSTAT, WRITE, struct_floppy_drive_struct_sz);
|
||||
_(FDGETDRVTYP, WRITE, 16);
|
||||
_(FDGETFDCSTAT, WRITE, struct_floppy_fdc_state_sz);
|
||||
_(FDGETMAXERRS, WRITE, struct_floppy_max_errors_sz);
|
||||
_(FDGETPRM, WRITE, struct_floppy_struct_sz);
|
||||
_(FDMSGOFF, NONE, 0);
|
||||
_(FDMSGON, NONE, 0);
|
||||
_(FDPOLLDRVSTAT, WRITE, struct_floppy_drive_struct_sz);
|
||||
_(FDRAWCMD, WRITE, struct_floppy_raw_cmd_sz);
|
||||
_(FDRESET, NONE, 0);
|
||||
_(FDSETDRVPRM, READ, struct_floppy_drive_params_sz);
|
||||
_(FDSETEMSGTRESH, NONE, 0);
|
||||
_(FDSETMAXERRS, READ, struct_floppy_max_errors_sz);
|
||||
_(FDSETPRM, READ, struct_floppy_struct_sz);
|
||||
_(FDTWADDLE, NONE, 0);
|
||||
_(FDWERRORCLR, NONE, 0);
|
||||
_(FDWERRORGET, WRITE, struct_floppy_write_errors_sz);
|
||||
_(HDIO_DRIVE_CMD, WRITE, sizeof(int));
|
||||
_(HDIO_GETGEO, WRITE, struct_hd_geometry_sz);
|
||||
_(HDIO_GET_32BIT, WRITE, sizeof(int));
|
||||
_(HDIO_GET_DMA, WRITE, sizeof(int));
|
||||
_(HDIO_GET_IDENTITY, WRITE, struct_hd_driveid_sz);
|
||||
_(HDIO_GET_KEEPSETTINGS, WRITE, sizeof(int));
|
||||
_(HDIO_GET_MULTCOUNT, WRITE, sizeof(int));
|
||||
_(HDIO_GET_NOWERR, WRITE, sizeof(int));
|
||||
_(HDIO_GET_UNMASKINTR, WRITE, sizeof(int));
|
||||
_(HDIO_SET_32BIT, NONE, 0);
|
||||
_(HDIO_SET_DMA, NONE, 0);
|
||||
_(HDIO_SET_KEEPSETTINGS, NONE, 0);
|
||||
_(HDIO_SET_MULTCOUNT, NONE, 0);
|
||||
_(HDIO_SET_NOWERR, NONE, 0);
|
||||
_(HDIO_SET_UNMASKINTR, NONE, 0);
|
||||
_(MTIOCGET, WRITE, struct_mtget_sz);
|
||||
_(MTIOCPOS, WRITE, struct_mtpos_sz);
|
||||
_(MTIOCTOP, READ, struct_mtop_sz);
|
||||
_(PPPIOCGASYNCMAP, WRITE, sizeof(int));
|
||||
_(PPPIOCGDEBUG, WRITE, sizeof(int));
|
||||
_(PPPIOCGFLAGS, WRITE, sizeof(int));
|
||||
_(PPPIOCGUNIT, WRITE, sizeof(int));
|
||||
_(PPPIOCGXASYNCMAP, WRITE, sizeof(int) * 8);
|
||||
_(PPPIOCSASYNCMAP, READ, sizeof(int));
|
||||
_(PPPIOCSDEBUG, READ, sizeof(int));
|
||||
_(PPPIOCSFLAGS, READ, sizeof(int));
|
||||
_(PPPIOCSMAXCID, READ, sizeof(int));
|
||||
_(PPPIOCSMRU, READ, sizeof(int));
|
||||
_(PPPIOCSXASYNCMAP, READ, sizeof(int) * 8);
|
||||
_(SIOCADDRT, READ, struct_rtentry_sz);
|
||||
_(SIOCDARP, READ, struct_arpreq_sz);
|
||||
_(SIOCDELRT, READ, struct_rtentry_sz);
|
||||
_(SIOCDRARP, READ, struct_arpreq_sz);
|
||||
_(SIOCGARP, WRITE, struct_arpreq_sz);
|
||||
_(SIOCGIFENCAP, WRITE, sizeof(int));
|
||||
_(SIOCGIFHWADDR, WRITE, struct_ifreq_sz);
|
||||
_(SIOCGIFMAP, WRITE, struct_ifreq_sz);
|
||||
_(SIOCGIFMEM, WRITE, struct_ifreq_sz);
|
||||
_(SIOCGIFNAME, NONE, 0);
|
||||
_(SIOCGIFSLAVE, NONE, 0);
|
||||
_(SIOCGRARP, WRITE, struct_arpreq_sz);
|
||||
_(SIOCGSTAMP, WRITE, timeval_sz);
|
||||
_(SIOCSARP, READ, struct_arpreq_sz);
|
||||
_(SIOCSIFENCAP, READ, sizeof(int));
|
||||
_(SIOCSIFHWADDR, READ, struct_ifreq_sz);
|
||||
_(SIOCSIFLINK, NONE, 0);
|
||||
_(SIOCSIFMAP, READ, struct_ifreq_sz);
|
||||
_(SIOCSIFMEM, READ, struct_ifreq_sz);
|
||||
_(SIOCSIFSLAVE, NONE, 0);
|
||||
_(SIOCSRARP, READ, struct_arpreq_sz);
|
||||
_(SNDCTL_COPR_HALT, WRITE, struct_copr_debug_buf_sz);
|
||||
_(SNDCTL_COPR_LOAD, READ, struct_copr_buffer_sz);
|
||||
_(SNDCTL_COPR_RCODE, WRITE, struct_copr_debug_buf_sz);
|
||||
_(SNDCTL_COPR_RCVMSG, WRITE, struct_copr_msg_sz);
|
||||
_(SNDCTL_COPR_RDATA, WRITE, struct_copr_debug_buf_sz);
|
||||
_(SNDCTL_COPR_RESET, NONE, 0);
|
||||
_(SNDCTL_COPR_RUN, WRITE, struct_copr_debug_buf_sz);
|
||||
_(SNDCTL_COPR_SENDMSG, READ, struct_copr_msg_sz);
|
||||
_(SNDCTL_COPR_WCODE, READ, struct_copr_debug_buf_sz);
|
||||
_(SNDCTL_COPR_WDATA, READ, struct_copr_debug_buf_sz);
|
||||
_(SNDCTL_DSP_GETBLKSIZE, WRITE, sizeof(int));
|
||||
_(SNDCTL_DSP_GETFMTS, WRITE, sizeof(int));
|
||||
_(SNDCTL_DSP_NONBLOCK, NONE, 0);
|
||||
_(SNDCTL_DSP_POST, NONE, 0);
|
||||
_(SNDCTL_DSP_RESET, NONE, 0);
|
||||
_(SNDCTL_DSP_SETFMT, WRITE, sizeof(int));
|
||||
_(SNDCTL_DSP_SETFRAGMENT, WRITE, sizeof(int));
|
||||
_(SNDCTL_DSP_SPEED, WRITE, sizeof(int));
|
||||
_(SNDCTL_DSP_STEREO, WRITE, sizeof(int));
|
||||
_(SNDCTL_DSP_SUBDIVIDE, WRITE, sizeof(int));
|
||||
_(SNDCTL_DSP_SYNC, NONE, 0);
|
||||
_(SNDCTL_FM_4OP_ENABLE, READ, sizeof(int));
|
||||
_(SNDCTL_FM_LOAD_INSTR, READ, struct_sbi_instrument_sz);
|
||||
_(SNDCTL_MIDI_INFO, WRITE, struct_midi_info_sz);
|
||||
_(SNDCTL_MIDI_PRETIME, WRITE, sizeof(int));
|
||||
_(SNDCTL_SEQ_CTRLRATE, WRITE, sizeof(int));
|
||||
_(SNDCTL_SEQ_GETINCOUNT, WRITE, sizeof(int));
|
||||
_(SNDCTL_SEQ_GETOUTCOUNT, WRITE, sizeof(int));
|
||||
_(SNDCTL_SEQ_NRMIDIS, WRITE, sizeof(int));
|
||||
_(SNDCTL_SEQ_NRSYNTHS, WRITE, sizeof(int));
|
||||
_(SNDCTL_SEQ_OUTOFBAND, READ, struct_seq_event_rec_sz);
|
||||
_(SNDCTL_SEQ_PANIC, NONE, 0);
|
||||
_(SNDCTL_SEQ_PERCMODE, NONE, 0);
|
||||
_(SNDCTL_SEQ_RESET, NONE, 0);
|
||||
_(SNDCTL_SEQ_RESETSAMPLES, READ, sizeof(int));
|
||||
_(SNDCTL_SEQ_SYNC, NONE, 0);
|
||||
_(SNDCTL_SEQ_TESTMIDI, READ, sizeof(int));
|
||||
_(SNDCTL_SEQ_THRESHOLD, READ, sizeof(int));
|
||||
_(SNDCTL_SYNTH_INFO, WRITE, struct_synth_info_sz);
|
||||
_(SNDCTL_SYNTH_MEMAVL, WRITE, sizeof(int));
|
||||
_(SNDCTL_TMR_METRONOME, READ, sizeof(int));
|
||||
_(SNDCTL_TMR_SELECT, WRITE, sizeof(int));
|
||||
_(SNDCTL_TMR_SOURCE, WRITE, sizeof(int));
|
||||
_(SNDCTL_TMR_TEMPO, WRITE, sizeof(int));
|
||||
_(SNDCTL_TMR_TIMEBASE, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_ALTPCM, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_BASS, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_CAPS, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_CD, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_DEVMASK, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_ENHANCE, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_IGAIN, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_IMIX, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_LINE, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_LINE1, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_LINE2, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_LINE3, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_MIC, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_OGAIN, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_PCM, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_RECLEV, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_RECMASK, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_RECSRC, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_SPEAKER, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_STEREODEVS, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_SYNTH, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_TREBLE, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_READ_VOLUME, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_ALTPCM, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_BASS, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_CD, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_ENHANCE, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_IGAIN, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_IMIX, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_LINE, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_LINE1, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_LINE2, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_LINE3, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_MIC, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_OGAIN, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_PCM, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_RECLEV, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_RECSRC, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_SPEAKER, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_SYNTH, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_TREBLE, WRITE, sizeof(int));
|
||||
_(SOUND_MIXER_WRITE_VOLUME, WRITE, sizeof(int));
|
||||
_(SOUND_PCM_READ_BITS, WRITE, sizeof(int));
|
||||
_(SOUND_PCM_READ_CHANNELS, WRITE, sizeof(int));
|
||||
_(SOUND_PCM_READ_FILTER, WRITE, sizeof(int));
|
||||
_(SOUND_PCM_READ_RATE, WRITE, sizeof(int));
|
||||
_(SOUND_PCM_WRITE_CHANNELS, WRITE, sizeof(int));
|
||||
_(SOUND_PCM_WRITE_FILTER, WRITE, sizeof(int));
|
||||
_(TCFLSH, NONE, 0);
|
||||
_(TCGETA, WRITE, struct_termio_sz);
|
||||
_(TCGETS, WRITE, struct_termios_sz);
|
||||
_(TCSBRK, NONE, 0);
|
||||
_(TCSBRKP, NONE, 0);
|
||||
_(TCSETA, READ, struct_termio_sz);
|
||||
_(TCSETAF, READ, struct_termio_sz);
|
||||
_(TCSETAW, READ, struct_termio_sz);
|
||||
_(TCSETS, READ, struct_termios_sz);
|
||||
_(TCSETSF, READ, struct_termios_sz);
|
||||
_(TCSETSW, READ, struct_termios_sz);
|
||||
_(TCXONC, NONE, 0);
|
||||
_(TIOCGLCKTRMIOS, WRITE, struct_termios_sz);
|
||||
_(TIOCGSOFTCAR, WRITE, sizeof(int));
|
||||
_(TIOCINQ, WRITE, sizeof(int));
|
||||
_(TIOCLINUX, READ, sizeof(char));
|
||||
_(TIOCSERCONFIG, NONE, 0);
|
||||
_(TIOCSERGETLSR, WRITE, sizeof(int));
|
||||
_(TIOCSERGWILD, WRITE, sizeof(int));
|
||||
_(TIOCSERSWILD, READ, sizeof(int));
|
||||
_(TIOCSLCKTRMIOS, READ, struct_termios_sz);
|
||||
_(TIOCSSOFTCAR, READ, sizeof(int));
|
||||
_(VT_ACTIVATE, NONE, 0);
|
||||
_(VT_DISALLOCATE, NONE, 0);
|
||||
_(VT_GETMODE, WRITE, struct_vt_mode_sz);
|
||||
_(VT_GETSTATE, WRITE, struct_vt_stat_sz);
|
||||
_(VT_OPENQRY, WRITE, sizeof(int));
|
||||
_(VT_RELDISP, NONE, 0);
|
||||
_(VT_RESIZE, READ, struct_vt_sizes_sz);
|
||||
_(VT_RESIZEX, READ, struct_vt_consize_sz);
|
||||
_(VT_SENDSIG, NONE, 0);
|
||||
_(VT_SETMODE, READ, struct_vt_mode_sz);
|
||||
_(VT_WAITACTIVE, NONE, 0);
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
// _(SIOCDEVPLIP, WRITE, struct_ifreq_sz); // the same as EQL_ENSLAVE
|
||||
_(CYGETDEFTHRESH, WRITE, sizeof(int));
|
||||
_(CYGETDEFTIMEOUT, WRITE, sizeof(int));
|
||||
_(CYGETMON, WRITE, struct_cyclades_monitor_sz);
|
||||
_(CYGETTHRESH, WRITE, sizeof(int));
|
||||
_(CYGETTIMEOUT, WRITE, sizeof(int));
|
||||
_(CYSETDEFTHRESH, NONE, 0);
|
||||
_(CYSETDEFTIMEOUT, NONE, 0);
|
||||
_(CYSETTHRESH, NONE, 0);
|
||||
_(CYSETTIMEOUT, NONE, 0);
|
||||
_(EQL_EMANCIPATE, WRITE, struct_ifreq_sz);
|
||||
_(EQL_ENSLAVE, WRITE, struct_ifreq_sz);
|
||||
_(EQL_GETMASTRCFG, WRITE, struct_ifreq_sz);
|
||||
_(EQL_GETSLAVECFG, WRITE, struct_ifreq_sz);
|
||||
_(EQL_SETMASTRCFG, WRITE, struct_ifreq_sz);
|
||||
_(EQL_SETSLAVECFG, WRITE, struct_ifreq_sz);
|
||||
_(EVIOCGKEYCODE_V2, WRITE, struct_input_keymap_entry_sz);
|
||||
_(EVIOCGPROP, WRITE, 0);
|
||||
_(EVIOCSKEYCODE_V2, READ, struct_input_keymap_entry_sz);
|
||||
_(FS_IOC_GETFLAGS, WRITE, sizeof(int));
|
||||
_(FS_IOC_GETVERSION, WRITE, sizeof(int));
|
||||
_(FS_IOC_SETFLAGS, READ, sizeof(int));
|
||||
_(FS_IOC_SETVERSION, READ, sizeof(int));
|
||||
_(GIO_CMAP, WRITE, 48);
|
||||
_(GIO_FONT, WRITE, 8192);
|
||||
_(GIO_SCRNMAP, WRITE, e_tabsz);
|
||||
_(GIO_UNIMAP, WRITE, struct_unimapdesc_sz);
|
||||
_(GIO_UNISCRNMAP, WRITE, sizeof(short) * e_tabsz);
|
||||
_(KDADDIO, NONE, 0);
|
||||
_(KDDELIO, NONE, 0);
|
||||
_(KDDISABIO, NONE, 0);
|
||||
_(KDENABIO, NONE, 0);
|
||||
_(KDGETKEYCODE, WRITE, struct_kbkeycode_sz);
|
||||
_(KDGETLED, WRITE, 1);
|
||||
_(KDGETMODE, WRITE, sizeof(int));
|
||||
_(KDGKBDIACR, WRITE, struct_kbdiacrs_sz);
|
||||
_(KDGKBENT, WRITE, struct_kbentry_sz);
|
||||
_(KDGKBLED, WRITE, sizeof(int));
|
||||
_(KDGKBMETA, WRITE, sizeof(int));
|
||||
_(KDGKBMODE, WRITE, sizeof(int));
|
||||
_(KDGKBSENT, WRITE, struct_kbsentry_sz);
|
||||
_(KDGKBTYPE, WRITE, 1);
|
||||
_(KDMAPDISP, NONE, 0);
|
||||
_(KDMKTONE, NONE, 0);
|
||||
_(KDSETKEYCODE, READ, struct_kbkeycode_sz);
|
||||
_(KDSETLED, NONE, 0);
|
||||
_(KDSETMODE, NONE, 0);
|
||||
_(KDSIGACCEPT, NONE, 0);
|
||||
_(KDSKBDIACR, READ, struct_kbdiacrs_sz);
|
||||
_(KDSKBENT, READ, struct_kbentry_sz);
|
||||
_(KDSKBLED, NONE, 0);
|
||||
_(KDSKBMETA, NONE, 0);
|
||||
_(KDSKBMODE, NONE, 0);
|
||||
_(KDSKBSENT, READ, struct_kbsentry_sz);
|
||||
_(KDUNMAPDISP, NONE, 0);
|
||||
_(KIOCSOUND, NONE, 0);
|
||||
_(LPABORT, NONE, 0);
|
||||
_(LPABORTOPEN, NONE, 0);
|
||||
_(LPCAREFUL, NONE, 0);
|
||||
_(LPCHAR, NONE, 0);
|
||||
_(LPGETIRQ, WRITE, sizeof(int));
|
||||
_(LPGETSTATUS, WRITE, sizeof(int));
|
||||
_(LPRESET, NONE, 0);
|
||||
_(LPSETIRQ, NONE, 0);
|
||||
_(LPTIME, NONE, 0);
|
||||
_(LPWAIT, NONE, 0);
|
||||
_(MTIOCGETCONFIG, WRITE, struct_mtconfiginfo_sz);
|
||||
_(MTIOCSETCONFIG, READ, struct_mtconfiginfo_sz);
|
||||
_(PIO_CMAP, NONE, 0);
|
||||
_(PIO_FONT, READ, 8192);
|
||||
_(PIO_SCRNMAP, READ, e_tabsz);
|
||||
_(PIO_UNIMAP, READ, struct_unimapdesc_sz);
|
||||
_(PIO_UNIMAPCLR, READ, struct_unimapinit_sz);
|
||||
_(PIO_UNISCRNMAP, READ, sizeof(short) * e_tabsz);
|
||||
_(SCSI_IOCTL_PROBE_HOST, READ, sizeof(int));
|
||||
_(SCSI_IOCTL_TAGGED_DISABLE, NONE, 0);
|
||||
_(SCSI_IOCTL_TAGGED_ENABLE, NONE, 0);
|
||||
_(SNDCTL_DSP_GETISPACE, WRITE, struct_audio_buf_info_sz);
|
||||
_(SNDCTL_DSP_GETOSPACE, WRITE, struct_audio_buf_info_sz);
|
||||
_(TIOCGSERIAL, WRITE, struct_serial_struct_sz);
|
||||
_(TIOCSERGETMULTI, WRITE, struct_serial_multiport_struct_sz);
|
||||
_(TIOCSERSETMULTI, READ, struct_serial_multiport_struct_sz);
|
||||
_(TIOCSSERIAL, READ, struct_serial_struct_sz);
|
||||
|
||||
// The following ioctl requests are shared between AX25, IPX, netrom and
|
||||
// mrouted.
|
||||
// _(SIOCAIPXITFCRT, READ, sizeof(char));
|
||||
// _(SIOCAX25GETUID, READ, struct_sockaddr_ax25_sz);
|
||||
// _(SIOCNRGETPARMS, WRITE, struct_nr_parms_struct_sz);
|
||||
// _(SIOCAIPXPRISLT, READ, sizeof(char));
|
||||
// _(SIOCNRSETPARMS, READ, struct_nr_parms_struct_sz);
|
||||
// _(SIOCAX25ADDUID, READ, struct_sockaddr_ax25_sz);
|
||||
// _(SIOCNRDECOBS, NONE, 0);
|
||||
// _(SIOCAX25DELUID, READ, struct_sockaddr_ax25_sz);
|
||||
// _(SIOCIPXCFGDATA, WRITE, struct_ipx_config_data_sz);
|
||||
// _(SIOCAX25NOUID, READ, sizeof(int));
|
||||
// _(SIOCNRRTCTL, READ, sizeof(int));
|
||||
// _(SIOCAX25DIGCTL, READ, sizeof(int));
|
||||
// _(SIOCAX25GETPARMS, WRITE, struct_ax25_parms_struct_sz);
|
||||
// _(SIOCAX25SETPARMS, READ, struct_ax25_parms_struct_sz);
|
||||
#endif
|
||||
#undef _
|
||||
}
|
||||
|
||||
static bool ioctl_initialized = false;
|
||||
|
||||
struct ioctl_desc_compare {
|
||||
bool operator()(const ioctl_desc& left, const ioctl_desc& right) const {
|
||||
return left.req < right.req;
|
||||
}
|
||||
};
|
||||
|
||||
static void ioctl_init() {
|
||||
ioctl_table_fill();
|
||||
Sort(ioctl_table, ioctl_table_size, ioctl_desc_compare());
|
||||
|
||||
bool bad = false;
|
||||
for (unsigned i = 0; i < ioctl_table_size - 1; ++i) {
|
||||
if (ioctl_table[i].req >= ioctl_table[i + 1].req) {
|
||||
Printf("Duplicate or unsorted ioctl request id %x >= %x (%s vs %s)\n",
|
||||
ioctl_table[i].req, ioctl_table[i + 1].req, ioctl_table[i].name,
|
||||
ioctl_table[i + 1].name);
|
||||
bad = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (bad) Die();
|
||||
|
||||
ioctl_initialized = true;
|
||||
}
|
||||
|
||||
// Handle the most evil ioctls that encode argument value as part of request id.
|
||||
static unsigned ioctl_request_fixup(unsigned req) {
|
||||
#if SANITIZER_LINUX
|
||||
// Strip size and event number.
|
||||
const unsigned kEviocgbitMask =
|
||||
(IOC_SIZEMASK << IOC_SIZESHIFT) | EVIOC_EV_MAX;
|
||||
if ((req & ~kEviocgbitMask) == IOCTL_EVIOCGBIT)
|
||||
return IOCTL_EVIOCGBIT;
|
||||
// Strip absolute axis number.
|
||||
if ((req & ~EVIOC_ABS_MAX) == IOCTL_EVIOCGABS)
|
||||
return IOCTL_EVIOCGABS;
|
||||
if ((req & ~EVIOC_ABS_MAX) == IOCTL_EVIOCSABS)
|
||||
return IOCTL_EVIOCSABS;
|
||||
#endif
|
||||
return req;
|
||||
}
|
||||
|
||||
static const ioctl_desc *ioctl_table_lookup(unsigned req) {
|
||||
int left = 0;
|
||||
int right = ioctl_table_size;
|
||||
while (left < right) {
|
||||
int mid = (left + right) / 2;
|
||||
if (ioctl_table[mid].req < req)
|
||||
left = mid + 1;
|
||||
else
|
||||
right = mid;
|
||||
}
|
||||
if (left == right && ioctl_table[left].req == req)
|
||||
return ioctl_table + left;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
|
||||
CHECK(desc);
|
||||
desc->req = req;
|
||||
desc->name = "<DECODED_IOCTL>";
|
||||
desc->size = IOC_SIZE(req);
|
||||
// Sanity check.
|
||||
if (desc->size > 0xFFFF) return false;
|
||||
unsigned dir = IOC_DIR(req);
|
||||
switch (dir) {
|
||||
case IOC_NONE:
|
||||
desc->type = ioctl_desc::NONE;
|
||||
break;
|
||||
case IOC_READ | IOC_WRITE:
|
||||
desc->type = ioctl_desc::READWRITE;
|
||||
break;
|
||||
case IOC_READ:
|
||||
desc->type = ioctl_desc::WRITE;
|
||||
break;
|
||||
case IOC_WRITE:
|
||||
desc->type = ioctl_desc::READ;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// Size can be 0 iff type is NONE.
|
||||
if ((desc->type == IOC_NONE) != (desc->size == 0)) return false;
|
||||
// Sanity check.
|
||||
if (IOC_TYPE(req) == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const ioctl_desc *ioctl_lookup(unsigned req) {
|
||||
req = ioctl_request_fixup(req);
|
||||
const ioctl_desc *desc = ioctl_table_lookup(req);
|
||||
if (desc) return desc;
|
||||
|
||||
// Try stripping access size from the request id.
|
||||
desc = ioctl_table_lookup(req & ~(IOC_SIZEMASK << IOC_SIZESHIFT));
|
||||
// Sanity check: requests that encode access size are either read or write and
|
||||
// have size of 0 in the table.
|
||||
if (desc && desc->size == 0 &&
|
||||
(desc->type == ioctl_desc::READWRITE || desc->type == ioctl_desc::WRITE ||
|
||||
desc->type == ioctl_desc::READ))
|
||||
return desc;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d,
|
||||
unsigned request, void *arg) {
|
||||
if (desc->type == ioctl_desc::READ || desc->type == ioctl_desc::READWRITE) {
|
||||
unsigned size = desc->size ? desc->size : IOC_SIZE(request);
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, arg, size);
|
||||
}
|
||||
if (desc->type != ioctl_desc::CUSTOM)
|
||||
return;
|
||||
if (request == IOCTL_SIOCGIFCONF) {
|
||||
struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, (char*)&ifc->ifc_len,
|
||||
sizeof(ifc->ifc_len));
|
||||
}
|
||||
}
|
||||
|
||||
static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d,
|
||||
unsigned request, void *arg) {
|
||||
if (desc->type == ioctl_desc::WRITE || desc->type == ioctl_desc::READWRITE) {
|
||||
// FIXME: add verbose output
|
||||
unsigned size = desc->size ? desc->size : IOC_SIZE(request);
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, arg, size);
|
||||
}
|
||||
if (desc->type != ioctl_desc::CUSTOM)
|
||||
return;
|
||||
if (request == IOCTL_SIOCGIFCONF) {
|
||||
struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,128 @@
|
||||
//===-- sanitizer_common_interceptors_netbsd_compat.inc ---------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Common function interceptors for tools like AddressSanitizer,
|
||||
// ThreadSanitizer, MemorySanitizer, etc.
|
||||
//
|
||||
// Interceptors for NetBSD old function calls that have been versioned.
|
||||
//
|
||||
// NetBSD minimal version supported 9.0.
|
||||
// NetBSD current version supported 9.99.26.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if SANITIZER_NETBSD
|
||||
|
||||
// First undef all mangled symbols.
|
||||
// Next, define compat interceptors.
|
||||
// Finally, undef INIT_ and redefine it.
|
||||
// This allows to avoid preprocessor issues.
|
||||
|
||||
#undef fstatvfs
|
||||
#undef fstatvfs1
|
||||
#undef getmntinfo
|
||||
#undef getvfsstat
|
||||
#undef statvfs
|
||||
#undef statvfs1
|
||||
|
||||
INTERCEPTOR(int, statvfs, char *path, void *buf) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, statvfs, path, buf);
|
||||
if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
int res = REAL(statvfs)(path, buf);
|
||||
if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs90_sz);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, fstatvfs, int fd, void *buf) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
int res = REAL(fstatvfs)(fd, buf);
|
||||
if (!res) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs90_sz);
|
||||
if (fd >= 0)
|
||||
COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#undef INIT_STATVFS
|
||||
#define INIT_STATVFS \
|
||||
COMMON_INTERCEPT_FUNCTION(statvfs); \
|
||||
COMMON_INTERCEPT_FUNCTION(fstatvfs); \
|
||||
COMMON_INTERCEPT_FUNCTION(__statvfs90); \
|
||||
COMMON_INTERCEPT_FUNCTION(__fstatvfs90)
|
||||
|
||||
INTERCEPTOR(int, __getmntinfo13, void **mntbufp, int flags) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, __getmntinfo13, mntbufp, flags);
|
||||
int cnt = REAL(__getmntinfo13)(mntbufp, flags);
|
||||
if (cnt > 0 && mntbufp) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mntbufp, sizeof(void *));
|
||||
if (*mntbufp)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *mntbufp, cnt * struct_statvfs90_sz);
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
#undef INIT_GETMNTINFO
|
||||
#define INIT_GETMNTINFO \
|
||||
COMMON_INTERCEPT_FUNCTION(__getmntinfo13); \
|
||||
COMMON_INTERCEPT_FUNCTION(__getmntinfo90)
|
||||
|
||||
INTERCEPTOR(int, getvfsstat, void *buf, SIZE_T bufsize, int flags) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, getvfsstat, buf, bufsize, flags);
|
||||
int ret = REAL(getvfsstat)(buf, bufsize, flags);
|
||||
if (buf && ret > 0)
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, ret * struct_statvfs90_sz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#undef INIT_GETVFSSTAT
|
||||
#define INIT_GETVFSSTAT \
|
||||
COMMON_INTERCEPT_FUNCTION(getvfsstat); \
|
||||
COMMON_INTERCEPT_FUNCTION(__getvfsstat90)
|
||||
|
||||
INTERCEPTOR(int, statvfs1, const char *path, void *buf, int flags) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, statvfs1, path, buf, flags);
|
||||
if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
|
||||
int res = REAL(statvfs1)(path, buf, flags);
|
||||
if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs90_sz);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, fstatvfs1, int fd, void *buf, int flags) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs1, fd, buf, flags);
|
||||
COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
|
||||
int res = REAL(fstatvfs1)(fd, buf, flags);
|
||||
if (!res) {
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs90_sz);
|
||||
if (fd >= 0)
|
||||
COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#undef INIT_STATVFS1
|
||||
#define INIT_STATVFS1 \
|
||||
COMMON_INTERCEPT_FUNCTION(statvfs1); \
|
||||
COMMON_INTERCEPT_FUNCTION(fstatvfs1); \
|
||||
COMMON_INTERCEPT_FUNCTION(__statvfs190); \
|
||||
COMMON_INTERCEPT_FUNCTION(__fstatvfs190)
|
||||
|
||||
#endif
|
||||
41
lib/tsan/sanitizer_common/sanitizer_common_interface.inc
Normal file
41
lib/tsan/sanitizer_common/sanitizer_common_interface.inc
Normal file
@@ -0,0 +1,41 @@
|
||||
//===-- sanitizer_common_interface.inc ------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Sanitizer Common interface list.
|
||||
//===----------------------------------------------------------------------===//
|
||||
INTERFACE_FUNCTION(__sanitizer_acquire_crash_state)
|
||||
INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
|
||||
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
|
||||
INTERFACE_FUNCTION(__sanitizer_set_death_callback)
|
||||
INTERFACE_FUNCTION(__sanitizer_set_report_path)
|
||||
INTERFACE_FUNCTION(__sanitizer_set_report_fd)
|
||||
INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_on_print)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_report_error_summary)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_sandbox_on_notify)
|
||||
// Sanitizer weak hooks
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_memcmp)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_strcmp)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_strncmp)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_weak_hook_strstr)
|
||||
// Stacktrace interface.
|
||||
INTERFACE_FUNCTION(__sanitizer_get_module_and_offset_for_pc)
|
||||
INTERFACE_FUNCTION(__sanitizer_symbolize_global)
|
||||
INTERFACE_FUNCTION(__sanitizer_symbolize_pc)
|
||||
// Allocator interface.
|
||||
INTERFACE_FUNCTION(__sanitizer_get_allocated_size)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_free_bytes)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_heap_size)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_ownership)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes)
|
||||
INTERFACE_FUNCTION(__sanitizer_install_malloc_and_free_hooks)
|
||||
INTERFACE_FUNCTION(__sanitizer_purge_allocator)
|
||||
INTERFACE_FUNCTION(__sanitizer_print_memory_profile)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_free_hook)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_malloc_hook)
|
||||
@@ -0,0 +1,13 @@
|
||||
//===-- sanitizer_common_interface_posix.inc ------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Sanitizer Common interface list only available for Posix systems.
|
||||
//===----------------------------------------------------------------------===//
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_symbolize_code)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_symbolize_data)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_symbolize_demangle)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_symbolize_flush)
|
||||
2914
lib/tsan/sanitizer_common/sanitizer_common_syscalls.inc
Normal file
2914
lib/tsan/sanitizer_common/sanitizer_common_syscalls.inc
Normal file
File diff suppressed because it is too large
Load Diff
33
lib/tsan/sanitizer_common/sanitizer_coverage_interface.inc
Normal file
33
lib/tsan/sanitizer_common/sanitizer_coverage_interface.inc
Normal file
@@ -0,0 +1,33 @@
|
||||
//===-- sanitizer_coverage_interface.inc ----------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Sanitizer Coverage interface list.
|
||||
//===----------------------------------------------------------------------===//
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_dump)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_reset)
|
||||
INTERFACE_FUNCTION(__sanitizer_dump_coverage)
|
||||
INTERFACE_FUNCTION(__sanitizer_dump_trace_pc_guard_coverage)
|
||||
INTERFACE_WEAK_FUNCTION(__sancov_default_options)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp1)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp2)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp4)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp8)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_const_cmp1)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_const_cmp2)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_const_cmp4)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_const_cmp8)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_div4)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_div8)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_gep)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_pc_guard)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_pc_guard_init)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_pc_indir)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_switch)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_8bit_counters_init)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_bool_flag_init)
|
||||
INTERFACE_WEAK_FUNCTION(__sanitizer_cov_pcs_init)
|
||||
41
lib/tsan/sanitizer_common/sanitizer_dbghelp.h
Normal file
41
lib/tsan/sanitizer_common/sanitizer_dbghelp.h
Normal file
@@ -0,0 +1,41 @@
|
||||
//===-- sanitizer_dbghelp.h ------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Wrappers for lazy loaded dbghelp.dll. Provides function pointers and a
|
||||
// callback to initialize them.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_SYMBOLIZER_WIN_H
|
||||
#define SANITIZER_SYMBOLIZER_WIN_H
|
||||
|
||||
#if !SANITIZER_WINDOWS
|
||||
#error "sanitizer_dbghelp.h is a Windows-only header"
|
||||
#endif
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
extern decltype(::StackWalk64) *StackWalk64;
|
||||
extern decltype(::SymCleanup) *SymCleanup;
|
||||
extern decltype(::SymFromAddr) *SymFromAddr;
|
||||
extern decltype(::SymFunctionTableAccess64) *SymFunctionTableAccess64;
|
||||
extern decltype(::SymGetLineFromAddr64) *SymGetLineFromAddr64;
|
||||
extern decltype(::SymGetModuleBase64) *SymGetModuleBase64;
|
||||
extern decltype(::SymGetSearchPathW) *SymGetSearchPathW;
|
||||
extern decltype(::SymInitialize) *SymInitialize;
|
||||
extern decltype(::SymSetOptions) *SymSetOptions;
|
||||
extern decltype(::SymSetSearchPathW) *SymSetSearchPathW;
|
||||
extern decltype(::UnDecorateSymbolName) *UnDecorateSymbolName;
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_SYMBOLIZER_WIN_H
|
||||
410
lib/tsan/sanitizer_common/sanitizer_deadlock_detector.h
Normal file
410
lib/tsan/sanitizer_common/sanitizer_deadlock_detector.h
Normal file
@@ -0,0 +1,410 @@
|
||||
//===-- sanitizer_deadlock_detector.h ---------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer runtime.
|
||||
// The deadlock detector maintains a directed graph of lock acquisitions.
|
||||
// When a lock event happens, the detector checks if the locks already held by
|
||||
// the current thread are reachable from the newly acquired lock.
|
||||
//
|
||||
// The detector can handle only a fixed amount of simultaneously live locks
|
||||
// (a lock is alive if it has been locked at least once and has not been
|
||||
// destroyed). When the maximal number of locks is reached the entire graph
|
||||
// is flushed and the new lock epoch is started. The node ids from the old
|
||||
// epochs can not be used with any of the detector methods except for
|
||||
// nodeBelongsToCurrentEpoch().
|
||||
//
|
||||
// FIXME: this is work in progress, nothing really works yet.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_DEADLOCK_DETECTOR_H
|
||||
#define SANITIZER_DEADLOCK_DETECTOR_H
|
||||
|
||||
#include "sanitizer_bvgraph.h"
|
||||
#include "sanitizer_common.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Thread-local state for DeadlockDetector.
|
||||
// It contains the locks currently held by the owning thread.
|
||||
template <class BV>
|
||||
class DeadlockDetectorTLS {
|
||||
public:
|
||||
// No CTOR.
|
||||
void clear() {
|
||||
bv_.clear();
|
||||
epoch_ = 0;
|
||||
n_recursive_locks = 0;
|
||||
n_all_locks_ = 0;
|
||||
}
|
||||
|
||||
bool empty() const { return bv_.empty(); }
|
||||
|
||||
void ensureCurrentEpoch(uptr current_epoch) {
|
||||
if (epoch_ == current_epoch) return;
|
||||
bv_.clear();
|
||||
epoch_ = current_epoch;
|
||||
n_recursive_locks = 0;
|
||||
n_all_locks_ = 0;
|
||||
}
|
||||
|
||||
uptr getEpoch() const { return epoch_; }
|
||||
|
||||
// Returns true if this is the first (non-recursive) acquisition of this lock.
|
||||
bool addLock(uptr lock_id, uptr current_epoch, u32 stk) {
|
||||
CHECK_EQ(epoch_, current_epoch);
|
||||
if (!bv_.setBit(lock_id)) {
|
||||
// The lock is already held by this thread, it must be recursive.
|
||||
CHECK_LT(n_recursive_locks, ARRAY_SIZE(recursive_locks));
|
||||
recursive_locks[n_recursive_locks++] = lock_id;
|
||||
return false;
|
||||
}
|
||||
CHECK_LT(n_all_locks_, ARRAY_SIZE(all_locks_with_contexts_));
|
||||
// lock_id < BV::kSize, can cast to a smaller int.
|
||||
u32 lock_id_short = static_cast<u32>(lock_id);
|
||||
LockWithContext l = {lock_id_short, stk};
|
||||
all_locks_with_contexts_[n_all_locks_++] = l;
|
||||
return true;
|
||||
}
|
||||
|
||||
void removeLock(uptr lock_id) {
|
||||
if (n_recursive_locks) {
|
||||
for (sptr i = n_recursive_locks - 1; i >= 0; i--) {
|
||||
if (recursive_locks[i] == lock_id) {
|
||||
n_recursive_locks--;
|
||||
Swap(recursive_locks[i], recursive_locks[n_recursive_locks]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
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)) {
|
||||
Swap(all_locks_with_contexts_[i],
|
||||
all_locks_with_contexts_[n_all_locks_ - 1]);
|
||||
n_all_locks_--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 findLockContext(uptr lock_id) {
|
||||
for (uptr i = 0; i < n_all_locks_; i++)
|
||||
if (all_locks_with_contexts_[i].lock == static_cast<u32>(lock_id))
|
||||
return all_locks_with_contexts_[i].stk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const BV &getLocks(uptr current_epoch) const {
|
||||
CHECK_EQ(epoch_, current_epoch);
|
||||
return bv_;
|
||||
}
|
||||
|
||||
uptr getNumLocks() const { return n_all_locks_; }
|
||||
uptr getLock(uptr idx) const { return all_locks_with_contexts_[idx].lock; }
|
||||
|
||||
private:
|
||||
BV bv_;
|
||||
uptr epoch_;
|
||||
uptr recursive_locks[64];
|
||||
uptr n_recursive_locks;
|
||||
struct LockWithContext {
|
||||
u32 lock;
|
||||
u32 stk;
|
||||
};
|
||||
LockWithContext all_locks_with_contexts_[64];
|
||||
uptr n_all_locks_;
|
||||
};
|
||||
|
||||
// DeadlockDetector.
|
||||
// For deadlock detection to work we need one global DeadlockDetector object
|
||||
// and one DeadlockDetectorTLS object per evey thread.
|
||||
// This class is not thread safe, all concurrent accesses should be guarded
|
||||
// by an external lock.
|
||||
// Most of the methods of this class are not thread-safe (i.e. should
|
||||
// be protected by an external lock) unless explicitly told otherwise.
|
||||
template <class BV>
|
||||
class DeadlockDetector {
|
||||
public:
|
||||
typedef BV BitVector;
|
||||
|
||||
uptr size() const { return g_.size(); }
|
||||
|
||||
// No CTOR.
|
||||
void clear() {
|
||||
current_epoch_ = 0;
|
||||
available_nodes_.clear();
|
||||
recycled_nodes_.clear();
|
||||
g_.clear();
|
||||
n_edges_ = 0;
|
||||
}
|
||||
|
||||
// Allocate new deadlock detector node.
|
||||
// If we are out of available nodes first try to recycle some.
|
||||
// If there is nothing to recycle, flush the graph and increment the epoch.
|
||||
// Associate 'data' (opaque user's object) with the new node.
|
||||
uptr newNode(uptr data) {
|
||||
if (!available_nodes_.empty())
|
||||
return getAvailableNode(data);
|
||||
if (!recycled_nodes_.empty()) {
|
||||
for (sptr i = n_edges_ - 1; i >= 0; i--) {
|
||||
if (recycled_nodes_.getBit(edges_[i].from) ||
|
||||
recycled_nodes_.getBit(edges_[i].to)) {
|
||||
Swap(edges_[i], edges_[n_edges_ - 1]);
|
||||
n_edges_--;
|
||||
}
|
||||
}
|
||||
CHECK(available_nodes_.empty());
|
||||
// removeEdgesFrom was called in removeNode.
|
||||
g_.removeEdgesTo(recycled_nodes_);
|
||||
available_nodes_.setUnion(recycled_nodes_);
|
||||
recycled_nodes_.clear();
|
||||
return getAvailableNode(data);
|
||||
}
|
||||
// We are out of vacant nodes. Flush and increment the current_epoch_.
|
||||
current_epoch_ += size();
|
||||
recycled_nodes_.clear();
|
||||
available_nodes_.setAll();
|
||||
g_.clear();
|
||||
n_edges_ = 0;
|
||||
return getAvailableNode(data);
|
||||
}
|
||||
|
||||
// Get data associated with the node created by newNode().
|
||||
uptr getData(uptr node) const { return data_[nodeToIndex(node)]; }
|
||||
|
||||
bool nodeBelongsToCurrentEpoch(uptr node) {
|
||||
return node && (node / size() * size()) == current_epoch_;
|
||||
}
|
||||
|
||||
void removeNode(uptr node) {
|
||||
uptr idx = nodeToIndex(node);
|
||||
CHECK(!available_nodes_.getBit(idx));
|
||||
CHECK(recycled_nodes_.setBit(idx));
|
||||
g_.removeEdgesFrom(idx);
|
||||
}
|
||||
|
||||
void ensureCurrentEpoch(DeadlockDetectorTLS<BV> *dtls) {
|
||||
dtls->ensureCurrentEpoch(current_epoch_);
|
||||
}
|
||||
|
||||
// Returns true if there is a cycle in the graph after this lock event.
|
||||
// Ideally should be called before the lock is acquired so that we can
|
||||
// report a deadlock before a real deadlock happens.
|
||||
bool onLockBefore(DeadlockDetectorTLS<BV> *dtls, uptr cur_node) {
|
||||
ensureCurrentEpoch(dtls);
|
||||
uptr cur_idx = nodeToIndex(cur_node);
|
||||
return g_.isReachable(cur_idx, dtls->getLocks(current_epoch_));
|
||||
}
|
||||
|
||||
u32 findLockContext(DeadlockDetectorTLS<BV> *dtls, uptr node) {
|
||||
return dtls->findLockContext(nodeToIndex(node));
|
||||
}
|
||||
|
||||
// Add cur_node to the set of locks held currently by dtls.
|
||||
void onLockAfter(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
|
||||
ensureCurrentEpoch(dtls);
|
||||
uptr cur_idx = nodeToIndex(cur_node);
|
||||
dtls->addLock(cur_idx, current_epoch_, stk);
|
||||
}
|
||||
|
||||
// Experimental *racy* fast path function.
|
||||
// Returns true if all edges from the currently held locks to cur_node exist.
|
||||
bool hasAllEdges(DeadlockDetectorTLS<BV> *dtls, uptr cur_node) {
|
||||
uptr local_epoch = dtls->getEpoch();
|
||||
// Read from current_epoch_ is racy.
|
||||
if (cur_node && local_epoch == current_epoch_ &&
|
||||
local_epoch == nodeToEpoch(cur_node)) {
|
||||
uptr cur_idx = nodeToIndexUnchecked(cur_node);
|
||||
for (uptr i = 0, n = dtls->getNumLocks(); i < n; i++) {
|
||||
if (!g_.hasEdge(dtls->getLock(i), cur_idx))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adds edges from currently held locks to cur_node,
|
||||
// returns the number of added edges, and puts the sources of added edges
|
||||
// into added_edges[].
|
||||
// Should be called before onLockAfter.
|
||||
uptr addEdges(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk,
|
||||
int unique_tid) {
|
||||
ensureCurrentEpoch(dtls);
|
||||
uptr cur_idx = nodeToIndex(cur_node);
|
||||
uptr added_edges[40];
|
||||
uptr n_added_edges = g_.addEdges(dtls->getLocks(current_epoch_), cur_idx,
|
||||
added_edges, ARRAY_SIZE(added_edges));
|
||||
for (uptr i = 0; i < n_added_edges; i++) {
|
||||
if (n_edges_ < ARRAY_SIZE(edges_)) {
|
||||
Edge e = {(u16)added_edges[i], (u16)cur_idx,
|
||||
dtls->findLockContext(added_edges[i]), stk,
|
||||
unique_tid};
|
||||
edges_[n_edges_++] = e;
|
||||
}
|
||||
}
|
||||
return n_added_edges;
|
||||
}
|
||||
|
||||
bool findEdge(uptr from_node, uptr to_node, u32 *stk_from, u32 *stk_to,
|
||||
int *unique_tid) {
|
||||
uptr from_idx = nodeToIndex(from_node);
|
||||
uptr to_idx = nodeToIndex(to_node);
|
||||
for (uptr i = 0; i < n_edges_; i++) {
|
||||
if (edges_[i].from == from_idx && edges_[i].to == to_idx) {
|
||||
*stk_from = edges_[i].stk_from;
|
||||
*stk_to = edges_[i].stk_to;
|
||||
*unique_tid = edges_[i].unique_tid;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test-only function. Handles the before/after lock events,
|
||||
// returns true if there is a cycle.
|
||||
bool onLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
|
||||
ensureCurrentEpoch(dtls);
|
||||
bool is_reachable = !isHeld(dtls, cur_node) && onLockBefore(dtls, cur_node);
|
||||
addEdges(dtls, cur_node, stk, 0);
|
||||
onLockAfter(dtls, cur_node, stk);
|
||||
return is_reachable;
|
||||
}
|
||||
|
||||
// Handles the try_lock event, returns false.
|
||||
// When a try_lock event happens (i.e. a try_lock call succeeds) we need
|
||||
// to add this lock to the currently held locks, but we should not try to
|
||||
// change the lock graph or to detect a cycle. We may want to investigate
|
||||
// whether a more aggressive strategy is possible for try_lock.
|
||||
bool onTryLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, u32 stk = 0) {
|
||||
ensureCurrentEpoch(dtls);
|
||||
uptr cur_idx = nodeToIndex(cur_node);
|
||||
dtls->addLock(cur_idx, current_epoch_, stk);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true iff dtls is empty (no locks are currently held) and we can
|
||||
// add the node to the currently held locks w/o chanding the global state.
|
||||
// This operation is thread-safe as it only touches the dtls.
|
||||
bool onFirstLock(DeadlockDetectorTLS<BV> *dtls, uptr node, u32 stk = 0) {
|
||||
if (!dtls->empty()) return false;
|
||||
if (dtls->getEpoch() && dtls->getEpoch() == nodeToEpoch(node)) {
|
||||
dtls->addLock(nodeToIndexUnchecked(node), nodeToEpoch(node), stk);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finds a path between the lock 'cur_node' (currently not held in dtls)
|
||||
// and some currently held lock, returns the length of the path
|
||||
// or 0 on failure.
|
||||
uptr findPathToLock(DeadlockDetectorTLS<BV> *dtls, uptr cur_node, uptr *path,
|
||||
uptr path_size) {
|
||||
tmp_bv_.copyFrom(dtls->getLocks(current_epoch_));
|
||||
uptr idx = nodeToIndex(cur_node);
|
||||
CHECK(!tmp_bv_.getBit(idx));
|
||||
uptr res = g_.findShortestPath(idx, tmp_bv_, path, path_size);
|
||||
for (uptr i = 0; i < res; i++)
|
||||
path[i] = indexToNode(path[i]);
|
||||
if (res)
|
||||
CHECK_EQ(path[0], cur_node);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Handle the unlock event.
|
||||
// This operation is thread-safe as it only touches the dtls.
|
||||
void onUnlock(DeadlockDetectorTLS<BV> *dtls, uptr node) {
|
||||
if (dtls->getEpoch() == nodeToEpoch(node))
|
||||
dtls->removeLock(nodeToIndexUnchecked(node));
|
||||
}
|
||||
|
||||
// Tries to handle the lock event w/o writing to global state.
|
||||
// Returns true on success.
|
||||
// This operation is thread-safe as it only touches the dtls
|
||||
// (modulo racy nature of hasAllEdges).
|
||||
bool onLockFast(DeadlockDetectorTLS<BV> *dtls, uptr node, u32 stk = 0) {
|
||||
if (hasAllEdges(dtls, node)) {
|
||||
dtls->addLock(nodeToIndexUnchecked(node), nodeToEpoch(node), stk);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isHeld(DeadlockDetectorTLS<BV> *dtls, uptr node) const {
|
||||
return dtls->getLocks(current_epoch_).getBit(nodeToIndex(node));
|
||||
}
|
||||
|
||||
uptr testOnlyGetEpoch() const { return current_epoch_; }
|
||||
bool testOnlyHasEdge(uptr l1, uptr l2) {
|
||||
return g_.hasEdge(nodeToIndex(l1), nodeToIndex(l2));
|
||||
}
|
||||
// idx1 and idx2 are raw indices to g_, not lock IDs.
|
||||
bool testOnlyHasEdgeRaw(uptr idx1, uptr idx2) {
|
||||
return g_.hasEdge(idx1, idx2);
|
||||
}
|
||||
|
||||
void Print() {
|
||||
for (uptr from = 0; from < size(); from++)
|
||||
for (uptr to = 0; to < size(); to++)
|
||||
if (g_.hasEdge(from, to))
|
||||
Printf(" %zx => %zx\n", from, to);
|
||||
}
|
||||
|
||||
private:
|
||||
void check_idx(uptr idx) const { CHECK_LT(idx, size()); }
|
||||
|
||||
void check_node(uptr node) const {
|
||||
CHECK_GE(node, size());
|
||||
CHECK_EQ(current_epoch_, nodeToEpoch(node));
|
||||
}
|
||||
|
||||
uptr indexToNode(uptr idx) const {
|
||||
check_idx(idx);
|
||||
return idx + current_epoch_;
|
||||
}
|
||||
|
||||
uptr nodeToIndexUnchecked(uptr node) const { return node % size(); }
|
||||
|
||||
uptr nodeToIndex(uptr node) const {
|
||||
check_node(node);
|
||||
return nodeToIndexUnchecked(node);
|
||||
}
|
||||
|
||||
uptr nodeToEpoch(uptr node) const { return node / size() * size(); }
|
||||
|
||||
uptr getAvailableNode(uptr data) {
|
||||
uptr idx = available_nodes_.getAndClearFirstOne();
|
||||
data_[idx] = data;
|
||||
return indexToNode(idx);
|
||||
}
|
||||
|
||||
struct Edge {
|
||||
u16 from;
|
||||
u16 to;
|
||||
u32 stk_from;
|
||||
u32 stk_to;
|
||||
int unique_tid;
|
||||
};
|
||||
|
||||
uptr current_epoch_;
|
||||
BV available_nodes_;
|
||||
BV recycled_nodes_;
|
||||
BV tmp_bv_;
|
||||
BVGraph<BV> g_;
|
||||
uptr data_[BV::kSize];
|
||||
Edge edges_[BV::kSize * 32];
|
||||
uptr n_edges_;
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_DEADLOCK_DETECTOR_H
|
||||
194
lib/tsan/sanitizer_common/sanitizer_deadlock_detector1.cpp
Normal file
194
lib/tsan/sanitizer_common/sanitizer_deadlock_detector1.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
//===-- sanitizer_deadlock_detector1.cpp ----------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Deadlock detector implementation based on NxN adjacency bit matrix.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_deadlock_detector_interface.h"
|
||||
#include "sanitizer_deadlock_detector.h"
|
||||
#include "sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
|
||||
#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
typedef TwoLevelBitVector<> DDBV; // DeadlockDetector's bit vector.
|
||||
|
||||
struct DDPhysicalThread {
|
||||
};
|
||||
|
||||
struct DDLogicalThread {
|
||||
u64 ctx;
|
||||
DeadlockDetectorTLS<DDBV> dd;
|
||||
DDReport rep;
|
||||
bool report_pending;
|
||||
};
|
||||
|
||||
struct DD : public DDetector {
|
||||
SpinMutex mtx;
|
||||
DeadlockDetector<DDBV> dd;
|
||||
DDFlags flags;
|
||||
|
||||
explicit DD(const DDFlags *flags);
|
||||
|
||||
DDPhysicalThread *CreatePhysicalThread() override;
|
||||
void DestroyPhysicalThread(DDPhysicalThread *pt) override;
|
||||
|
||||
DDLogicalThread *CreateLogicalThread(u64 ctx) override;
|
||||
void DestroyLogicalThread(DDLogicalThread *lt) override;
|
||||
|
||||
void MutexInit(DDCallback *cb, DDMutex *m) override;
|
||||
void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override;
|
||||
void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
|
||||
bool trylock) override;
|
||||
void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override;
|
||||
void MutexDestroy(DDCallback *cb, DDMutex *m) override;
|
||||
|
||||
DDReport *GetReport(DDCallback *cb) override;
|
||||
|
||||
void MutexEnsureID(DDLogicalThread *lt, DDMutex *m);
|
||||
void ReportDeadlock(DDCallback *cb, DDMutex *m);
|
||||
};
|
||||
|
||||
DDetector *DDetector::Create(const DDFlags *flags) {
|
||||
(void)flags;
|
||||
void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
|
||||
return new(mem) DD(flags);
|
||||
}
|
||||
|
||||
DD::DD(const DDFlags *flags)
|
||||
: flags(*flags) {
|
||||
dd.clear();
|
||||
}
|
||||
|
||||
DDPhysicalThread* DD::CreatePhysicalThread() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
|
||||
}
|
||||
|
||||
DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
|
||||
DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(sizeof(*lt));
|
||||
lt->ctx = ctx;
|
||||
lt->dd.clear();
|
||||
lt->report_pending = false;
|
||||
return lt;
|
||||
}
|
||||
|
||||
void DD::DestroyLogicalThread(DDLogicalThread *lt) {
|
||||
lt->~DDLogicalThread();
|
||||
InternalFree(lt);
|
||||
}
|
||||
|
||||
void DD::MutexInit(DDCallback *cb, DDMutex *m) {
|
||||
m->id = 0;
|
||||
m->stk = cb->Unwind();
|
||||
}
|
||||
|
||||
void DD::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) {
|
||||
if (!dd.nodeBelongsToCurrentEpoch(m->id))
|
||||
m->id = dd.newNode(reinterpret_cast<uptr>(m));
|
||||
dd.ensureCurrentEpoch(<->dd);
|
||||
}
|
||||
|
||||
void DD::MutexBeforeLock(DDCallback *cb,
|
||||
DDMutex *m, bool wlock) {
|
||||
DDLogicalThread *lt = cb->lt;
|
||||
if (lt->dd.empty()) return; // This will be the first lock held by lt.
|
||||
if (dd.hasAllEdges(<->dd, m->id)) return; // We already have all edges.
|
||||
SpinMutexLock lk(&mtx);
|
||||
MutexEnsureID(lt, m);
|
||||
if (dd.isHeld(<->dd, m->id))
|
||||
return; // FIXME: allow this only for recursive locks.
|
||||
if (dd.onLockBefore(<->dd, m->id)) {
|
||||
// Actually add this edge now so that we have all the stack traces.
|
||||
dd.addEdges(<->dd, m->id, cb->Unwind(), cb->UniqueTid());
|
||||
ReportDeadlock(cb, m);
|
||||
}
|
||||
}
|
||||
|
||||
void DD::ReportDeadlock(DDCallback *cb, DDMutex *m) {
|
||||
DDLogicalThread *lt = cb->lt;
|
||||
uptr path[20];
|
||||
uptr len = dd.findPathToLock(<->dd, m->id, path, ARRAY_SIZE(path));
|
||||
if (len == 0U) {
|
||||
// A cycle of 20+ locks? Well, that's a bit odd...
|
||||
Printf("WARNING: too long mutex cycle found\n");
|
||||
return;
|
||||
}
|
||||
CHECK_EQ(m->id, path[0]);
|
||||
lt->report_pending = true;
|
||||
len = Min<uptr>(len, DDReport::kMaxLoopSize);
|
||||
DDReport *rep = <->rep;
|
||||
rep->n = len;
|
||||
for (uptr i = 0; i < len; i++) {
|
||||
uptr from = path[i];
|
||||
uptr to = path[(i + 1) % len];
|
||||
DDMutex *m0 = (DDMutex*)dd.getData(from);
|
||||
DDMutex *m1 = (DDMutex*)dd.getData(to);
|
||||
|
||||
u32 stk_from = -1U, stk_to = -1U;
|
||||
int unique_tid = 0;
|
||||
dd.findEdge(from, to, &stk_from, &stk_to, &unique_tid);
|
||||
// Printf("Edge: %zd=>%zd: %u/%u T%d\n", from, to, stk_from, stk_to,
|
||||
// unique_tid);
|
||||
rep->loop[i].thr_ctx = unique_tid;
|
||||
rep->loop[i].mtx_ctx0 = m0->ctx;
|
||||
rep->loop[i].mtx_ctx1 = m1->ctx;
|
||||
rep->loop[i].stk[0] = stk_to;
|
||||
rep->loop[i].stk[1] = stk_from;
|
||||
}
|
||||
}
|
||||
|
||||
void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock) {
|
||||
DDLogicalThread *lt = cb->lt;
|
||||
u32 stk = 0;
|
||||
if (flags.second_deadlock_stack)
|
||||
stk = cb->Unwind();
|
||||
// Printf("T%p MutexLock: %zx stk %u\n", lt, m->id, stk);
|
||||
if (dd.onFirstLock(<->dd, m->id, stk))
|
||||
return;
|
||||
if (dd.onLockFast(<->dd, m->id, stk))
|
||||
return;
|
||||
|
||||
SpinMutexLock lk(&mtx);
|
||||
MutexEnsureID(lt, m);
|
||||
if (wlock) // Only a recursive rlock may be held.
|
||||
CHECK(!dd.isHeld(<->dd, m->id));
|
||||
if (!trylock)
|
||||
dd.addEdges(<->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid());
|
||||
dd.onLockAfter(<->dd, m->id, stk);
|
||||
}
|
||||
|
||||
void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
|
||||
// Printf("T%p MutexUnLock: %zx\n", cb->lt, m->id);
|
||||
dd.onUnlock(&cb->lt->dd, m->id);
|
||||
}
|
||||
|
||||
void DD::MutexDestroy(DDCallback *cb,
|
||||
DDMutex *m) {
|
||||
if (!m->id) return;
|
||||
SpinMutexLock lk(&mtx);
|
||||
if (dd.nodeBelongsToCurrentEpoch(m->id))
|
||||
dd.removeNode(m->id);
|
||||
m->id = 0;
|
||||
}
|
||||
|
||||
DDReport *DD::GetReport(DDCallback *cb) {
|
||||
if (!cb->lt->report_pending)
|
||||
return nullptr;
|
||||
cb->lt->report_pending = false;
|
||||
return &cb->lt->rep;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
|
||||
423
lib/tsan/sanitizer_common/sanitizer_deadlock_detector2.cpp
Normal file
423
lib/tsan/sanitizer_common/sanitizer_deadlock_detector2.cpp
Normal file
@@ -0,0 +1,423 @@
|
||||
//===-- sanitizer_deadlock_detector2.cpp ----------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Deadlock detector implementation based on adjacency lists.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_deadlock_detector_interface.h"
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
|
||||
#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
const int kMaxNesting = 64;
|
||||
const u32 kNoId = -1;
|
||||
const u32 kEndId = -2;
|
||||
const int kMaxLink = 8;
|
||||
const int kL1Size = 1024;
|
||||
const int kL2Size = 1024;
|
||||
const int kMaxMutex = kL1Size * kL2Size;
|
||||
|
||||
struct Id {
|
||||
u32 id;
|
||||
u32 seq;
|
||||
|
||||
explicit Id(u32 id = 0, u32 seq = 0)
|
||||
: id(id)
|
||||
, seq(seq) {
|
||||
}
|
||||
};
|
||||
|
||||
struct Link {
|
||||
u32 id;
|
||||
u32 seq;
|
||||
u32 tid;
|
||||
u32 stk0;
|
||||
u32 stk1;
|
||||
|
||||
explicit Link(u32 id = 0, u32 seq = 0, u32 tid = 0, u32 s0 = 0, u32 s1 = 0)
|
||||
: id(id)
|
||||
, seq(seq)
|
||||
, tid(tid)
|
||||
, stk0(s0)
|
||||
, stk1(s1) {
|
||||
}
|
||||
};
|
||||
|
||||
struct DDPhysicalThread {
|
||||
DDReport rep;
|
||||
bool report_pending;
|
||||
bool visited[kMaxMutex];
|
||||
Link pending[kMaxMutex];
|
||||
Link path[kMaxMutex];
|
||||
};
|
||||
|
||||
struct ThreadMutex {
|
||||
u32 id;
|
||||
u32 stk;
|
||||
};
|
||||
|
||||
struct DDLogicalThread {
|
||||
u64 ctx;
|
||||
ThreadMutex locked[kMaxNesting];
|
||||
int nlocked;
|
||||
};
|
||||
|
||||
struct Mutex {
|
||||
StaticSpinMutex mtx;
|
||||
u32 seq;
|
||||
int nlink;
|
||||
Link link[kMaxLink];
|
||||
};
|
||||
|
||||
struct DD : public DDetector {
|
||||
explicit DD(const DDFlags *flags);
|
||||
|
||||
DDPhysicalThread* CreatePhysicalThread();
|
||||
void DestroyPhysicalThread(DDPhysicalThread *pt);
|
||||
|
||||
DDLogicalThread* CreateLogicalThread(u64 ctx);
|
||||
void DestroyLogicalThread(DDLogicalThread *lt);
|
||||
|
||||
void MutexInit(DDCallback *cb, DDMutex *m);
|
||||
void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock);
|
||||
void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
|
||||
bool trylock);
|
||||
void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock);
|
||||
void MutexDestroy(DDCallback *cb, DDMutex *m);
|
||||
|
||||
DDReport *GetReport(DDCallback *cb);
|
||||
|
||||
void CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt, DDMutex *mtx);
|
||||
void Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath);
|
||||
u32 allocateId(DDCallback *cb);
|
||||
Mutex *getMutex(u32 id);
|
||||
u32 getMutexId(Mutex *m);
|
||||
|
||||
DDFlags flags;
|
||||
|
||||
Mutex* mutex[kL1Size];
|
||||
|
||||
SpinMutex mtx;
|
||||
InternalMmapVector<u32> free_id;
|
||||
int id_gen = 0;
|
||||
};
|
||||
|
||||
DDetector *DDetector::Create(const DDFlags *flags) {
|
||||
(void)flags;
|
||||
void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
|
||||
return new(mem) DD(flags);
|
||||
}
|
||||
|
||||
DD::DD(const DDFlags *flags) : flags(*flags) { free_id.reserve(1024); }
|
||||
|
||||
DDPhysicalThread* DD::CreatePhysicalThread() {
|
||||
DDPhysicalThread *pt = (DDPhysicalThread*)MmapOrDie(sizeof(DDPhysicalThread),
|
||||
"deadlock detector (physical thread)");
|
||||
return pt;
|
||||
}
|
||||
|
||||
void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
|
||||
pt->~DDPhysicalThread();
|
||||
UnmapOrDie(pt, sizeof(DDPhysicalThread));
|
||||
}
|
||||
|
||||
DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
|
||||
DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(
|
||||
sizeof(DDLogicalThread));
|
||||
lt->ctx = ctx;
|
||||
lt->nlocked = 0;
|
||||
return lt;
|
||||
}
|
||||
|
||||
void DD::DestroyLogicalThread(DDLogicalThread *lt) {
|
||||
lt->~DDLogicalThread();
|
||||
InternalFree(lt);
|
||||
}
|
||||
|
||||
void DD::MutexInit(DDCallback *cb, DDMutex *m) {
|
||||
VPrintf(2, "#%llu: DD::MutexInit(%p)\n", cb->lt->ctx, m);
|
||||
m->id = kNoId;
|
||||
m->recursion = 0;
|
||||
atomic_store(&m->owner, 0, memory_order_relaxed);
|
||||
}
|
||||
|
||||
Mutex *DD::getMutex(u32 id) {
|
||||
return &mutex[id / kL2Size][id % kL2Size];
|
||||
}
|
||||
|
||||
u32 DD::getMutexId(Mutex *m) {
|
||||
for (int i = 0; i < kL1Size; i++) {
|
||||
Mutex *tab = mutex[i];
|
||||
if (tab == 0)
|
||||
break;
|
||||
if (m >= tab && m < tab + kL2Size)
|
||||
return i * kL2Size + (m - tab);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
u32 DD::allocateId(DDCallback *cb) {
|
||||
u32 id = -1;
|
||||
SpinMutexLock l(&mtx);
|
||||
if (free_id.size() > 0) {
|
||||
id = free_id.back();
|
||||
free_id.pop_back();
|
||||
} else {
|
||||
CHECK_LT(id_gen, kMaxMutex);
|
||||
if ((id_gen % kL2Size) == 0) {
|
||||
mutex[id_gen / kL2Size] = (Mutex*)MmapOrDie(kL2Size * sizeof(Mutex),
|
||||
"deadlock detector (mutex table)");
|
||||
}
|
||||
id = id_gen++;
|
||||
}
|
||||
CHECK_LE(id, kMaxMutex);
|
||||
VPrintf(3, "#%llu: DD::allocateId assign id %d\n", cb->lt->ctx, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
void DD::MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {
|
||||
VPrintf(2, "#%llu: DD::MutexBeforeLock(%p, wlock=%d) nlocked=%d\n",
|
||||
cb->lt->ctx, m, wlock, cb->lt->nlocked);
|
||||
DDPhysicalThread *pt = cb->pt;
|
||||
DDLogicalThread *lt = cb->lt;
|
||||
|
||||
uptr owner = atomic_load(&m->owner, memory_order_relaxed);
|
||||
if (owner == (uptr)cb->lt) {
|
||||
VPrintf(3, "#%llu: DD::MutexBeforeLock recursive\n",
|
||||
cb->lt->ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK_LE(lt->nlocked, kMaxNesting);
|
||||
|
||||
// FIXME(dvyukov): don't allocate id if lt->nlocked == 0?
|
||||
if (m->id == kNoId)
|
||||
m->id = allocateId(cb);
|
||||
|
||||
ThreadMutex *tm = <->locked[lt->nlocked++];
|
||||
tm->id = m->id;
|
||||
if (flags.second_deadlock_stack)
|
||||
tm->stk = cb->Unwind();
|
||||
if (lt->nlocked == 1) {
|
||||
VPrintf(3, "#%llu: DD::MutexBeforeLock first mutex\n",
|
||||
cb->lt->ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
bool added = false;
|
||||
Mutex *mtx = getMutex(m->id);
|
||||
for (int i = 0; i < lt->nlocked - 1; i++) {
|
||||
u32 id1 = lt->locked[i].id;
|
||||
u32 stk1 = lt->locked[i].stk;
|
||||
Mutex *mtx1 = getMutex(id1);
|
||||
SpinMutexLock l(&mtx1->mtx);
|
||||
if (mtx1->nlink == kMaxLink) {
|
||||
// FIXME(dvyukov): check stale links
|
||||
continue;
|
||||
}
|
||||
int li = 0;
|
||||
for (; li < mtx1->nlink; li++) {
|
||||
Link *link = &mtx1->link[li];
|
||||
if (link->id == m->id) {
|
||||
if (link->seq != mtx->seq) {
|
||||
link->seq = mtx->seq;
|
||||
link->tid = lt->ctx;
|
||||
link->stk0 = stk1;
|
||||
link->stk1 = cb->Unwind();
|
||||
added = true;
|
||||
VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
|
||||
cb->lt->ctx, getMutexId(mtx1), m->id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (li == mtx1->nlink) {
|
||||
// FIXME(dvyukov): check stale links
|
||||
Link *link = &mtx1->link[mtx1->nlink++];
|
||||
link->id = m->id;
|
||||
link->seq = mtx->seq;
|
||||
link->tid = lt->ctx;
|
||||
link->stk0 = stk1;
|
||||
link->stk1 = cb->Unwind();
|
||||
added = true;
|
||||
VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
|
||||
cb->lt->ctx, getMutexId(mtx1), m->id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!added || mtx->nlink == 0) {
|
||||
VPrintf(3, "#%llu: DD::MutexBeforeLock don't check\n",
|
||||
cb->lt->ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
CycleCheck(pt, lt, m);
|
||||
}
|
||||
|
||||
void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
|
||||
bool trylock) {
|
||||
VPrintf(2, "#%llu: DD::MutexAfterLock(%p, wlock=%d, try=%d) nlocked=%d\n",
|
||||
cb->lt->ctx, m, wlock, trylock, cb->lt->nlocked);
|
||||
DDLogicalThread *lt = cb->lt;
|
||||
|
||||
uptr owner = atomic_load(&m->owner, memory_order_relaxed);
|
||||
if (owner == (uptr)cb->lt) {
|
||||
VPrintf(3, "#%llu: DD::MutexAfterLock recursive\n", cb->lt->ctx);
|
||||
CHECK(wlock);
|
||||
m->recursion++;
|
||||
return;
|
||||
}
|
||||
CHECK_EQ(owner, 0);
|
||||
if (wlock) {
|
||||
VPrintf(3, "#%llu: DD::MutexAfterLock set owner\n", cb->lt->ctx);
|
||||
CHECK_EQ(m->recursion, 0);
|
||||
m->recursion = 1;
|
||||
atomic_store(&m->owner, (uptr)cb->lt, memory_order_relaxed);
|
||||
}
|
||||
|
||||
if (!trylock)
|
||||
return;
|
||||
|
||||
CHECK_LE(lt->nlocked, kMaxNesting);
|
||||
if (m->id == kNoId)
|
||||
m->id = allocateId(cb);
|
||||
ThreadMutex *tm = <->locked[lt->nlocked++];
|
||||
tm->id = m->id;
|
||||
if (flags.second_deadlock_stack)
|
||||
tm->stk = cb->Unwind();
|
||||
}
|
||||
|
||||
void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
|
||||
VPrintf(2, "#%llu: DD::MutexBeforeUnlock(%p, wlock=%d) nlocked=%d\n",
|
||||
cb->lt->ctx, m, wlock, cb->lt->nlocked);
|
||||
DDLogicalThread *lt = cb->lt;
|
||||
|
||||
uptr owner = atomic_load(&m->owner, memory_order_relaxed);
|
||||
if (owner == (uptr)cb->lt) {
|
||||
VPrintf(3, "#%llu: DD::MutexBeforeUnlock recursive\n", cb->lt->ctx);
|
||||
if (--m->recursion > 0)
|
||||
return;
|
||||
VPrintf(3, "#%llu: DD::MutexBeforeUnlock reset owner\n", cb->lt->ctx);
|
||||
atomic_store(&m->owner, 0, memory_order_relaxed);
|
||||
}
|
||||
CHECK_NE(m->id, kNoId);
|
||||
int last = lt->nlocked - 1;
|
||||
for (int i = last; i >= 0; i--) {
|
||||
if (cb->lt->locked[i].id == m->id) {
|
||||
lt->locked[i] = lt->locked[last];
|
||||
lt->nlocked--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DD::MutexDestroy(DDCallback *cb, DDMutex *m) {
|
||||
VPrintf(2, "#%llu: DD::MutexDestroy(%p)\n",
|
||||
cb->lt->ctx, m);
|
||||
DDLogicalThread *lt = cb->lt;
|
||||
|
||||
if (m->id == kNoId)
|
||||
return;
|
||||
|
||||
// Remove the mutex from lt->locked if there.
|
||||
int last = lt->nlocked - 1;
|
||||
for (int i = last; i >= 0; i--) {
|
||||
if (lt->locked[i].id == m->id) {
|
||||
lt->locked[i] = lt->locked[last];
|
||||
lt->nlocked--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear and invalidate the mutex descriptor.
|
||||
{
|
||||
Mutex *mtx = getMutex(m->id);
|
||||
SpinMutexLock l(&mtx->mtx);
|
||||
mtx->seq++;
|
||||
mtx->nlink = 0;
|
||||
}
|
||||
|
||||
// Return id to cache.
|
||||
{
|
||||
SpinMutexLock l(&mtx);
|
||||
free_id.push_back(m->id);
|
||||
}
|
||||
}
|
||||
|
||||
void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt,
|
||||
DDMutex *m) {
|
||||
internal_memset(pt->visited, 0, sizeof(pt->visited));
|
||||
int npath = 0;
|
||||
int npending = 0;
|
||||
{
|
||||
Mutex *mtx = getMutex(m->id);
|
||||
SpinMutexLock l(&mtx->mtx);
|
||||
for (int li = 0; li < mtx->nlink; li++)
|
||||
pt->pending[npending++] = mtx->link[li];
|
||||
}
|
||||
while (npending > 0) {
|
||||
Link link = pt->pending[--npending];
|
||||
if (link.id == kEndId) {
|
||||
npath--;
|
||||
continue;
|
||||
}
|
||||
if (pt->visited[link.id])
|
||||
continue;
|
||||
Mutex *mtx1 = getMutex(link.id);
|
||||
SpinMutexLock l(&mtx1->mtx);
|
||||
if (mtx1->seq != link.seq)
|
||||
continue;
|
||||
pt->visited[link.id] = true;
|
||||
if (mtx1->nlink == 0)
|
||||
continue;
|
||||
pt->path[npath++] = link;
|
||||
pt->pending[npending++] = Link(kEndId);
|
||||
if (link.id == m->id)
|
||||
return Report(pt, lt, npath); // Bingo!
|
||||
for (int li = 0; li < mtx1->nlink; li++) {
|
||||
Link *link1 = &mtx1->link[li];
|
||||
// Mutex *mtx2 = getMutex(link->id);
|
||||
// FIXME(dvyukov): fast seq check
|
||||
// FIXME(dvyukov): fast nlink != 0 check
|
||||
// FIXME(dvyukov): fast pending check?
|
||||
// FIXME(dvyukov): npending can be larger than kMaxMutex
|
||||
pt->pending[npending++] = *link1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DD::Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath) {
|
||||
DDReport *rep = &pt->rep;
|
||||
rep->n = npath;
|
||||
for (int i = 0; i < npath; i++) {
|
||||
Link *link = &pt->path[i];
|
||||
Link *link0 = &pt->path[i ? i - 1 : npath - 1];
|
||||
rep->loop[i].thr_ctx = link->tid;
|
||||
rep->loop[i].mtx_ctx0 = link0->id;
|
||||
rep->loop[i].mtx_ctx1 = link->id;
|
||||
rep->loop[i].stk[0] = flags.second_deadlock_stack ? link->stk0 : 0;
|
||||
rep->loop[i].stk[1] = link->stk1;
|
||||
}
|
||||
pt->report_pending = true;
|
||||
}
|
||||
|
||||
DDReport *DD::GetReport(DDCallback *cb) {
|
||||
if (!cb->pt->report_pending)
|
||||
return 0;
|
||||
cb->pt->report_pending = false;
|
||||
return &cb->pt->rep;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
|
||||
@@ -0,0 +1,92 @@
|
||||
//===-- sanitizer_deadlock_detector_interface.h -----------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer runtime.
|
||||
// Abstract deadlock detector interface.
|
||||
// FIXME: this is work in progress, nothing really works yet.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H
|
||||
#define SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H
|
||||
|
||||
#ifndef SANITIZER_DEADLOCK_DETECTOR_VERSION
|
||||
# define SANITIZER_DEADLOCK_DETECTOR_VERSION 1
|
||||
#endif
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_atomic.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// dd - deadlock detector.
|
||||
// lt - logical (user) thread.
|
||||
// pt - physical (OS) thread.
|
||||
|
||||
struct DDPhysicalThread;
|
||||
struct DDLogicalThread;
|
||||
|
||||
struct DDMutex {
|
||||
#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
|
||||
uptr id;
|
||||
u32 stk; // creation stack
|
||||
#elif SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
|
||||
u32 id;
|
||||
u32 recursion;
|
||||
atomic_uintptr_t owner;
|
||||
#else
|
||||
# error "BAD SANITIZER_DEADLOCK_DETECTOR_VERSION"
|
||||
#endif
|
||||
u64 ctx;
|
||||
};
|
||||
|
||||
struct DDFlags {
|
||||
bool second_deadlock_stack;
|
||||
};
|
||||
|
||||
struct DDReport {
|
||||
enum { kMaxLoopSize = 20 };
|
||||
int n; // number of entries in loop
|
||||
struct {
|
||||
u64 thr_ctx; // user thread context
|
||||
u64 mtx_ctx0; // user mutex context, start of the edge
|
||||
u64 mtx_ctx1; // user mutex context, end of the edge
|
||||
u32 stk[2]; // stack ids for the edge
|
||||
} loop[kMaxLoopSize];
|
||||
};
|
||||
|
||||
struct DDCallback {
|
||||
DDPhysicalThread *pt;
|
||||
DDLogicalThread *lt;
|
||||
|
||||
virtual u32 Unwind() { return 0; }
|
||||
virtual int UniqueTid() { return 0; }
|
||||
};
|
||||
|
||||
struct DDetector {
|
||||
static DDetector *Create(const DDFlags *flags);
|
||||
|
||||
virtual DDPhysicalThread* CreatePhysicalThread() { return nullptr; }
|
||||
virtual void DestroyPhysicalThread(DDPhysicalThread *pt) {}
|
||||
|
||||
virtual DDLogicalThread* CreateLogicalThread(u64 ctx) { return nullptr; }
|
||||
virtual void DestroyLogicalThread(DDLogicalThread *lt) {}
|
||||
|
||||
virtual void MutexInit(DDCallback *cb, DDMutex *m) {}
|
||||
virtual void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {}
|
||||
virtual void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
|
||||
bool trylock) {}
|
||||
virtual void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {}
|
||||
virtual void MutexDestroy(DDCallback *cb, DDMutex *m) {}
|
||||
|
||||
virtual DDReport *GetReport(DDCallback *cb) { return nullptr; }
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_DEADLOCK_DETECTOR_INTERFACE_H
|
||||
34
lib/tsan/sanitizer_common/sanitizer_errno.cpp
Normal file
34
lib/tsan/sanitizer_common/sanitizer_errno.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
//===-- sanitizer_errno.cpp -------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between sanitizers run-time libraries.
|
||||
//
|
||||
// Defines errno to avoid including errno.h and its dependencies into other
|
||||
// files (e.g. interceptors are not supposed to include any system headers).
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_errno_codes.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
COMPILER_CHECK(errno_ENOMEM == ENOMEM);
|
||||
COMPILER_CHECK(errno_EBUSY == EBUSY);
|
||||
COMPILER_CHECK(errno_EINVAL == EINVAL);
|
||||
|
||||
// EOWNERDEAD is not present in some older platforms.
|
||||
#if defined(EOWNERDEAD)
|
||||
extern const int errno_EOWNERDEAD = EOWNERDEAD;
|
||||
#else
|
||||
extern const int errno_EOWNERDEAD = -1;
|
||||
#endif
|
||||
|
||||
} // namespace __sanitizer
|
||||
39
lib/tsan/sanitizer_common/sanitizer_errno.h
Normal file
39
lib/tsan/sanitizer_common/sanitizer_errno.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//===-- sanitizer_errno.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between sanitizers run-time libraries.
|
||||
//
|
||||
// Defines errno to avoid including errno.h and its dependencies into sensitive
|
||||
// files (e.g. interceptors are not supposed to include any system headers).
|
||||
// It's ok to use errno.h directly when your file already depend on other system
|
||||
// includes though.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ERRNO_H
|
||||
#define SANITIZER_ERRNO_H
|
||||
|
||||
#include "sanitizer_errno_codes.h"
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_FREEBSD || SANITIZER_MAC
|
||||
# define __errno_location __error
|
||||
#elif SANITIZER_ANDROID || SANITIZER_NETBSD || SANITIZER_OPENBSD || \
|
||||
SANITIZER_RTEMS
|
||||
# define __errno_location __errno
|
||||
#elif SANITIZER_SOLARIS
|
||||
# define __errno_location ___errno
|
||||
#elif SANITIZER_WINDOWS
|
||||
# define __errno_location _errno
|
||||
#endif
|
||||
|
||||
extern "C" int *__errno_location();
|
||||
|
||||
#define errno (*__errno_location())
|
||||
|
||||
#endif // SANITIZER_ERRNO_H
|
||||
33
lib/tsan/sanitizer_common/sanitizer_errno_codes.h
Normal file
33
lib/tsan/sanitizer_common/sanitizer_errno_codes.h
Normal file
@@ -0,0 +1,33 @@
|
||||
//===-- sanitizer_errno_codes.h ---------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between sanitizers run-time libraries.
|
||||
//
|
||||
// Defines errno codes to avoid including errno.h and its dependencies into
|
||||
// sensitive files (e.g. interceptors are not supposed to include any system
|
||||
// headers).
|
||||
// It's ok to use errno.h directly when your file already depend on other system
|
||||
// includes though.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_ERRNO_CODES_H
|
||||
#define SANITIZER_ERRNO_CODES_H
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
#define errno_ENOMEM 12
|
||||
#define errno_EBUSY 16
|
||||
#define errno_EINVAL 22
|
||||
|
||||
// Those might not present or their value differ on different platforms.
|
||||
extern const int errno_EOWNERDEAD;
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_ERRNO_CODES_H
|
||||
215
lib/tsan/sanitizer_common/sanitizer_file.cpp
Normal file
215
lib/tsan/sanitizer_common/sanitizer_file.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
//===-- sanitizer_file.cpp -----------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries. It defines filesystem-related interfaces. This
|
||||
// is separate from sanitizer_common.cpp so that it's simpler to disable
|
||||
// all the filesystem support code for a port that doesn't use it.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if !SANITIZER_FUCHSIA
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_file.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
void CatastrophicErrorWrite(const char *buffer, uptr length) {
|
||||
WriteToFile(kStderrFd, buffer, length);
|
||||
}
|
||||
|
||||
StaticSpinMutex report_file_mu;
|
||||
ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
|
||||
|
||||
void RawWrite(const char *buffer) {
|
||||
report_file.Write(buffer, internal_strlen(buffer));
|
||||
}
|
||||
|
||||
void ReportFile::ReopenIfNecessary() {
|
||||
mu->CheckLocked();
|
||||
if (fd == kStdoutFd || fd == kStderrFd) return;
|
||||
|
||||
uptr pid = internal_getpid();
|
||||
// If in tracer, use the parent's file.
|
||||
if (pid == stoptheworld_tracer_pid)
|
||||
pid = stoptheworld_tracer_ppid;
|
||||
if (fd != kInvalidFd) {
|
||||
// If the report file is already opened by the current process,
|
||||
// do nothing. Otherwise the report file was opened by the parent
|
||||
// process, close it now.
|
||||
if (fd_pid == pid)
|
||||
return;
|
||||
else
|
||||
CloseFile(fd);
|
||||
}
|
||||
|
||||
const char *exe_name = GetProcessName();
|
||||
if (common_flags()->log_exe_name && exe_name) {
|
||||
internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
|
||||
exe_name, pid);
|
||||
} else {
|
||||
internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
|
||||
}
|
||||
fd = OpenFile(full_path, WrOnly);
|
||||
if (fd == kInvalidFd) {
|
||||
const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
|
||||
WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
|
||||
WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
|
||||
Die();
|
||||
}
|
||||
fd_pid = pid;
|
||||
}
|
||||
|
||||
void ReportFile::SetReportPath(const char *path) {
|
||||
if (!path)
|
||||
return;
|
||||
uptr len = internal_strlen(path);
|
||||
if (len > sizeof(path_prefix) - 100) {
|
||||
Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
|
||||
path[0], path[1], path[2], path[3],
|
||||
path[4], path[5], path[6], path[7]);
|
||||
Die();
|
||||
}
|
||||
|
||||
SpinMutexLock l(mu);
|
||||
if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
|
||||
CloseFile(fd);
|
||||
fd = kInvalidFd;
|
||||
if (internal_strcmp(path, "stdout") == 0) {
|
||||
fd = kStdoutFd;
|
||||
} else if (internal_strcmp(path, "stderr") == 0) {
|
||||
fd = kStderrFd;
|
||||
} else {
|
||||
internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
|
||||
}
|
||||
}
|
||||
|
||||
bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
|
||||
uptr *read_len, uptr max_len, error_t *errno_p) {
|
||||
*buff = nullptr;
|
||||
*buff_size = 0;
|
||||
*read_len = 0;
|
||||
if (!max_len)
|
||||
return true;
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
uptr kMinFileLen = Min(PageSize, max_len);
|
||||
|
||||
// The files we usually open are not seekable, so try different buffer sizes.
|
||||
for (uptr size = kMinFileLen;; size = Min(size * 2, max_len)) {
|
||||
UnmapOrDie(*buff, *buff_size);
|
||||
*buff = (char*)MmapOrDie(size, __func__);
|
||||
*buff_size = size;
|
||||
fd_t fd = OpenFile(file_name, RdOnly, errno_p);
|
||||
if (fd == kInvalidFd) {
|
||||
UnmapOrDie(*buff, *buff_size);
|
||||
return false;
|
||||
}
|
||||
*read_len = 0;
|
||||
// Read up to one page at a time.
|
||||
bool reached_eof = false;
|
||||
while (*read_len < size) {
|
||||
uptr just_read;
|
||||
if (!ReadFromFile(fd, *buff + *read_len, size - *read_len, &just_read,
|
||||
errno_p)) {
|
||||
UnmapOrDie(*buff, *buff_size);
|
||||
CloseFile(fd);
|
||||
return false;
|
||||
}
|
||||
*read_len += just_read;
|
||||
if (just_read == 0 || *read_len == max_len) {
|
||||
reached_eof = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseFile(fd);
|
||||
if (reached_eof) // We've read the whole file.
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadFileToVector(const char *file_name,
|
||||
InternalMmapVectorNoCtor<char> *buff, uptr max_len,
|
||||
error_t *errno_p) {
|
||||
buff->clear();
|
||||
if (!max_len)
|
||||
return true;
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
fd_t fd = OpenFile(file_name, RdOnly, errno_p);
|
||||
if (fd == kInvalidFd)
|
||||
return false;
|
||||
uptr read_len = 0;
|
||||
while (read_len < max_len) {
|
||||
if (read_len >= buff->size())
|
||||
buff->resize(Min(Max(PageSize, read_len * 2), max_len));
|
||||
CHECK_LT(read_len, buff->size());
|
||||
CHECK_LE(buff->size(), max_len);
|
||||
uptr just_read;
|
||||
if (!ReadFromFile(fd, buff->data() + read_len, buff->size() - read_len,
|
||||
&just_read, errno_p)) {
|
||||
CloseFile(fd);
|
||||
return false;
|
||||
}
|
||||
read_len += just_read;
|
||||
if (!just_read)
|
||||
break;
|
||||
}
|
||||
CloseFile(fd);
|
||||
buff->resize(read_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
|
||||
|
||||
char *FindPathToBinary(const char *name) {
|
||||
if (FileExists(name)) {
|
||||
return internal_strdup(name);
|
||||
}
|
||||
|
||||
const char *path = GetEnv("PATH");
|
||||
if (!path)
|
||||
return nullptr;
|
||||
uptr name_len = internal_strlen(name);
|
||||
InternalMmapVector<char> buffer(kMaxPathLength);
|
||||
const char *beg = path;
|
||||
while (true) {
|
||||
const char *end = internal_strchrnul(beg, kPathSeparator);
|
||||
uptr prefix_len = end - beg;
|
||||
if (prefix_len + name_len + 2 <= kMaxPathLength) {
|
||||
internal_memcpy(buffer.data(), beg, prefix_len);
|
||||
buffer[prefix_len] = '/';
|
||||
internal_memcpy(&buffer[prefix_len + 1], name, name_len);
|
||||
buffer[prefix_len + 1 + name_len] = '\0';
|
||||
if (FileExists(buffer.data()))
|
||||
return internal_strdup(buffer.data());
|
||||
}
|
||||
if (*end == '\0') break;
|
||||
beg = end + 1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
extern "C" {
|
||||
void __sanitizer_set_report_path(const char *path) {
|
||||
report_file.SetReportPath(path);
|
||||
}
|
||||
|
||||
void __sanitizer_set_report_fd(void *fd) {
|
||||
report_file.fd = (fd_t)reinterpret_cast<uptr>(fd);
|
||||
report_file.fd_pid = internal_getpid();
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
#endif // !SANITIZER_FUCHSIA
|
||||
106
lib/tsan/sanitizer_common/sanitizer_file.h
Normal file
106
lib/tsan/sanitizer_common/sanitizer_file.h
Normal file
@@ -0,0 +1,106 @@
|
||||
//===-- sanitizer_file.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between run-time libraries of sanitizers.
|
||||
// It declares filesystem-related interfaces. This is separate from
|
||||
// sanitizer_common.h so that it's simpler to disable all the filesystem
|
||||
// support code for a port that doesn't use it.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_FILE_H
|
||||
#define SANITIZER_FILE_H
|
||||
|
||||
#include "sanitizer_interface_internal.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
struct ReportFile {
|
||||
void Write(const char *buffer, uptr length);
|
||||
bool SupportsColors();
|
||||
void SetReportPath(const char *path);
|
||||
|
||||
// Don't use fields directly. They are only declared public to allow
|
||||
// aggregate initialization.
|
||||
|
||||
// Protects fields below.
|
||||
StaticSpinMutex *mu;
|
||||
// Opened file descriptor. Defaults to stderr. It may be equal to
|
||||
// kInvalidFd, in which case new file will be opened when necessary.
|
||||
fd_t fd;
|
||||
// Path prefix of report file, set via __sanitizer_set_report_path.
|
||||
char path_prefix[kMaxPathLength];
|
||||
// Full path to report, obtained as <path_prefix>.PID
|
||||
char full_path[kMaxPathLength];
|
||||
// PID of the process that opened fd. If a fork() occurs,
|
||||
// the PID of child will be different from fd_pid.
|
||||
uptr fd_pid;
|
||||
|
||||
private:
|
||||
void ReopenIfNecessary();
|
||||
};
|
||||
extern ReportFile report_file;
|
||||
|
||||
enum FileAccessMode {
|
||||
RdOnly,
|
||||
WrOnly,
|
||||
RdWr
|
||||
};
|
||||
|
||||
// Returns kInvalidFd on error.
|
||||
fd_t OpenFile(const char *filename, FileAccessMode mode,
|
||||
error_t *errno_p = nullptr);
|
||||
void CloseFile(fd_t);
|
||||
|
||||
// Return true on success, false on error.
|
||||
bool ReadFromFile(fd_t fd, void *buff, uptr buff_size,
|
||||
uptr *bytes_read = nullptr, error_t *error_p = nullptr);
|
||||
bool WriteToFile(fd_t fd, const void *buff, uptr buff_size,
|
||||
uptr *bytes_written = nullptr, error_t *error_p = nullptr);
|
||||
|
||||
// Scoped file handle closer.
|
||||
struct FileCloser {
|
||||
explicit FileCloser(fd_t fd) : fd(fd) {}
|
||||
~FileCloser() { CloseFile(fd); }
|
||||
fd_t fd;
|
||||
};
|
||||
|
||||
bool SupportsColoredOutput(fd_t fd);
|
||||
|
||||
// OS
|
||||
const char *GetPwd();
|
||||
bool FileExists(const char *filename);
|
||||
char *FindPathToBinary(const char *name);
|
||||
bool IsPathSeparator(const char c);
|
||||
bool IsAbsolutePath(const char *path);
|
||||
// Starts a subprocess and returs its pid.
|
||||
// If *_fd parameters are not kInvalidFd their corresponding input/output
|
||||
// streams will be redirect to the file. The files will always be closed
|
||||
// in parent process even in case of an error.
|
||||
// The child process will close all fds after STDERR_FILENO
|
||||
// before passing control to a program.
|
||||
pid_t StartSubprocess(const char *filename, const char *const argv[],
|
||||
const char *const envp[], fd_t stdin_fd = kInvalidFd,
|
||||
fd_t stdout_fd = kInvalidFd, fd_t stderr_fd = kInvalidFd);
|
||||
// Checks if specified process is still running
|
||||
bool IsProcessRunning(pid_t pid);
|
||||
// Waits for the process to finish and returns its exit code.
|
||||
// Returns -1 in case of an error.
|
||||
int WaitForProcess(pid_t pid);
|
||||
|
||||
// Maps given file to virtual memory, and returns pointer to it
|
||||
// (or NULL if mapping fails). Stores the size of mmaped region
|
||||
// in '*buff_size'.
|
||||
void *MapFileToMemory(const char *file_name, uptr *buff_size);
|
||||
void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset);
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_FILE_H
|
||||
191
lib/tsan/sanitizer_common/sanitizer_flag_parser.cpp
Normal file
191
lib/tsan/sanitizer_common/sanitizer_flag_parser.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
//===-- sanitizer_flag_parser.cpp -----------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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() {
|
||||
char buffer[128];
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
Printf("Available flags for %s:\n", SanitizerToolName);
|
||||
for (int i = 0; i < n_flags_; ++i) {
|
||||
bool truncated = !(flags_[i].handler->Format(buffer, sizeof(buffer)));
|
||||
CHECK_EQ(buffer[sizeof(buffer) - 1], '\0');
|
||||
const char *truncation_str = truncated ? " Truncated" : "";
|
||||
Printf("\t%s\n\t\t- %s (Current Value%s: %s)\n", flags_[i].name,
|
||||
flags_[i].desc, truncation_str, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void FlagParser::fatal_error(const char *err) {
|
||||
Printf("%s: ERROR: %s\n", SanitizerToolName, 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(const char *env_option_name) {
|
||||
uptr name_start = pos_;
|
||||
while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_;
|
||||
if (buf_[pos_] != '=') {
|
||||
if (env_option_name) {
|
||||
Printf("%s: ERROR: expected '=' in %s\n", SanitizerToolName,
|
||||
env_option_name);
|
||||
Die();
|
||||
} else {
|
||||
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(const char *env_option_name) {
|
||||
while (true) {
|
||||
skip_whitespace();
|
||||
if (buf_[pos_] == 0) break;
|
||||
parse_flag(env_option_name);
|
||||
}
|
||||
|
||||
// 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::ParseStringFromEnv(const char *env_name) {
|
||||
const char *env = GetEnv(env_name);
|
||||
VPrintf(1, "%s: %s\n", env_name, env ? env : "<empty>");
|
||||
ParseString(env, env_name);
|
||||
}
|
||||
|
||||
void FlagParser::ParseString(const char *s, const char *env_option_name) {
|
||||
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(env_option_name);
|
||||
|
||||
buf_ = old_buf_;
|
||||
pos_ = old_pos_;
|
||||
}
|
||||
|
||||
bool FlagParser::ParseFile(const char *path, bool ignore_missing) {
|
||||
static const uptr kMaxIncludeSize = 1 << 15;
|
||||
char *data;
|
||||
uptr data_mapped_size;
|
||||
error_t err;
|
||||
uptr len;
|
||||
if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len,
|
||||
Max(kMaxIncludeSize, GetPageSizeCached()), &err)) {
|
||||
if (ignore_missing)
|
||||
return true;
|
||||
Printf("Failed to read options from '%s': error %d\n", path, err);
|
||||
return false;
|
||||
}
|
||||
ParseString(data, path);
|
||||
UnmapOrDie(data, data_mapped_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
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
|
||||
204
lib/tsan/sanitizer_common/sanitizer_flag_parser.h
Normal file
204
lib/tsan/sanitizer_common/sanitizer_flag_parser.h
Normal file
@@ -0,0 +1,204 @@
|
||||
//===-- sanitizer_flag_parser.h ---------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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; }
|
||||
// Write the C string representation of the current value (truncated to fit)
|
||||
// into the buffer of size `size`. Returns false if truncation occurred and
|
||||
// returns true otherwise.
|
||||
virtual bool Format(char *buffer, uptr size) {
|
||||
if (size > 0)
|
||||
buffer[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
~FlagHandlerBase() {}
|
||||
|
||||
inline bool FormatString(char *buffer, uptr size, const char *str_to_use) {
|
||||
uptr num_symbols_should_write =
|
||||
internal_snprintf(buffer, size, "%s", str_to_use);
|
||||
return num_symbols_should_write < size;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class FlagHandler : public FlagHandlerBase {
|
||||
T *t_;
|
||||
|
||||
public:
|
||||
explicit FlagHandler(T *t) : t_(t) {}
|
||||
bool Parse(const char *value) final;
|
||||
bool Format(char *buffer, uptr size) final;
|
||||
};
|
||||
|
||||
inline bool ParseBool(const char *value, bool *b) {
|
||||
if (internal_strcmp(value, "0") == 0 ||
|
||||
internal_strcmp(value, "no") == 0 ||
|
||||
internal_strcmp(value, "false") == 0) {
|
||||
*b = false;
|
||||
return true;
|
||||
}
|
||||
if (internal_strcmp(value, "1") == 0 ||
|
||||
internal_strcmp(value, "yes") == 0 ||
|
||||
internal_strcmp(value, "true") == 0) {
|
||||
*b = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<bool>::Parse(const char *value) {
|
||||
if (ParseBool(value, t_)) return true;
|
||||
Printf("ERROR: Invalid value for bool option: '%s'\n", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<bool>::Format(char *buffer, uptr size) {
|
||||
return FormatString(buffer, size, *t_ ? "true" : "false");
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<HandleSignalMode>::Parse(const char *value) {
|
||||
bool b;
|
||||
if (ParseBool(value, &b)) {
|
||||
*t_ = b ? kHandleSignalYes : kHandleSignalNo;
|
||||
return true;
|
||||
}
|
||||
if (internal_strcmp(value, "2") == 0 ||
|
||||
internal_strcmp(value, "exclusive") == 0) {
|
||||
*t_ = kHandleSignalExclusive;
|
||||
return true;
|
||||
}
|
||||
Printf("ERROR: Invalid value for signal handler option: '%s'\n", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<HandleSignalMode>::Format(char *buffer, uptr size) {
|
||||
uptr num_symbols_should_write = internal_snprintf(buffer, size, "%d", *t_);
|
||||
return num_symbols_should_write < size;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<const char *>::Parse(const char *value) {
|
||||
*t_ = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<const char *>::Format(char *buffer, uptr size) {
|
||||
return FormatString(buffer, size, *t_);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<int>::Parse(const char *value) {
|
||||
const 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<int>::Format(char *buffer, uptr size) {
|
||||
uptr num_symbols_should_write = internal_snprintf(buffer, size, "%d", *t_);
|
||||
return num_symbols_should_write < size;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<uptr>::Parse(const char *value) {
|
||||
const 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;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<uptr>::Format(char *buffer, uptr size) {
|
||||
uptr num_symbols_should_write = internal_snprintf(buffer, size, "%p", *t_);
|
||||
return num_symbols_should_write < size;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<s64>::Parse(const char *value) {
|
||||
const char *value_end;
|
||||
*t_ = internal_simple_strtoll(value, &value_end, 10);
|
||||
bool ok = *value_end == 0;
|
||||
if (!ok) Printf("ERROR: Invalid value for s64 option: '%s'\n", value);
|
||||
return ok;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool FlagHandler<s64>::Format(char *buffer, uptr size) {
|
||||
uptr num_symbols_should_write = internal_snprintf(buffer, size, "%lld", *t_);
|
||||
return num_symbols_should_write < size;
|
||||
}
|
||||
|
||||
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, const char *env_name = 0);
|
||||
void ParseStringFromEnv(const char *env_name);
|
||||
bool ParseFile(const char *path, bool ignore_missing);
|
||||
void PrintFlagDescriptions();
|
||||
|
||||
static LowLevelAllocator Alloc;
|
||||
|
||||
private:
|
||||
void fatal_error(const char *err);
|
||||
bool is_space(char c);
|
||||
void skip_whitespace();
|
||||
void parse_flags(const char *env_option_name);
|
||||
void parse_flag(const char *env_option_name);
|
||||
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);
|
||||
parser->RegisterHandler(name, fh, desc);
|
||||
}
|
||||
|
||||
void ReportUnrecognizedFlags();
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_FLAG_REGISTRY_H
|
||||
129
lib/tsan/sanitizer_common/sanitizer_flags.cpp
Normal file
129
lib/tsan/sanitizer_common/sanitizer_flags.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
//===-- sanitizer_flags.cpp -----------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_flags.h"
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_list.h"
|
||||
#include "sanitizer_flag_parser.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
CommonFlags common_flags_dont_use;
|
||||
|
||||
void CommonFlags::SetDefaults() {
|
||||
#define COMMON_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
|
||||
#include "sanitizer_flags.inc"
|
||||
#undef COMMON_FLAG
|
||||
}
|
||||
|
||||
void CommonFlags::CopyFrom(const CommonFlags &other) {
|
||||
internal_memcpy(this, &other, sizeof(*this));
|
||||
}
|
||||
|
||||
// Copy the string from "s" to "out", making the following substitutions:
|
||||
// %b = binary basename
|
||||
// %p = pid
|
||||
void SubstituteForFlagValue(const char *s, char *out, uptr out_size) {
|
||||
char *out_end = out + out_size;
|
||||
while (*s && out < out_end - 1) {
|
||||
if (s[0] != '%') {
|
||||
*out++ = *s++;
|
||||
continue;
|
||||
}
|
||||
switch (s[1]) {
|
||||
case 'b': {
|
||||
const char *base = GetProcessName();
|
||||
CHECK(base);
|
||||
while (*base && out < out_end - 1)
|
||||
*out++ = *base++;
|
||||
s += 2; // skip "%b"
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
int pid = internal_getpid();
|
||||
char buf[32];
|
||||
char *buf_pos = buf + 32;
|
||||
do {
|
||||
*--buf_pos = (pid % 10) + '0';
|
||||
pid /= 10;
|
||||
} while (pid);
|
||||
while (buf_pos < buf + 32 && out < out_end - 1)
|
||||
*out++ = *buf_pos++;
|
||||
s += 2; // skip "%p"
|
||||
break;
|
||||
}
|
||||
default:
|
||||
*out++ = *s++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK(out < out_end - 1);
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
class FlagHandlerInclude : public FlagHandlerBase {
|
||||
FlagParser *parser_;
|
||||
bool ignore_missing_;
|
||||
const char *original_path_;
|
||||
|
||||
public:
|
||||
explicit FlagHandlerInclude(FlagParser *parser, bool ignore_missing)
|
||||
: parser_(parser), ignore_missing_(ignore_missing), original_path_("") {}
|
||||
bool Parse(const char *value) final {
|
||||
original_path_ = value;
|
||||
if (internal_strchr(value, '%')) {
|
||||
char *buf = (char *)MmapOrDie(kMaxPathLength, "FlagHandlerInclude");
|
||||
SubstituteForFlagValue(value, buf, kMaxPathLength);
|
||||
bool res = parser_->ParseFile(buf, ignore_missing_);
|
||||
UnmapOrDie(buf, kMaxPathLength);
|
||||
return res;
|
||||
}
|
||||
return parser_->ParseFile(value, ignore_missing_);
|
||||
}
|
||||
bool Format(char *buffer, uptr size) {
|
||||
// Note `original_path_` isn't actually what's parsed due to `%`
|
||||
// substitutions. Printing the substituted path would require holding onto
|
||||
// mmap'ed memory.
|
||||
return FormatString(buffer, size, original_path_);
|
||||
}
|
||||
};
|
||||
|
||||
void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf) {
|
||||
FlagHandlerInclude *fh_include = new (FlagParser::Alloc)
|
||||
FlagHandlerInclude(parser, /*ignore_missing*/ false);
|
||||
parser->RegisterHandler("include", fh_include,
|
||||
"read more options from the given file");
|
||||
FlagHandlerInclude *fh_include_if_exists = new (FlagParser::Alloc)
|
||||
FlagHandlerInclude(parser, /*ignore_missing*/ true);
|
||||
parser->RegisterHandler(
|
||||
"include_if_exists", fh_include_if_exists,
|
||||
"read more options from the given file (if it exists)");
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
RegisterIncludeFlags(parser, cf);
|
||||
}
|
||||
|
||||
void InitializeCommonFlags(CommonFlags *cf) {
|
||||
// need to record coverage to generate coverage report.
|
||||
cf->coverage |= cf->html_cov_report;
|
||||
SetVerbosity(cf->verbosity);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
67
lib/tsan/sanitizer_common/sanitizer_flags.h
Normal file
67
lib/tsan/sanitizer_common/sanitizer_flags.h
Normal file
@@ -0,0 +1,67 @@
|
||||
//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_FLAGS_H
|
||||
#define SANITIZER_FLAGS_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
enum HandleSignalMode {
|
||||
kHandleSignalNo,
|
||||
kHandleSignalYes,
|
||||
kHandleSignalExclusive,
|
||||
};
|
||||
|
||||
struct CommonFlags {
|
||||
#define COMMON_FLAG(Type, Name, DefaultValue, Description) Type Name;
|
||||
#include "sanitizer_flags.inc"
|
||||
#undef COMMON_FLAG
|
||||
|
||||
void SetDefaults();
|
||||
void CopyFrom(const CommonFlags &other);
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void SubstituteForFlagValue(const char *s, char *out, uptr out_size);
|
||||
|
||||
class FlagParser;
|
||||
void RegisterCommonFlags(FlagParser *parser,
|
||||
CommonFlags *cf = &common_flags_dont_use);
|
||||
void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf);
|
||||
|
||||
// Should be called after parsing all flags. Sets up common flag values
|
||||
// and perform initializations common to all sanitizers (e.g. setting
|
||||
// verbosity).
|
||||
void InitializeCommonFlags(CommonFlags *cf = &common_flags_dont_use);
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_FLAGS_H
|
||||
250
lib/tsan/sanitizer_common/sanitizer_flags.inc
Normal file
250
lib/tsan/sanitizer_common/sanitizer_flags.inc
Normal file
@@ -0,0 +1,250 @@
|
||||
//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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, nullptr,
|
||||
"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(
|
||||
bool, log_exe_name, false,
|
||||
"Mention name of executable when reporting error and "
|
||||
"append executable name to logs (as in \"log_path.exe_name.pid\").")
|
||||
COMMON_FLAG(
|
||||
bool, log_to_syslog, (bool)SANITIZER_ANDROID || (bool)SANITIZER_MAC,
|
||||
"Write all sanitizer output to syslog in addition to other means of "
|
||||
"logging.")
|
||||
COMMON_FLAG(
|
||||
int, verbosity, 0,
|
||||
"Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).")
|
||||
COMMON_FLAG(bool, strip_env, 1,
|
||||
"Whether to remove the sanitizer from DYLD_INSERT_LIBRARIES to "
|
||||
"avoid passing it to children. Default is true.")
|
||||
COMMON_FLAG(bool, detect_leaks, !SANITIZER_MAC, "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(int, print_module_map, 0,
|
||||
"OS X only (0 - don't print, 1 - print only once before process "
|
||||
"exits, 2 - print after each report).")
|
||||
COMMON_FLAG(bool, check_printf, true, "Check printf arguments.")
|
||||
#define COMMON_FLAG_HANDLE_SIGNAL_HELP(signal) \
|
||||
"Controls custom tool's " #signal " handler (0 - do not registers the " \
|
||||
"handler, 1 - register the handler and allow user to set own, " \
|
||||
"2 - registers the handler and block user from changing it). "
|
||||
COMMON_FLAG(HandleSignalMode, handle_segv, kHandleSignalYes,
|
||||
COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGSEGV))
|
||||
COMMON_FLAG(HandleSignalMode, handle_sigbus, kHandleSignalYes,
|
||||
COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGBUS))
|
||||
COMMON_FLAG(HandleSignalMode, handle_abort, kHandleSignalNo,
|
||||
COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGABRT))
|
||||
COMMON_FLAG(HandleSignalMode, handle_sigill, kHandleSignalNo,
|
||||
COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGILL))
|
||||
COMMON_FLAG(HandleSignalMode, handle_sigtrap, kHandleSignalNo,
|
||||
COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGTRAP))
|
||||
COMMON_FLAG(HandleSignalMode, handle_sigfpe, kHandleSignalYes,
|
||||
COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGFPE))
|
||||
#undef COMMON_FLAG_HANDLE_SIGNAL_HELP
|
||||
COMMON_FLAG(bool, allow_user_segv_handler, true,
|
||||
"Deprecated. True has no effect, use handle_sigbus=1. If false, "
|
||||
"handle_*=1 will be upgraded to handle_*=2.")
|
||||
COMMON_FLAG(bool, use_sigaltstack, true,
|
||||
"If set, uses alternate stack for signal handling.")
|
||||
COMMON_FLAG(bool, detect_deadlocks, true,
|
||||
"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(uptr, max_allocation_size_mb, 0,
|
||||
"If non-zero, malloc/new calls larger than this size will return "
|
||||
"nullptr (or crash if allocator_may_return_null=false).")
|
||||
COMMON_FLAG(bool, heap_profile, false, "Experimental heap profiler, asan-only")
|
||||
COMMON_FLAG(s32, allocator_release_to_os_interval_ms,
|
||||
((bool)SANITIZER_FUCHSIA || (bool)SANITIZER_WINDOWS) ? -1 : 5000,
|
||||
"Only affects a 64-bit allocator. If set, tries to release unused "
|
||||
"memory to the OS, but not more often than this interval (in "
|
||||
"milliseconds). Negative values mean do not attempt to release "
|
||||
"memory to the OS.\n")
|
||||
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).")
|
||||
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(bool, print_suppressions, true,
|
||||
"Print matched suppressions at exit.")
|
||||
COMMON_FLAG(
|
||||
bool, disable_coredump, (SANITIZER_WORDSIZE == 64) && !SANITIZER_GO,
|
||||
"Disable core dumping. By default, disable_coredump=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(bool, symbolize_vs_style, false,
|
||||
"Print file locations in Visual Studio style (e.g: "
|
||||
" file(10,42): ...")
|
||||
COMMON_FLAG(int, dedup_token_length, 0,
|
||||
"If positive, after printing a stack trace also print a short "
|
||||
"string token based on this number of frames that will simplify "
|
||||
"deduplication of the reports. "
|
||||
"Example: 'DEDUP_TOKEN: foo-bar-main'. Default is 0.")
|
||||
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. ")
|
||||
COMMON_FLAG(bool, strict_string_checks, false,
|
||||
"If set check that string arguments are properly null-terminated")
|
||||
COMMON_FLAG(bool, intercept_strstr, true,
|
||||
"If set, uses custom wrappers for strstr and strcasestr functions "
|
||||
"to find more errors.")
|
||||
COMMON_FLAG(bool, intercept_strspn, true,
|
||||
"If set, uses custom wrappers for strspn and strcspn function "
|
||||
"to find more errors.")
|
||||
COMMON_FLAG(bool, intercept_strtok, true,
|
||||
"If set, uses a custom wrapper for the strtok function "
|
||||
"to find more errors.")
|
||||
COMMON_FLAG(bool, intercept_strpbrk, true,
|
||||
"If set, uses custom wrappers for strpbrk function "
|
||||
"to find more errors.")
|
||||
COMMON_FLAG(bool, intercept_strlen, true,
|
||||
"If set, uses custom wrappers for strlen and strnlen functions "
|
||||
"to find more errors.")
|
||||
COMMON_FLAG(bool, intercept_strndup, true,
|
||||
"If set, uses custom wrappers for strndup functions "
|
||||
"to find more errors.")
|
||||
COMMON_FLAG(bool, intercept_strchr, true,
|
||||
"If set, uses custom wrappers for strchr, strchrnul, and strrchr "
|
||||
"functions to find more errors.")
|
||||
COMMON_FLAG(bool, intercept_memcmp, true,
|
||||
"If set, uses custom wrappers for memcmp function "
|
||||
"to find more errors.")
|
||||
COMMON_FLAG(bool, strict_memcmp, true,
|
||||
"If true, assume that memcmp(p1, p2, n) always reads n bytes before "
|
||||
"comparing p1 and p2.")
|
||||
COMMON_FLAG(bool, intercept_memmem, true,
|
||||
"If set, uses a wrapper for memmem() to find more errors.")
|
||||
COMMON_FLAG(bool, intercept_intrin, true,
|
||||
"If set, uses custom wrappers for memset/memcpy/memmove "
|
||||
"intrinsics to find more errors.")
|
||||
COMMON_FLAG(bool, intercept_stat, true,
|
||||
"If set, uses custom wrappers for *stat functions "
|
||||
"to find more errors.")
|
||||
COMMON_FLAG(bool, intercept_send, true,
|
||||
"If set, uses custom wrappers for send* functions "
|
||||
"to find more errors.")
|
||||
COMMON_FLAG(bool, decorate_proc_maps, (bool)SANITIZER_ANDROID,
|
||||
"If set, decorate sanitizer mappings in /proc/self/maps with "
|
||||
"user-readable names")
|
||||
COMMON_FLAG(int, exitcode, 1, "Override the program exit status if the tool "
|
||||
"found an error")
|
||||
COMMON_FLAG(
|
||||
bool, abort_on_error, (bool)SANITIZER_ANDROID || (bool)SANITIZER_MAC,
|
||||
"If set, the tool calls abort() instead of _exit() after printing the "
|
||||
"error report.")
|
||||
COMMON_FLAG(bool, suppress_equal_pcs, true,
|
||||
"Deduplicate multiple reports for single source location in "
|
||||
"halt_on_error=false mode (asan only).")
|
||||
COMMON_FLAG(bool, print_cmdline, false, "Print command line on crash "
|
||||
"(asan only).")
|
||||
COMMON_FLAG(bool, html_cov_report, false, "Generate html coverage report.")
|
||||
COMMON_FLAG(const char *, sancov_path, "sancov", "Sancov tool location.")
|
||||
COMMON_FLAG(bool, dump_instruction_bytes, false,
|
||||
"If true, dump 16 bytes starting at the instruction that caused SEGV")
|
||||
COMMON_FLAG(bool, dump_registers, true,
|
||||
"If true, dump values of CPU registers when SEGV happens. Only "
|
||||
"available on OS X for now.")
|
||||
COMMON_FLAG(bool, detect_write_exec, false,
|
||||
"If true, triggers warning when writable-executable pages requests "
|
||||
"are being made")
|
||||
COMMON_FLAG(bool, test_only_emulate_no_memorymap, false,
|
||||
"TEST ONLY fail to read memory mappings to emulate sanitized "
|
||||
"\"init\"")
|
||||
137
lib/tsan/sanitizer_common/sanitizer_freebsd.h
Normal file
137
lib/tsan/sanitizer_common/sanitizer_freebsd.h
Normal file
@@ -0,0 +1,137 @@
|
||||
//===-- sanitizer_freebsd.h -------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer runtime. It contains FreeBSD-specific
|
||||
// definitions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_FREEBSD_H
|
||||
#define SANITIZER_FREEBSD_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
|
||||
// 32-bit mode.
|
||||
#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
|
||||
#include <osreldate.h>
|
||||
#if __FreeBSD_version <= 902001 // v9.2
|
||||
#include <link.h>
|
||||
#include <sys/param.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
typedef unsigned long long __xuint64_t;
|
||||
|
||||
typedef __int32_t __xregister_t;
|
||||
|
||||
typedef struct __xmcontext {
|
||||
__xregister_t mc_onstack;
|
||||
__xregister_t mc_gs;
|
||||
__xregister_t mc_fs;
|
||||
__xregister_t mc_es;
|
||||
__xregister_t mc_ds;
|
||||
__xregister_t mc_edi;
|
||||
__xregister_t mc_esi;
|
||||
__xregister_t mc_ebp;
|
||||
__xregister_t mc_isp;
|
||||
__xregister_t mc_ebx;
|
||||
__xregister_t mc_edx;
|
||||
__xregister_t mc_ecx;
|
||||
__xregister_t mc_eax;
|
||||
__xregister_t mc_trapno;
|
||||
__xregister_t mc_err;
|
||||
__xregister_t mc_eip;
|
||||
__xregister_t mc_cs;
|
||||
__xregister_t mc_eflags;
|
||||
__xregister_t mc_esp;
|
||||
__xregister_t mc_ss;
|
||||
|
||||
int mc_len;
|
||||
int mc_fpformat;
|
||||
int mc_ownedfp;
|
||||
__xregister_t mc_flags;
|
||||
|
||||
int mc_fpstate[128] __aligned(16);
|
||||
__xregister_t mc_fsbase;
|
||||
__xregister_t mc_gsbase;
|
||||
__xregister_t mc_xfpustate;
|
||||
__xregister_t mc_xfpustate_len;
|
||||
|
||||
int mc_spare2[4];
|
||||
} xmcontext_t;
|
||||
|
||||
typedef struct __xucontext {
|
||||
sigset_t uc_sigmask;
|
||||
xmcontext_t uc_mcontext;
|
||||
|
||||
struct __ucontext *uc_link;
|
||||
stack_t uc_stack;
|
||||
int uc_flags;
|
||||
int __spare__[4];
|
||||
} xucontext_t;
|
||||
|
||||
struct xkinfo_vmentry {
|
||||
int kve_structsize;
|
||||
int kve_type;
|
||||
__xuint64_t kve_start;
|
||||
__xuint64_t kve_end;
|
||||
__xuint64_t kve_offset;
|
||||
__xuint64_t kve_vn_fileid;
|
||||
__uint32_t kve_vn_fsid;
|
||||
int kve_flags;
|
||||
int kve_resident;
|
||||
int kve_private_resident;
|
||||
int kve_protection;
|
||||
int kve_ref_count;
|
||||
int kve_shadow_count;
|
||||
int kve_vn_type;
|
||||
__xuint64_t kve_vn_size;
|
||||
__uint32_t kve_vn_rdev;
|
||||
__uint16_t kve_vn_mode;
|
||||
__uint16_t kve_status;
|
||||
int _kve_ispare[12];
|
||||
char kve_path[PATH_MAX];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
__uint32_t p_type;
|
||||
__uint32_t p_offset;
|
||||
__uint32_t p_vaddr;
|
||||
__uint32_t p_paddr;
|
||||
__uint32_t p_filesz;
|
||||
__uint32_t p_memsz;
|
||||
__uint32_t p_flags;
|
||||
__uint32_t p_align;
|
||||
} XElf32_Phdr;
|
||||
|
||||
struct xdl_phdr_info {
|
||||
Elf_Addr dlpi_addr;
|
||||
const char *dlpi_name;
|
||||
const XElf32_Phdr *dlpi_phdr;
|
||||
Elf_Half dlpi_phnum;
|
||||
unsigned long long int dlpi_adds;
|
||||
unsigned long long int dlpi_subs;
|
||||
size_t dlpi_tls_modid;
|
||||
void *dlpi_tls_data;
|
||||
};
|
||||
|
||||
typedef int (*__xdl_iterate_hdr_callback)(struct xdl_phdr_info *, size_t,
|
||||
void *);
|
||||
typedef int xdl_iterate_phdr_t(__xdl_iterate_hdr_callback, void *);
|
||||
|
||||
#define xdl_iterate_phdr(callback, param) \
|
||||
(((xdl_iterate_phdr_t *)dl_iterate_phdr)((callback), (param)))
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // __FreeBSD_version <= 902001
|
||||
#endif // SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
|
||||
|
||||
#endif // SANITIZER_FREEBSD_H
|
||||
531
lib/tsan/sanitizer_common/sanitizer_fuchsia.cpp
Normal file
531
lib/tsan/sanitizer_common/sanitizer_fuchsia.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
//===-- sanitizer_fuchsia.cpp ---------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and other sanitizer
|
||||
// run-time libraries and implements Fuchsia-specific functions from
|
||||
// sanitizer_common.h.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_fuchsia.h"
|
||||
#if SANITIZER_FUCHSIA
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <zircon/errors.h>
|
||||
#include <zircon/process.h>
|
||||
#include <zircon/syscalls.h>
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
void NORETURN internal__exit(int exitcode) { _zx_process_exit(exitcode); }
|
||||
|
||||
uptr internal_sched_yield() {
|
||||
zx_status_t status = _zx_nanosleep(0);
|
||||
CHECK_EQ(status, ZX_OK);
|
||||
return 0; // Why doesn't this return void?
|
||||
}
|
||||
|
||||
static void internal_nanosleep(zx_time_t ns) {
|
||||
zx_status_t status = _zx_nanosleep(_zx_deadline_after(ns));
|
||||
CHECK_EQ(status, ZX_OK);
|
||||
}
|
||||
|
||||
unsigned int internal_sleep(unsigned int seconds) {
|
||||
internal_nanosleep(ZX_SEC(seconds));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 NanoTime() {
|
||||
zx_time_t time;
|
||||
zx_status_t status = _zx_clock_get(ZX_CLOCK_UTC, &time);
|
||||
CHECK_EQ(status, ZX_OK);
|
||||
return time;
|
||||
}
|
||||
|
||||
u64 MonotonicNanoTime() { return _zx_clock_get_monotonic(); }
|
||||
|
||||
uptr internal_getpid() {
|
||||
zx_info_handle_basic_t info;
|
||||
zx_status_t status =
|
||||
_zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &info,
|
||||
sizeof(info), NULL, NULL);
|
||||
CHECK_EQ(status, ZX_OK);
|
||||
uptr pid = static_cast<uptr>(info.koid);
|
||||
CHECK_EQ(pid, info.koid);
|
||||
return pid;
|
||||
}
|
||||
|
||||
int internal_dlinfo(void *handle, int request, void *p) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
uptr GetThreadSelf() { return reinterpret_cast<uptr>(thrd_current()); }
|
||||
|
||||
tid_t GetTid() { return GetThreadSelf(); }
|
||||
|
||||
void Abort() { abort(); }
|
||||
|
||||
int Atexit(void (*function)(void)) { return atexit(function); }
|
||||
|
||||
void SleepForSeconds(int seconds) { internal_sleep(seconds); }
|
||||
|
||||
void SleepForMillis(int millis) { internal_nanosleep(ZX_MSEC(millis)); }
|
||||
|
||||
void GetThreadStackTopAndBottom(bool, uptr *stack_top, uptr *stack_bottom) {
|
||||
pthread_attr_t attr;
|
||||
CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
|
||||
void *base;
|
||||
size_t size;
|
||||
CHECK_EQ(pthread_attr_getstack(&attr, &base, &size), 0);
|
||||
CHECK_EQ(pthread_attr_destroy(&attr), 0);
|
||||
|
||||
*stack_bottom = reinterpret_cast<uptr>(base);
|
||||
*stack_top = *stack_bottom + size;
|
||||
}
|
||||
|
||||
void InitializePlatformEarly() {}
|
||||
void MaybeReexec() {}
|
||||
void CheckASLR() {}
|
||||
void CheckMPROTECT() {}
|
||||
void PlatformPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {}
|
||||
void DisableCoreDumperIfNecessary() {}
|
||||
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
|
||||
void SetAlternateSignalStack() {}
|
||||
void UnsetAlternateSignalStack() {}
|
||||
void InitTlsSize() {}
|
||||
|
||||
void PrintModuleMap() {}
|
||||
|
||||
bool SignalContext::IsStackOverflow() const { return false; }
|
||||
void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); }
|
||||
const char *SignalContext::Describe() const { UNIMPLEMENTED(); }
|
||||
|
||||
enum MutexState : int { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 };
|
||||
|
||||
BlockingMutex::BlockingMutex() {
|
||||
// NOTE! It's important that this use internal_memset, because plain
|
||||
// memset might be intercepted (e.g., actually be __asan_memset).
|
||||
// Defining this so the compiler initializes each field, e.g.:
|
||||
// BlockingMutex::BlockingMutex() : BlockingMutex(LINKER_INITIALIZED) {}
|
||||
// might result in the compiler generating a call to memset, which would
|
||||
// have the same problem.
|
||||
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;
|
||||
while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
|
||||
zx_status_t status =
|
||||
_zx_futex_wait(reinterpret_cast<zx_futex_t *>(m), MtxSleeping,
|
||||
ZX_HANDLE_INVALID, ZX_TIME_INFINITE);
|
||||
if (status != ZX_ERR_BAD_STATE) // Normal race.
|
||||
CHECK_EQ(status, ZX_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockingMutex::Unlock() {
|
||||
atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
|
||||
u32 v = atomic_exchange(m, MtxUnlocked, memory_order_release);
|
||||
CHECK_NE(v, MtxUnlocked);
|
||||
if (v == MtxSleeping) {
|
||||
zx_status_t status = _zx_futex_wake(reinterpret_cast<zx_futex_t *>(m), 1);
|
||||
CHECK_EQ(status, ZX_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockingMutex::CheckLocked() {
|
||||
atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
|
||||
CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
|
||||
}
|
||||
|
||||
uptr GetPageSize() { return PAGE_SIZE; }
|
||||
|
||||
uptr GetMmapGranularity() { return PAGE_SIZE; }
|
||||
|
||||
sanitizer_shadow_bounds_t ShadowBounds;
|
||||
|
||||
uptr GetMaxUserVirtualAddress() {
|
||||
ShadowBounds = __sanitizer_shadow_bounds();
|
||||
return ShadowBounds.memory_limit - 1;
|
||||
}
|
||||
|
||||
uptr GetMaxVirtualAddress() { return GetMaxUserVirtualAddress(); }
|
||||
|
||||
static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
|
||||
bool raw_report, bool die_for_nomem) {
|
||||
size = RoundUpTo(size, PAGE_SIZE);
|
||||
|
||||
zx_handle_t vmo;
|
||||
zx_status_t status = _zx_vmo_create(size, 0, &vmo);
|
||||
if (status != ZX_OK) {
|
||||
if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
|
||||
ReportMmapFailureAndDie(size, mem_type, "zx_vmo_create", status,
|
||||
raw_report);
|
||||
return nullptr;
|
||||
}
|
||||
_zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
|
||||
internal_strlen(mem_type));
|
||||
|
||||
// TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
|
||||
uintptr_t addr;
|
||||
status =
|
||||
_zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
|
||||
vmo, 0, size, &addr);
|
||||
_zx_handle_close(vmo);
|
||||
|
||||
if (status != ZX_OK) {
|
||||
if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
|
||||
ReportMmapFailureAndDie(size, mem_type, "zx_vmar_map", status,
|
||||
raw_report);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IncreaseTotalMmap(size);
|
||||
|
||||
return reinterpret_cast<void *>(addr);
|
||||
}
|
||||
|
||||
void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
|
||||
return DoAnonymousMmapOrDie(size, mem_type, raw_report, true);
|
||||
}
|
||||
|
||||
void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
|
||||
return MmapOrDie(size, mem_type);
|
||||
}
|
||||
|
||||
void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
|
||||
return DoAnonymousMmapOrDie(size, mem_type, false, false);
|
||||
}
|
||||
|
||||
uptr ReservedAddressRange::Init(uptr init_size, const char *name,
|
||||
uptr fixed_addr) {
|
||||
init_size = RoundUpTo(init_size, PAGE_SIZE);
|
||||
DCHECK_EQ(os_handle_, ZX_HANDLE_INVALID);
|
||||
uintptr_t base;
|
||||
zx_handle_t vmar;
|
||||
zx_status_t status =
|
||||
_zx_vmar_allocate(
|
||||
_zx_vmar_root_self(),
|
||||
ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC,
|
||||
0, init_size, &vmar, &base);
|
||||
if (status != ZX_OK)
|
||||
ReportMmapFailureAndDie(init_size, name, "zx_vmar_allocate", status);
|
||||
base_ = reinterpret_cast<void *>(base);
|
||||
size_ = init_size;
|
||||
name_ = name;
|
||||
os_handle_ = vmar;
|
||||
|
||||
return reinterpret_cast<uptr>(base_);
|
||||
}
|
||||
|
||||
static uptr DoMmapFixedOrDie(zx_handle_t vmar, uptr fixed_addr, uptr map_size,
|
||||
void *base, const char *name, bool die_for_nomem) {
|
||||
uptr offset = fixed_addr - reinterpret_cast<uptr>(base);
|
||||
map_size = RoundUpTo(map_size, PAGE_SIZE);
|
||||
zx_handle_t vmo;
|
||||
zx_status_t status = _zx_vmo_create(map_size, 0, &vmo);
|
||||
if (status != ZX_OK) {
|
||||
if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
|
||||
ReportMmapFailureAndDie(map_size, name, "zx_vmo_create", status);
|
||||
return 0;
|
||||
}
|
||||
_zx_object_set_property(vmo, ZX_PROP_NAME, name, internal_strlen(name));
|
||||
DCHECK_GE(base + size_, map_size + offset);
|
||||
uintptr_t addr;
|
||||
|
||||
status =
|
||||
_zx_vmar_map(vmar, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC,
|
||||
offset, vmo, 0, map_size, &addr);
|
||||
_zx_handle_close(vmo);
|
||||
if (status != ZX_OK) {
|
||||
if (status != ZX_ERR_NO_MEMORY || die_for_nomem) {
|
||||
ReportMmapFailureAndDie(map_size, name, "zx_vmar_map", status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
IncreaseTotalMmap(map_size);
|
||||
return addr;
|
||||
}
|
||||
|
||||
uptr ReservedAddressRange::Map(uptr fixed_addr, uptr map_size,
|
||||
const char *name) {
|
||||
return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_,
|
||||
name_, false);
|
||||
}
|
||||
|
||||
uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr map_size,
|
||||
const char *name) {
|
||||
return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_,
|
||||
name_, true);
|
||||
}
|
||||
|
||||
void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar) {
|
||||
if (!addr || !size) return;
|
||||
size = RoundUpTo(size, PAGE_SIZE);
|
||||
|
||||
zx_status_t status =
|
||||
_zx_vmar_unmap(target_vmar, reinterpret_cast<uintptr_t>(addr), size);
|
||||
if (status != ZX_OK) {
|
||||
Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
|
||||
SanitizerToolName, size, size, addr);
|
||||
CHECK("unable to unmap" && 0);
|
||||
}
|
||||
|
||||
DecreaseTotalMmap(size);
|
||||
}
|
||||
|
||||
void ReservedAddressRange::Unmap(uptr addr, uptr size) {
|
||||
CHECK_LE(size, size_);
|
||||
const zx_handle_t vmar = static_cast<zx_handle_t>(os_handle_);
|
||||
if (addr == reinterpret_cast<uptr>(base_)) {
|
||||
if (size == size_) {
|
||||
// Destroying the vmar effectively unmaps the whole mapping.
|
||||
_zx_vmar_destroy(vmar);
|
||||
_zx_handle_close(vmar);
|
||||
os_handle_ = static_cast<uptr>(ZX_HANDLE_INVALID);
|
||||
DecreaseTotalMmap(size);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
CHECK_EQ(addr + size, reinterpret_cast<uptr>(base_) + size_);
|
||||
}
|
||||
// Partial unmapping does not affect the fact that the initial range is still
|
||||
// reserved, and the resulting unmapped memory can't be reused.
|
||||
UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar);
|
||||
}
|
||||
|
||||
// This should never be called.
|
||||
void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
|
||||
const char *mem_type) {
|
||||
CHECK_GE(size, PAGE_SIZE);
|
||||
CHECK(IsPowerOfTwo(size));
|
||||
CHECK(IsPowerOfTwo(alignment));
|
||||
|
||||
zx_handle_t vmo;
|
||||
zx_status_t status = _zx_vmo_create(size, 0, &vmo);
|
||||
if (status != ZX_OK) {
|
||||
if (status != ZX_ERR_NO_MEMORY)
|
||||
ReportMmapFailureAndDie(size, mem_type, "zx_vmo_create", status, false);
|
||||
return nullptr;
|
||||
}
|
||||
_zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
|
||||
internal_strlen(mem_type));
|
||||
|
||||
// TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
|
||||
|
||||
// Map a larger size to get a chunk of address space big enough that
|
||||
// it surely contains an aligned region of the requested size. Then
|
||||
// overwrite the aligned middle portion with a mapping from the
|
||||
// beginning of the VMO, and unmap the excess before and after.
|
||||
size_t map_size = size + alignment;
|
||||
uintptr_t addr;
|
||||
status =
|
||||
_zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
|
||||
vmo, 0, map_size, &addr);
|
||||
if (status == ZX_OK) {
|
||||
uintptr_t map_addr = addr;
|
||||
uintptr_t map_end = map_addr + map_size;
|
||||
addr = RoundUpTo(map_addr, alignment);
|
||||
uintptr_t end = addr + size;
|
||||
if (addr != map_addr) {
|
||||
zx_info_vmar_t info;
|
||||
status = _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &info,
|
||||
sizeof(info), NULL, NULL);
|
||||
if (status == ZX_OK) {
|
||||
uintptr_t new_addr;
|
||||
status = _zx_vmar_map(
|
||||
_zx_vmar_root_self(),
|
||||
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC_OVERWRITE,
|
||||
addr - info.base, vmo, 0, size, &new_addr);
|
||||
if (status == ZX_OK) CHECK_EQ(new_addr, addr);
|
||||
}
|
||||
}
|
||||
if (status == ZX_OK && addr != map_addr)
|
||||
status = _zx_vmar_unmap(_zx_vmar_root_self(), map_addr, addr - map_addr);
|
||||
if (status == ZX_OK && end != map_end)
|
||||
status = _zx_vmar_unmap(_zx_vmar_root_self(), end, map_end - end);
|
||||
}
|
||||
_zx_handle_close(vmo);
|
||||
|
||||
if (status != ZX_OK) {
|
||||
if (status != ZX_ERR_NO_MEMORY)
|
||||
ReportMmapFailureAndDie(size, mem_type, "zx_vmar_map", status, false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IncreaseTotalMmap(size);
|
||||
|
||||
return reinterpret_cast<void *>(addr);
|
||||
}
|
||||
|
||||
void UnmapOrDie(void *addr, uptr size) {
|
||||
UnmapOrDieVmar(addr, size, _zx_vmar_root_self());
|
||||
}
|
||||
|
||||
// This is used on the shadow mapping, which cannot be changed.
|
||||
// Zircon doesn't have anything like MADV_DONTNEED.
|
||||
void ReleaseMemoryPagesToOS(uptr beg, uptr end) {}
|
||||
|
||||
void DumpProcessMap() {
|
||||
// TODO(mcgrathr): write it
|
||||
return;
|
||||
}
|
||||
|
||||
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
|
||||
// TODO(mcgrathr): Figure out a better way.
|
||||
zx_handle_t vmo;
|
||||
zx_status_t status = _zx_vmo_create(size, 0, &vmo);
|
||||
if (status == ZX_OK) {
|
||||
status = _zx_vmo_write(vmo, reinterpret_cast<const void *>(beg), 0, size);
|
||||
_zx_handle_close(vmo);
|
||||
}
|
||||
return status == ZX_OK;
|
||||
}
|
||||
|
||||
// FIXME implement on this platform.
|
||||
void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {}
|
||||
|
||||
bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
|
||||
uptr *read_len, uptr max_len, error_t *errno_p) {
|
||||
zx_handle_t vmo;
|
||||
zx_status_t status = __sanitizer_get_configuration(file_name, &vmo);
|
||||
if (status == ZX_OK) {
|
||||
uint64_t vmo_size;
|
||||
status = _zx_vmo_get_size(vmo, &vmo_size);
|
||||
if (status == ZX_OK) {
|
||||
if (vmo_size < max_len) max_len = vmo_size;
|
||||
size_t map_size = RoundUpTo(max_len, PAGE_SIZE);
|
||||
uintptr_t addr;
|
||||
status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ, 0, vmo, 0,
|
||||
map_size, &addr);
|
||||
if (status == ZX_OK) {
|
||||
*buff = reinterpret_cast<char *>(addr);
|
||||
*buff_size = map_size;
|
||||
*read_len = max_len;
|
||||
}
|
||||
}
|
||||
_zx_handle_close(vmo);
|
||||
}
|
||||
if (status != ZX_OK && errno_p) *errno_p = status;
|
||||
return status == ZX_OK;
|
||||
}
|
||||
|
||||
void RawWrite(const char *buffer) {
|
||||
constexpr size_t size = 128;
|
||||
static _Thread_local char line[size];
|
||||
static _Thread_local size_t lastLineEnd = 0;
|
||||
static _Thread_local size_t cur = 0;
|
||||
|
||||
while (*buffer) {
|
||||
if (cur >= size) {
|
||||
if (lastLineEnd == 0)
|
||||
lastLineEnd = size;
|
||||
__sanitizer_log_write(line, lastLineEnd);
|
||||
internal_memmove(line, line + lastLineEnd, cur - lastLineEnd);
|
||||
cur = cur - lastLineEnd;
|
||||
lastLineEnd = 0;
|
||||
}
|
||||
if (*buffer == '\n')
|
||||
lastLineEnd = cur + 1;
|
||||
line[cur++] = *buffer++;
|
||||
}
|
||||
// Flush all complete lines before returning.
|
||||
if (lastLineEnd != 0) {
|
||||
__sanitizer_log_write(line, lastLineEnd);
|
||||
internal_memmove(line, line + lastLineEnd, cur - lastLineEnd);
|
||||
cur = cur - lastLineEnd;
|
||||
lastLineEnd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CatastrophicErrorWrite(const char *buffer, uptr length) {
|
||||
__sanitizer_log_write(buffer, length);
|
||||
}
|
||||
|
||||
char **StoredArgv;
|
||||
char **StoredEnviron;
|
||||
|
||||
char **GetArgv() { return StoredArgv; }
|
||||
char **GetEnviron() { return StoredEnviron; }
|
||||
|
||||
const char *GetEnv(const char *name) {
|
||||
if (StoredEnviron) {
|
||||
uptr NameLen = internal_strlen(name);
|
||||
for (char **Env = StoredEnviron; *Env != 0; Env++) {
|
||||
if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=')
|
||||
return (*Env) + NameLen + 1;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uptr ReadBinaryName(/*out*/ char *buf, uptr buf_len) {
|
||||
const char *argv0 = "<UNKNOWN>";
|
||||
if (StoredArgv && StoredArgv[0]) {
|
||||
argv0 = StoredArgv[0];
|
||||
}
|
||||
internal_strncpy(buf, argv0, buf_len);
|
||||
return internal_strlen(buf);
|
||||
}
|
||||
|
||||
uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
|
||||
return ReadBinaryName(buf, buf_len);
|
||||
}
|
||||
|
||||
uptr MainThreadStackBase, MainThreadStackSize;
|
||||
|
||||
bool GetRandom(void *buffer, uptr length, bool blocking) {
|
||||
CHECK_LE(length, ZX_CPRNG_DRAW_MAX_LEN);
|
||||
_zx_cprng_draw(buffer, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 GetNumberOfCPUs() {
|
||||
return zx_system_get_num_cpus();
|
||||
}
|
||||
|
||||
uptr GetRSS() { UNIMPLEMENTED(); }
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
extern "C" {
|
||||
void __sanitizer_startup_hook(int argc, char **argv, char **envp,
|
||||
void *stack_base, size_t stack_size) {
|
||||
__sanitizer::StoredArgv = argv;
|
||||
__sanitizer::StoredEnviron = envp;
|
||||
__sanitizer::MainThreadStackBase = reinterpret_cast<uintptr_t>(stack_base);
|
||||
__sanitizer::MainThreadStackSize = stack_size;
|
||||
}
|
||||
|
||||
void __sanitizer_set_report_path(const char *path) {
|
||||
// Handle the initialization code in each sanitizer, but no other calls.
|
||||
// This setting is never consulted on Fuchsia.
|
||||
DCHECK_EQ(path, common_flags()->log_path);
|
||||
}
|
||||
|
||||
void __sanitizer_set_report_fd(void *fd) {
|
||||
UNREACHABLE("not available on Fuchsia");
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
#endif // SANITIZER_FUCHSIA
|
||||
36
lib/tsan/sanitizer_common/sanitizer_fuchsia.h
Normal file
36
lib/tsan/sanitizer_common/sanitizer_fuchsia.h
Normal file
@@ -0,0 +1,36 @@
|
||||
//===-- sanitizer_fuchsia.h ------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
//
|
||||
// Fuchsia-specific sanitizer support.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_FUCHSIA_H
|
||||
#define SANITIZER_FUCHSIA_H
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_FUCHSIA
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
|
||||
#include <zircon/sanitizer.h>
|
||||
#include <zircon/syscalls/object.h>
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
extern uptr MainThreadStackBase, MainThreadStackSize;
|
||||
extern sanitizer_shadow_bounds_t ShadowBounds;
|
||||
|
||||
struct MemoryMappingLayoutData {
|
||||
InternalMmapVector<zx_info_maps_t> data;
|
||||
size_t current; // Current index into the vector.
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_FUCHSIA
|
||||
#endif // SANITIZER_FUCHSIA_H
|
||||
59
lib/tsan/sanitizer_common/sanitizer_getauxval.h
Normal file
59
lib/tsan/sanitizer_common/sanitizer_getauxval.h
Normal file
@@ -0,0 +1,59 @@
|
||||
//===-- sanitizer_getauxval.h -----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Common getauxval() guards and definitions.
|
||||
// getauxval() is not defined until glibc version 2.16, or until API level 21
|
||||
// for Android.
|
||||
// Implement the getauxval() compat function for NetBSD.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_GETAUXVAL_H
|
||||
#define SANITIZER_GETAUXVAL_H
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#include "sanitizer_glibc_version.h"
|
||||
|
||||
#if SANITIZER_LINUX || SANITIZER_FUCHSIA
|
||||
|
||||
# if __GLIBC_PREREQ(2, 16) || (SANITIZER_ANDROID && __ANDROID_API__ >= 21) || \
|
||||
SANITIZER_FUCHSIA
|
||||
# define SANITIZER_USE_GETAUXVAL 1
|
||||
# else
|
||||
# define SANITIZER_USE_GETAUXVAL 0
|
||||
# endif
|
||||
|
||||
# if SANITIZER_USE_GETAUXVAL
|
||||
# include <sys/auxv.h>
|
||||
# else
|
||||
// The weak getauxval definition allows to check for the function at runtime.
|
||||
// This is useful for Android, when compiled at a lower API level yet running
|
||||
// on a more recent platform that offers the function.
|
||||
extern "C" SANITIZER_WEAK_ATTRIBUTE unsigned long getauxval(unsigned long type);
|
||||
# endif
|
||||
|
||||
#elif SANITIZER_NETBSD
|
||||
|
||||
#define SANITIZER_USE_GETAUXVAL 1
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <elf.h>
|
||||
|
||||
static inline decltype(AuxInfo::a_v) getauxval(decltype(AuxInfo::a_type) type) {
|
||||
for (const AuxInfo *aux = (const AuxInfo *)_dlauxinfo();
|
||||
aux->a_type != AT_NULL; ++aux) {
|
||||
if (type == aux->a_type)
|
||||
return aux->a_v;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // SANITIZER_GETAUXVAL_H
|
||||
26
lib/tsan/sanitizer_common/sanitizer_glibc_version.h
Normal file
26
lib/tsan/sanitizer_common/sanitizer_glibc_version.h
Normal file
@@ -0,0 +1,26 @@
|
||||
//===-- sanitizer_glibc_version.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer common code.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_GLIBC_VERSION_H
|
||||
#define SANITIZER_GLIBC_VERSION_H
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_LINUX || SANITIZER_FUCHSIA
|
||||
#include <features.h>
|
||||
#endif
|
||||
|
||||
#ifndef __GLIBC_PREREQ
|
||||
#define __GLIBC_PREREQ(x, y) 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
43
lib/tsan/sanitizer_common/sanitizer_hash.h
Normal file
43
lib/tsan/sanitizer_common/sanitizer_hash.h
Normal file
@@ -0,0 +1,43 @@
|
||||
//===-- sanitizer_common.h --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements a simple hash function.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_HASH_H
|
||||
#define SANITIZER_HASH_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
class MurMur2HashBuilder {
|
||||
static const u32 m = 0x5bd1e995;
|
||||
static const u32 seed = 0x9747b28c;
|
||||
static const u32 r = 24;
|
||||
u32 h;
|
||||
|
||||
public:
|
||||
explicit MurMur2HashBuilder(u32 init = 0) { h = seed ^ init; }
|
||||
void add(u32 k) {
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
h *= m;
|
||||
h ^= k;
|
||||
}
|
||||
u32 get() {
|
||||
u32 x = h;
|
||||
x ^= x >> 13;
|
||||
x *= m;
|
||||
x ^= x >> 15;
|
||||
return x;
|
||||
}
|
||||
};
|
||||
} //namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_HASH_H
|
||||
1535
lib/tsan/sanitizer_common/sanitizer_interceptors_ioctl_netbsd.inc
Normal file
1535
lib/tsan/sanitizer_common/sanitizer_interceptors_ioctl_netbsd.inc
Normal file
File diff suppressed because it is too large
Load Diff
118
lib/tsan/sanitizer_common/sanitizer_interface_internal.h
Normal file
118
lib/tsan/sanitizer_common/sanitizer_interface_internal.h
Normal file
@@ -0,0 +1,118 @@
|
||||
//===-- sanitizer_interface_internal.h --------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between run-time libraries of sanitizers.
|
||||
//
|
||||
// This header declares the sanitizer runtime interface functions.
|
||||
// The runtime library has to define these functions so the instrumented program
|
||||
// could call them.
|
||||
//
|
||||
// See also include/sanitizer/common_interface_defs.h
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_INTERFACE_INTERNAL_H
|
||||
#define SANITIZER_INTERFACE_INTERNAL_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
extern "C" {
|
||||
// Tell the tools to write their reports to "path.<pid>" instead of stderr.
|
||||
// The special values are "stdout" and "stderr".
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_set_report_path(const char *path);
|
||||
// Tell the tools to write their reports to the provided file descriptor
|
||||
// (casted to void *).
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_set_report_fd(void *fd);
|
||||
|
||||
typedef struct {
|
||||
int coverage_sandboxed;
|
||||
__sanitizer::sptr coverage_fd;
|
||||
unsigned int coverage_max_block_size;
|
||||
} __sanitizer_sandbox_arguments;
|
||||
|
||||
// Notify the tools that the sandbox is going to be turned on.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
|
||||
__sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
|
||||
|
||||
// This function is called by the tool when it has just finished reporting
|
||||
// an error. 'error_summary' is a one-line string that summarizes
|
||||
// the error message. This function can be overridden by the client.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_report_error_summary(const char *error_summary);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage(
|
||||
const __sanitizer::uptr *pcs, const __sanitizer::uptr len);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard);
|
||||
|
||||
// Returns 1 on the first call, then returns 0 thereafter. Called by the tool
|
||||
// to ensure only one report is printed when multiple errors occur
|
||||
// simultaneously.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_acquire_crash_state();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_annotate_contiguous_container(const void *beg,
|
||||
const void *end,
|
||||
const void *old_mid,
|
||||
const void *new_mid);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
|
||||
const void *end);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
const void *__sanitizer_contiguous_container_find_bad_address(
|
||||
const void *beg, const void *mid, const void *end);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __sanitizer_get_module_and_offset_for_pc(
|
||||
__sanitizer::uptr pc, char *module_path,
|
||||
__sanitizer::uptr module_path_len, __sanitizer::uptr *pc_offset);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_cmp();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_cmp1();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_cmp2();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_cmp4();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_cmp8();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_const_cmp1();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_const_cmp2();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_const_cmp4();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_const_cmp8();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_switch();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_div4();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_div8();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_gep();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_pc_indir();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_pc_guard(__sanitizer::u32*);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_trace_pc_guard_init(__sanitizer::u32*,
|
||||
__sanitizer::u32*);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_cov_8bit_counters_init();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
|
||||
__sanitizer_cov_bool_flag_init();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
|
||||
__sanitizer_cov_pcs_init();
|
||||
} // extern "C"
|
||||
|
||||
#endif // SANITIZER_INTERFACE_INTERNAL_H
|
||||
459
lib/tsan/sanitizer_common/sanitizer_internal_defs.h
Normal file
459
lib/tsan/sanitizer_common/sanitizer_internal_defs.h
Normal file
@@ -0,0 +1,459 @@
|
||||
//===-- sanitizer_internal_defs.h -------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer.
|
||||
// It contains macro used in run-time libraries code.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_DEFS_H
|
||||
#define SANITIZER_DEFS_H
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#ifndef SANITIZER_DEBUG
|
||||
# define SANITIZER_DEBUG 0
|
||||
#endif
|
||||
|
||||
#define SANITIZER_STRINGIFY_(S) #S
|
||||
#define SANITIZER_STRINGIFY(S) SANITIZER_STRINGIFY_(S)
|
||||
|
||||
// Only use SANITIZER_*ATTRIBUTE* before the function return type!
|
||||
#if SANITIZER_WINDOWS
|
||||
#if SANITIZER_IMPORT_INTERFACE
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllimport)
|
||||
#else
|
||||
# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport)
|
||||
#endif
|
||||
# define SANITIZER_WEAK_ATTRIBUTE
|
||||
#elif 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
|
||||
|
||||
// TLS is handled differently on different platforms
|
||||
#if SANITIZER_LINUX || SANITIZER_NETBSD || \
|
||||
SANITIZER_FREEBSD || SANITIZER_OPENBSD
|
||||
# define SANITIZER_TLS_INITIAL_EXEC_ATTRIBUTE \
|
||||
__attribute__((tls_model("initial-exec"))) thread_local
|
||||
#else
|
||||
# define SANITIZER_TLS_INITIAL_EXEC_ATTRIBUTE
|
||||
#endif
|
||||
|
||||
//--------------------------- WEAK FUNCTIONS ---------------------------------//
|
||||
// When working with weak functions, to simplify the code and make it more
|
||||
// portable, when possible define a default implementation using this macro:
|
||||
//
|
||||
// SANITIZER_INTERFACE_WEAK_DEF(<return_type>, <name>, <parameter list>)
|
||||
//
|
||||
// For example:
|
||||
// SANITIZER_INTERFACE_WEAK_DEF(bool, compare, int a, int b) { return a > b; }
|
||||
//
|
||||
#if SANITIZER_WINDOWS
|
||||
#include "sanitizer_win_defs.h"
|
||||
# define SANITIZER_INTERFACE_WEAK_DEF(ReturnType, Name, ...) \
|
||||
WIN_WEAK_EXPORT_DEF(ReturnType, Name, __VA_ARGS__)
|
||||
#else
|
||||
# define SANITIZER_INTERFACE_WEAK_DEF(ReturnType, Name, ...) \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE \
|
||||
ReturnType Name(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
// SANITIZER_SUPPORTS_WEAK_HOOKS means that we support real weak functions that
|
||||
// will evaluate to a null pointer when not defined.
|
||||
#ifndef SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
#if (SANITIZER_LINUX || SANITIZER_SOLARIS) && !SANITIZER_GO
|
||||
# define SANITIZER_SUPPORTS_WEAK_HOOKS 1
|
||||
// Before Xcode 4.5, the Darwin linker doesn't reliably support undefined
|
||||
// weak symbols. Mac OS X 10.9/Darwin 13 is the first release only supported
|
||||
// by Xcode >= 4.5.
|
||||
#elif SANITIZER_MAC && \
|
||||
__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1090 && !SANITIZER_GO
|
||||
# define SANITIZER_SUPPORTS_WEAK_HOOKS 1
|
||||
#else
|
||||
# define SANITIZER_SUPPORTS_WEAK_HOOKS 0
|
||||
#endif
|
||||
#endif // SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
// For some weak hooks that will be called very often and we want to avoid the
|
||||
// overhead of executing the default implementation when it is not necessary,
|
||||
// we can use the flag SANITIZER_SUPPORTS_WEAK_HOOKS to only define the default
|
||||
// implementation for platforms that doesn't support weak symbols. For example:
|
||||
//
|
||||
// #if !SANITIZER_SUPPORT_WEAK_HOOKS
|
||||
// SANITIZER_INTERFACE_WEAK_DEF(bool, compare_hook, int a, int b) {
|
||||
// return a > b;
|
||||
// }
|
||||
// #endif
|
||||
//
|
||||
// And then use it as: if (compare_hook) compare_hook(a, b);
|
||||
//----------------------------------------------------------------------------//
|
||||
|
||||
|
||||
// We can use .preinit_array section on Linux to call sanitizer initialization
|
||||
// functions very early in the process startup (unless PIC macro is defined).
|
||||
//
|
||||
// On FreeBSD, .preinit_array functions are called with rtld_bind_lock writer
|
||||
// lock held. It will lead to dead lock if unresolved PLT functions (which helds
|
||||
// rtld_bind_lock reader lock) are called inside .preinit_array functions.
|
||||
//
|
||||
// FIXME: do we have anything like this on Mac?
|
||||
#ifndef SANITIZER_CAN_USE_PREINIT_ARRAY
|
||||
#if ((SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_OPENBSD || \
|
||||
SANITIZER_FUCHSIA || SANITIZER_NETBSD) && !defined(PIC)
|
||||
#define SANITIZER_CAN_USE_PREINIT_ARRAY 1
|
||||
// Before Solaris 11.4, .preinit_array is fully supported only with GNU ld.
|
||||
// FIXME: Check for those conditions.
|
||||
#elif SANITIZER_SOLARIS && !defined(PIC)
|
||||
# define SANITIZER_CAN_USE_PREINIT_ARRAY 1
|
||||
#else
|
||||
# define SANITIZER_CAN_USE_PREINIT_ARRAY 0
|
||||
#endif
|
||||
#endif // SANITIZER_CAN_USE_PREINIT_ARRAY
|
||||
|
||||
// GCC does not understand __has_feature
|
||||
#if !defined(__has_feature)
|
||||
# define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
// Older GCCs do not understand __has_attribute.
|
||||
#if !defined(__has_attribute)
|
||||
# define __has_attribute(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;
|
||||
typedef signed long long sptr;
|
||||
#else
|
||||
typedef unsigned long uptr;
|
||||
typedef signed long sptr;
|
||||
#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;
|
||||
#else
|
||||
typedef uptr uhwptr;
|
||||
#endif
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef signed char s8;
|
||||
typedef signed short s16;
|
||||
typedef signed int s32;
|
||||
typedef signed long long s64;
|
||||
#if SANITIZER_WINDOWS
|
||||
// On Windows, files are HANDLE, which is a synonim of void*.
|
||||
// Use void* to avoid including <windows.h> everywhere.
|
||||
typedef void* fd_t;
|
||||
typedef unsigned error_t;
|
||||
#else
|
||||
typedef int fd_t;
|
||||
typedef int error_t;
|
||||
#endif
|
||||
#if SANITIZER_SOLARIS && !defined(_LP64)
|
||||
typedef long pid_t;
|
||||
#else
|
||||
typedef int pid_t;
|
||||
#endif
|
||||
|
||||
#if SANITIZER_FREEBSD || SANITIZER_NETBSD || \
|
||||
SANITIZER_OPENBSD || SANITIZER_MAC || \
|
||||
(SANITIZER_SOLARIS && (defined(_LP64) || _FILE_OFFSET_BITS == 64)) || \
|
||||
(SANITIZER_LINUX && defined(__x86_64__))
|
||||
typedef u64 OFF_T;
|
||||
#else
|
||||
typedef uptr OFF_T;
|
||||
#endif
|
||||
typedef u64 OFF64_T;
|
||||
|
||||
#if (SANITIZER_WORDSIZE == 64) || SANITIZER_MAC
|
||||
typedef uptr operator_new_size_type;
|
||||
#else
|
||||
# if SANITIZER_OPENBSD || defined(__s390__) && !defined(__s390x__)
|
||||
// Special case: 31-bit s390 has unsigned long as size_t.
|
||||
typedef unsigned long operator_new_size_type;
|
||||
# else
|
||||
typedef u32 operator_new_size_type;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef u64 tid_t;
|
||||
|
||||
// ----------- ATTENTION -------------
|
||||
// This header should NOT include any other headers to avoid portability issues.
|
||||
|
||||
// Common defs.
|
||||
#ifndef INLINE
|
||||
#define INLINE inline
|
||||
#endif
|
||||
#define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
|
||||
#define SANITIZER_WEAK_DEFAULT_IMPL \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE
|
||||
#define SANITIZER_WEAK_CXX_DEFAULT_IMPL \
|
||||
extern "C++" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE
|
||||
|
||||
// Platform-specific defs.
|
||||
#if defined(_MSC_VER)
|
||||
# define ALWAYS_INLINE __forceinline
|
||||
// FIXME(timurrrr): do we need this on Windows?
|
||||
# define ALIAS(x)
|
||||
# define ALIGNED(x) __declspec(align(x))
|
||||
# define FORMAT(f, a)
|
||||
# define NOINLINE __declspec(noinline)
|
||||
# define NORETURN __declspec(noreturn)
|
||||
# define THREADLOCAL __declspec(thread)
|
||||
# define LIKELY(x) (x)
|
||||
# define UNLIKELY(x) (x)
|
||||
# define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */ (void)0
|
||||
# define WARN_UNUSED_RESULT
|
||||
#else // _MSC_VER
|
||||
# define ALWAYS_INLINE inline __attribute__((always_inline))
|
||||
# define ALIAS(x) __attribute__((alias(x)))
|
||||
// Please only use the ALIGNED macro before the type.
|
||||
// Using ALIGNED after the variable declaration is not portable!
|
||||
# define ALIGNED(x) __attribute__((aligned(x)))
|
||||
# define FORMAT(f, a) __attribute__((format(printf, f, a)))
|
||||
# define NOINLINE __attribute__((noinline))
|
||||
# define NORETURN __attribute__((noreturn))
|
||||
# define THREADLOCAL __thread
|
||||
# define LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
# if defined(__i386__) || defined(__x86_64__)
|
||||
// __builtin_prefetch(x) generates prefetchnt0 on x86
|
||||
# define PREFETCH(x) __asm__("prefetchnta (%0)" : : "r" (x))
|
||||
# else
|
||||
# define PREFETCH(x) __builtin_prefetch(x)
|
||||
# endif
|
||||
# define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
#endif // _MSC_VER
|
||||
|
||||
#if !defined(_MSC_VER) || defined(__clang__)
|
||||
# define UNUSED __attribute__((unused))
|
||||
# define USED __attribute__((used))
|
||||
#else
|
||||
# define UNUSED
|
||||
# define USED
|
||||
#endif
|
||||
|
||||
#if !defined(_MSC_VER) || defined(__clang__) || MSC_PREREQ(1900)
|
||||
# define NOEXCEPT noexcept
|
||||
#else
|
||||
# define NOEXCEPT throw()
|
||||
#endif
|
||||
|
||||
// Unaligned versions of basic types.
|
||||
typedef ALIGNED(1) u16 uu16;
|
||||
typedef ALIGNED(1) u32 uu32;
|
||||
typedef ALIGNED(1) u64 uu64;
|
||||
typedef ALIGNED(1) s16 us16;
|
||||
typedef ALIGNED(1) s32 us32;
|
||||
typedef ALIGNED(1) s64 us64;
|
||||
|
||||
#if SANITIZER_WINDOWS
|
||||
} // namespace __sanitizer
|
||||
typedef unsigned long DWORD;
|
||||
namespace __sanitizer {
|
||||
typedef DWORD thread_return_t;
|
||||
# define THREAD_CALLING_CONV __stdcall
|
||||
#else // _WIN32
|
||||
typedef void* thread_return_t;
|
||||
# define THREAD_CALLING_CONV
|
||||
#endif // _WIN32
|
||||
typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg);
|
||||
|
||||
// NOTE: Functions below must be defined in each run-time.
|
||||
void NORETURN Die();
|
||||
|
||||
void NORETURN CheckFailed(const char *file, int line, const char *cond,
|
||||
u64 v1, u64 v2);
|
||||
|
||||
// Check macro
|
||||
#define RAW_CHECK_MSG(expr, msg) do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
RawWrite(msg); \
|
||||
Die(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RAW_CHECK(expr) RAW_CHECK_MSG(expr, #expr)
|
||||
|
||||
#define CHECK_IMPL(c1, op, c2) \
|
||||
do { \
|
||||
__sanitizer::u64 v1 = (__sanitizer::u64)(c1); \
|
||||
__sanitizer::u64 v2 = (__sanitizer::u64)(c2); \
|
||||
if (UNLIKELY(!(v1 op v2))) \
|
||||
__sanitizer::CheckFailed(__FILE__, __LINE__, \
|
||||
"(" #c1 ") " #op " (" #c2 ")", v1, v2); \
|
||||
} while (false) \
|
||||
/**/
|
||||
|
||||
#define CHECK(a) CHECK_IMPL((a), !=, 0)
|
||||
#define CHECK_EQ(a, b) CHECK_IMPL((a), ==, (b))
|
||||
#define CHECK_NE(a, b) CHECK_IMPL((a), !=, (b))
|
||||
#define CHECK_LT(a, b) CHECK_IMPL((a), <, (b))
|
||||
#define CHECK_LE(a, b) CHECK_IMPL((a), <=, (b))
|
||||
#define CHECK_GT(a, b) CHECK_IMPL((a), >, (b))
|
||||
#define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b))
|
||||
|
||||
#if SANITIZER_DEBUG
|
||||
#define DCHECK(a) CHECK(a)
|
||||
#define DCHECK_EQ(a, b) CHECK_EQ(a, b)
|
||||
#define DCHECK_NE(a, b) CHECK_NE(a, b)
|
||||
#define DCHECK_LT(a, b) CHECK_LT(a, b)
|
||||
#define DCHECK_LE(a, b) CHECK_LE(a, b)
|
||||
#define DCHECK_GT(a, b) CHECK_GT(a, b)
|
||||
#define DCHECK_GE(a, b) CHECK_GE(a, b)
|
||||
#else
|
||||
#define DCHECK(a)
|
||||
#define DCHECK_EQ(a, b)
|
||||
#define DCHECK_NE(a, b)
|
||||
#define DCHECK_LT(a, b)
|
||||
#define DCHECK_LE(a, b)
|
||||
#define DCHECK_GT(a, b)
|
||||
#define DCHECK_GE(a, b)
|
||||
#endif
|
||||
|
||||
#define UNREACHABLE(msg) do { \
|
||||
CHECK(0 && msg); \
|
||||
Die(); \
|
||||
} while (0)
|
||||
|
||||
#define UNIMPLEMENTED() UNREACHABLE("unimplemented")
|
||||
|
||||
#define COMPILER_CHECK(pred) IMPL_COMPILER_ASSERT(pred, __LINE__)
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
|
||||
|
||||
#define IMPL_PASTE(a, b) a##b
|
||||
#define IMPL_COMPILER_ASSERT(pred, line) \
|
||||
typedef char IMPL_PASTE(assertion_failed_##_, line)[2*(int)(pred)-1]
|
||||
|
||||
// Limits for integral types. We have to redefine it in case we don't
|
||||
// have stdint.h (like in Visual Studio 9).
|
||||
#undef __INT64_C
|
||||
#undef __UINT64_C
|
||||
#if SANITIZER_WORDSIZE == 64
|
||||
# define __INT64_C(c) c ## L
|
||||
# define __UINT64_C(c) c ## UL
|
||||
#else
|
||||
# define __INT64_C(c) c ## LL
|
||||
# define __UINT64_C(c) c ## ULL
|
||||
#endif // SANITIZER_WORDSIZE == 64
|
||||
#undef INT32_MIN
|
||||
#define INT32_MIN (-2147483647-1)
|
||||
#undef INT32_MAX
|
||||
#define INT32_MAX (2147483647)
|
||||
#undef UINT32_MAX
|
||||
#define UINT32_MAX (4294967295U)
|
||||
#undef INT64_MIN
|
||||
#define INT64_MIN (-__INT64_C(9223372036854775807)-1)
|
||||
#undef INT64_MAX
|
||||
#define INT64_MAX (__INT64_C(9223372036854775807))
|
||||
#undef UINT64_MAX
|
||||
#define UINT64_MAX (__UINT64_C(18446744073709551615))
|
||||
#undef UINTPTR_MAX
|
||||
#if SANITIZER_WORDSIZE == 64
|
||||
# define UINTPTR_MAX (18446744073709551615UL)
|
||||
#else
|
||||
# define UINTPTR_MAX (4294967295U)
|
||||
#endif // SANITIZER_WORDSIZE == 64
|
||||
|
||||
enum LinkerInitialized { LINKER_INITIALIZED = 0 };
|
||||
|
||||
#if !defined(_MSC_VER) || defined(__clang__)
|
||||
#if SANITIZER_S390_31
|
||||
#define GET_CALLER_PC() \
|
||||
(__sanitizer::uptr) __builtin_extract_return_addr(__builtin_return_address(0))
|
||||
#else
|
||||
#define GET_CALLER_PC() (__sanitizer::uptr) __builtin_return_address(0)
|
||||
#endif
|
||||
#define GET_CURRENT_FRAME() (__sanitizer::uptr) __builtin_frame_address(0)
|
||||
inline void Trap() {
|
||||
__builtin_trap();
|
||||
}
|
||||
#else
|
||||
extern "C" void* _ReturnAddress(void);
|
||||
extern "C" void* _AddressOfReturnAddress(void);
|
||||
# pragma intrinsic(_ReturnAddress)
|
||||
# pragma intrinsic(_AddressOfReturnAddress)
|
||||
#define GET_CALLER_PC() (__sanitizer::uptr) _ReturnAddress()
|
||||
// CaptureStackBackTrace doesn't need to know BP on Windows.
|
||||
#define GET_CURRENT_FRAME() \
|
||||
(((__sanitizer::uptr)_AddressOfReturnAddress()) + sizeof(__sanitizer::uptr))
|
||||
|
||||
extern "C" void __ud2(void);
|
||||
# pragma intrinsic(__ud2)
|
||||
inline void Trap() {
|
||||
__ud2();
|
||||
}
|
||||
#endif
|
||||
|
||||
#define HANDLE_EINTR(res, f) \
|
||||
{ \
|
||||
int rverrno; \
|
||||
do { \
|
||||
res = (f); \
|
||||
} while (internal_iserror(res, &rverrno) && rverrno == EINTR); \
|
||||
}
|
||||
|
||||
// Forces the compiler to generate a frame pointer in the function.
|
||||
#define ENABLE_FRAME_POINTER \
|
||||
do { \
|
||||
volatile __sanitizer::uptr enable_fp; \
|
||||
enable_fp = GET_CURRENT_FRAME(); \
|
||||
(void)enable_fp; \
|
||||
} while (0)
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
namespace __asan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __dsan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __dfsan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __lsan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __msan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __hwasan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __tsan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __scudo {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __ubsan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __xray {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __interception {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
namespace __hwasan {
|
||||
using namespace __sanitizer;
|
||||
}
|
||||
|
||||
#endif // SANITIZER_DEFS_H
|
||||
72
lib/tsan/sanitizer_common/sanitizer_lfstack.h
Normal file
72
lib/tsan/sanitizer_common/sanitizer_lfstack.h
Normal file
@@ -0,0 +1,72 @@
|
||||
//===-- sanitizer_lfstack.h -=-----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Lock-free stack.
|
||||
// Uses 32/17 bits as ABA-counter on 32/64-bit platforms.
|
||||
// The memory passed to Push() must not be ever munmap'ed.
|
||||
// The type T must contain T *next field.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_LFSTACK_H
|
||||
#define SANITIZER_LFSTACK_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_atomic.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
template<typename T>
|
||||
struct LFStack {
|
||||
void Clear() {
|
||||
atomic_store(&head_, 0, memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool Empty() const {
|
||||
return (atomic_load(&head_, memory_order_relaxed) & kPtrMask) == 0;
|
||||
}
|
||||
|
||||
void Push(T *p) {
|
||||
u64 cmp = atomic_load(&head_, memory_order_relaxed);
|
||||
for (;;) {
|
||||
u64 cnt = (cmp & kCounterMask) + kCounterInc;
|
||||
u64 xch = (u64)(uptr)p | cnt;
|
||||
p->next = (T*)(uptr)(cmp & kPtrMask);
|
||||
if (atomic_compare_exchange_weak(&head_, &cmp, xch,
|
||||
memory_order_release))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
T *Pop() {
|
||||
u64 cmp = atomic_load(&head_, memory_order_acquire);
|
||||
for (;;) {
|
||||
T *cur = (T*)(uptr)(cmp & kPtrMask);
|
||||
if (!cur)
|
||||
return nullptr;
|
||||
T *nxt = cur->next;
|
||||
u64 cnt = (cmp & kCounterMask);
|
||||
u64 xch = (u64)(uptr)nxt | cnt;
|
||||
if (atomic_compare_exchange_weak(&head_, &cmp, xch,
|
||||
memory_order_acquire))
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
// private:
|
||||
static const int kCounterBits = FIRST_32_SECOND_64(32, 17);
|
||||
static const u64 kPtrMask = ((u64)-1) >> kCounterBits;
|
||||
static const u64 kCounterMask = ~kPtrMask;
|
||||
static const u64 kCounterInc = kPtrMask + 1;
|
||||
|
||||
atomic_uint64_t head_;
|
||||
};
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_LFSTACK_H
|
||||
280
lib/tsan/sanitizer_common/sanitizer_libc.cpp
Normal file
280
lib/tsan/sanitizer_common/sanitizer_libc.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
//===-- sanitizer_libc.cpp ------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries. See sanitizer_libc.h for details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_libc.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
s64 internal_atoll(const char *nptr) {
|
||||
return internal_simple_strtoll(nptr, nullptr, 10);
|
||||
}
|
||||
|
||||
void *internal_memchr(const void *s, int c, uptr n) {
|
||||
const char *t = (const char *)s;
|
||||
for (uptr i = 0; i < n; ++i, ++t)
|
||||
if (*t == c)
|
||||
return reinterpret_cast<void *>(const_cast<char *>(t));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
for (uptr i = 0; i < n; ++i, ++t1, ++t2)
|
||||
if (*t1 != *t2)
|
||||
return *t1 < *t2 ? -1 : 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *internal_memcpy(void *dest, const void *src, uptr n) {
|
||||
char *d = (char*)dest;
|
||||
const char *s = (const char *)src;
|
||||
for (uptr i = 0; i < n; ++i)
|
||||
d[i] = s[i];
|
||||
return dest;
|
||||
}
|
||||
|
||||
void *internal_memmove(void *dest, const void *src, uptr n) {
|
||||
char *d = (char*)dest;
|
||||
const char *s = (const char *)src;
|
||||
sptr i, signed_n = (sptr)n;
|
||||
CHECK_GE(signed_n, 0);
|
||||
if (d < s) {
|
||||
for (i = 0; i < signed_n; ++i)
|
||||
d[i] = s[i];
|
||||
} else {
|
||||
if (d > s && signed_n > 0) {
|
||||
for (i = signed_n - 1; i >= 0; --i) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
void *internal_memset(void* s, int c, uptr n) {
|
||||
// Optimize for the most performance-critical case:
|
||||
if ((reinterpret_cast<uptr>(s) % 16) == 0 && (n % 16) == 0) {
|
||||
u64 *p = reinterpret_cast<u64*>(s);
|
||||
u64 *e = p + n / 8;
|
||||
u64 v = c;
|
||||
v |= v << 8;
|
||||
v |= v << 16;
|
||||
v |= v << 32;
|
||||
for (; p < e; p += 2)
|
||||
p[0] = p[1] = v;
|
||||
return s;
|
||||
}
|
||||
// The next line prevents Clang from making a call to memset() instead of the
|
||||
// loop below.
|
||||
// FIXME: building the runtime with -ffreestanding is a better idea. However
|
||||
// there currently are linktime problems due to PR12396.
|
||||
char volatile *t = (char*)s;
|
||||
for (uptr i = 0; i < n; ++i, ++t) {
|
||||
*t = c;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
uptr internal_strcspn(const char *s, const char *reject) {
|
||||
uptr i;
|
||||
for (i = 0; s[i]; i++) {
|
||||
if (internal_strchr(reject, s[i]))
|
||||
return i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
char* internal_strdup(const char *s) {
|
||||
uptr len = internal_strlen(s);
|
||||
char *s2 = (char*)InternalAlloc(len + 1);
|
||||
internal_memcpy(s2, s, len);
|
||||
s2[len] = 0;
|
||||
return s2;
|
||||
}
|
||||
|
||||
int internal_strcmp(const char *s1, const char *s2) {
|
||||
while (true) {
|
||||
unsigned c1 = *s1;
|
||||
unsigned c2 = *s2;
|
||||
if (c1 != c2) return (c1 < c2) ? -1 : 1;
|
||||
if (c1 == 0) break;
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int internal_strncmp(const char *s1, const char *s2, uptr n) {
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
unsigned c1 = *s1;
|
||||
unsigned c2 = *s2;
|
||||
if (c1 != c2) return (c1 < c2) ? -1 : 1;
|
||||
if (c1 == 0) break;
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* internal_strchr(const char *s, int c) {
|
||||
while (true) {
|
||||
if (*s == (char)c)
|
||||
return const_cast<char *>(s);
|
||||
if (*s == 0)
|
||||
return nullptr;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
char *internal_strchrnul(const char *s, int c) {
|
||||
char *res = internal_strchr(s, c);
|
||||
if (!res)
|
||||
res = const_cast<char *>(s) + internal_strlen(s);
|
||||
return res;
|
||||
}
|
||||
|
||||
char *internal_strrchr(const char *s, int c) {
|
||||
const char *res = nullptr;
|
||||
for (uptr i = 0; s[i]; i++) {
|
||||
if (s[i] == c) res = s + i;
|
||||
}
|
||||
return const_cast<char *>(res);
|
||||
}
|
||||
|
||||
uptr internal_strlen(const char *s) {
|
||||
uptr i = 0;
|
||||
while (s[i]) i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
uptr internal_strlcat(char *dst, const char *src, uptr maxlen) {
|
||||
const uptr srclen = internal_strlen(src);
|
||||
const uptr dstlen = internal_strnlen(dst, maxlen);
|
||||
if (dstlen == maxlen) return maxlen + srclen;
|
||||
if (srclen < maxlen - dstlen) {
|
||||
internal_memmove(dst + dstlen, src, srclen + 1);
|
||||
} else {
|
||||
internal_memmove(dst + dstlen, src, maxlen - dstlen - 1);
|
||||
dst[maxlen - 1] = '\0';
|
||||
}
|
||||
return dstlen + srclen;
|
||||
}
|
||||
|
||||
char *internal_strncat(char *dst, const char *src, uptr n) {
|
||||
uptr len = internal_strlen(dst);
|
||||
uptr i;
|
||||
for (i = 0; i < n && src[i]; i++)
|
||||
dst[len + i] = src[i];
|
||||
dst[len + i] = 0;
|
||||
return dst;
|
||||
}
|
||||
|
||||
uptr internal_strlcpy(char *dst, const char *src, uptr maxlen) {
|
||||
const uptr srclen = internal_strlen(src);
|
||||
if (srclen < maxlen) {
|
||||
internal_memmove(dst, src, srclen + 1);
|
||||
} else if (maxlen != 0) {
|
||||
internal_memmove(dst, src, maxlen - 1);
|
||||
dst[maxlen - 1] = '\0';
|
||||
}
|
||||
return srclen;
|
||||
}
|
||||
|
||||
char *internal_strncpy(char *dst, const char *src, uptr n) {
|
||||
uptr i;
|
||||
for (i = 0; i < n && src[i]; i++)
|
||||
dst[i] = src[i];
|
||||
internal_memset(dst + i, '\0', n - i);
|
||||
return dst;
|
||||
}
|
||||
|
||||
uptr internal_strnlen(const char *s, uptr maxlen) {
|
||||
uptr i = 0;
|
||||
while (i < maxlen && s[i]) i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
char *internal_strstr(const char *haystack, const char *needle) {
|
||||
// This is O(N^2), but we are not using it in hot places.
|
||||
uptr len1 = internal_strlen(haystack);
|
||||
uptr len2 = internal_strlen(needle);
|
||||
if (len1 < len2) return nullptr;
|
||||
for (uptr pos = 0; pos <= len1 - len2; pos++) {
|
||||
if (internal_memcmp(haystack + pos, needle, len2) == 0)
|
||||
return const_cast<char *>(haystack) + pos;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s64 internal_simple_strtoll(const char *nptr, const char **endptr, int base) {
|
||||
CHECK_EQ(base, 10);
|
||||
while (IsSpace(*nptr)) nptr++;
|
||||
int sgn = 1;
|
||||
u64 res = 0;
|
||||
bool have_digits = false;
|
||||
char *old_nptr = const_cast<char *>(nptr);
|
||||
if (*nptr == '+') {
|
||||
sgn = 1;
|
||||
nptr++;
|
||||
} else if (*nptr == '-') {
|
||||
sgn = -1;
|
||||
nptr++;
|
||||
}
|
||||
while (IsDigit(*nptr)) {
|
||||
res = (res <= UINT64_MAX / 10) ? res * 10 : UINT64_MAX;
|
||||
int digit = ((*nptr) - '0');
|
||||
res = (res <= UINT64_MAX - digit) ? res + digit : UINT64_MAX;
|
||||
have_digits = true;
|
||||
nptr++;
|
||||
}
|
||||
if (endptr) {
|
||||
*endptr = (have_digits) ? const_cast<char *>(nptr) : old_nptr;
|
||||
}
|
||||
if (sgn > 0) {
|
||||
return (s64)(Min((u64)INT64_MAX, res));
|
||||
} else {
|
||||
return (res > INT64_MAX) ? INT64_MIN : ((s64)res * -1);
|
||||
}
|
||||
}
|
||||
|
||||
bool mem_is_zero(const char *beg, uptr size) {
|
||||
CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40)); // Sanity check.
|
||||
const char *end = beg + size;
|
||||
uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr));
|
||||
uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr));
|
||||
uptr all = 0;
|
||||
// Prologue.
|
||||
for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++)
|
||||
all |= *mem;
|
||||
// Aligned loop.
|
||||
for (; aligned_beg < aligned_end; aligned_beg++)
|
||||
all |= *aligned_beg;
|
||||
// Epilogue.
|
||||
if ((char *)aligned_end >= beg) {
|
||||
for (const char *mem = (char *)aligned_end; mem < end; mem++) all |= *mem;
|
||||
}
|
||||
return all == 0;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
85
lib/tsan/sanitizer_common/sanitizer_libc.h
Normal file
85
lib/tsan/sanitizer_common/sanitizer_libc.h
Normal file
@@ -0,0 +1,85 @@
|
||||
//===-- sanitizer_libc.h ----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries.
|
||||
// These tools can not use some of the libc functions directly because those
|
||||
// functions are intercepted. Instead, we implement a tiny subset of libc here.
|
||||
// FIXME: Some of functions declared in this file are in fact POSIX, not libc.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_LIBC_H
|
||||
#define SANITIZER_LIBC_H
|
||||
|
||||
// ----------- ATTENTION -------------
|
||||
// This header should NOT include any other headers from sanitizer runtime.
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// internal_X() is a custom implementation of X() for use in RTL.
|
||||
|
||||
// String functions
|
||||
s64 internal_atoll(const char *nptr);
|
||||
void *internal_memchr(const void *s, int c, uptr n);
|
||||
void *internal_memrchr(const void *s, int c, uptr n);
|
||||
int internal_memcmp(const void* s1, const void* s2, uptr n);
|
||||
void *internal_memcpy(void *dest, const void *src, uptr n);
|
||||
void *internal_memmove(void *dest, const void *src, uptr n);
|
||||
// Should not be used in performance-critical places.
|
||||
void *internal_memset(void *s, int c, uptr n);
|
||||
char* internal_strchr(const char *s, int c);
|
||||
char *internal_strchrnul(const char *s, int c);
|
||||
int internal_strcmp(const char *s1, const char *s2);
|
||||
uptr internal_strcspn(const char *s, const char *reject);
|
||||
char *internal_strdup(const char *s);
|
||||
uptr internal_strlen(const char *s);
|
||||
uptr internal_strlcat(char *dst, const char *src, uptr maxlen);
|
||||
char *internal_strncat(char *dst, const char *src, uptr n);
|
||||
int internal_strncmp(const char *s1, const char *s2, uptr n);
|
||||
uptr internal_strlcpy(char *dst, const char *src, uptr maxlen);
|
||||
char *internal_strncpy(char *dst, const char *src, uptr n);
|
||||
uptr internal_strnlen(const char *s, uptr maxlen);
|
||||
char *internal_strrchr(const char *s, int c);
|
||||
char *internal_strstr(const char *haystack, const char *needle);
|
||||
// Works only for base=10 and doesn't set errno.
|
||||
s64 internal_simple_strtoll(const char *nptr, const char **endptr, int base);
|
||||
int internal_snprintf(char *buffer, uptr length, const char *format, ...);
|
||||
|
||||
// Return true if all bytes in [mem, mem+size) are zero.
|
||||
// Optimized for the case when the result is true.
|
||||
bool mem_is_zero(const char *mem, uptr size);
|
||||
|
||||
// I/O
|
||||
// Define these as macros so we can use them in linker initialized global
|
||||
// structs without dynamic initialization.
|
||||
#define kInvalidFd ((fd_t)-1)
|
||||
#define kStdinFd ((fd_t)0)
|
||||
#define kStdoutFd ((fd_t)1)
|
||||
#define kStderrFd ((fd_t)2)
|
||||
|
||||
uptr internal_ftruncate(fd_t fd, uptr size);
|
||||
|
||||
// OS
|
||||
void NORETURN internal__exit(int exitcode);
|
||||
unsigned int internal_sleep(unsigned int seconds);
|
||||
|
||||
uptr internal_getpid();
|
||||
uptr internal_getppid();
|
||||
|
||||
int internal_dlinfo(void *handle, int request, void *p);
|
||||
|
||||
// Threading
|
||||
uptr internal_sched_yield();
|
||||
|
||||
// Error handling
|
||||
bool internal_iserror(uptr retval, int *rverrno = nullptr);
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_LIBC_H
|
||||
129
lib/tsan/sanitizer_common/sanitizer_libignore.cpp
Normal file
129
lib/tsan/sanitizer_common/sanitizer_libignore.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
//===-- sanitizer_libignore.cpp -------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC || \
|
||||
SANITIZER_NETBSD || SANITIZER_OPENBSD
|
||||
|
||||
#include "sanitizer_libignore.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_posix.h"
|
||||
#include "sanitizer_procmaps.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
LibIgnore::LibIgnore(LinkerInitialized) {
|
||||
}
|
||||
|
||||
void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
|
||||
BlockingMutexLock lock(&mutex_);
|
||||
if (count_ >= kMaxLibs) {
|
||||
Report("%s: too many ignored libraries (max: %d)\n", SanitizerToolName,
|
||||
kMaxLibs);
|
||||
Die();
|
||||
}
|
||||
Lib *lib = &libs_[count_++];
|
||||
lib->templ = internal_strdup(name_templ);
|
||||
lib->name = nullptr;
|
||||
lib->real_name = nullptr;
|
||||
lib->loaded = false;
|
||||
}
|
||||
|
||||
void LibIgnore::OnLibraryLoaded(const char *name) {
|
||||
BlockingMutexLock lock(&mutex_);
|
||||
// Try to match suppressions with symlink target.
|
||||
InternalScopedString buf(kMaxPathLength);
|
||||
if (name && internal_readlink(name, buf.data(), buf.size() - 1) > 0 &&
|
||||
buf[0]) {
|
||||
for (uptr i = 0; i < count_; i++) {
|
||||
Lib *lib = &libs_[i];
|
||||
if (!lib->loaded && (!lib->real_name) &&
|
||||
TemplateMatch(lib->templ, name))
|
||||
lib->real_name = internal_strdup(buf.data());
|
||||
}
|
||||
}
|
||||
|
||||
// Scan suppressions list and find newly loaded and unloaded libraries.
|
||||
ListOfModules modules;
|
||||
modules.init();
|
||||
for (uptr i = 0; i < count_; i++) {
|
||||
Lib *lib = &libs_[i];
|
||||
bool loaded = false;
|
||||
for (const auto &mod : modules) {
|
||||
for (const auto &range : mod.ranges()) {
|
||||
if (!range.executable)
|
||||
continue;
|
||||
if (!TemplateMatch(lib->templ, mod.full_name()) &&
|
||||
!(lib->real_name &&
|
||||
internal_strcmp(lib->real_name, mod.full_name()) == 0))
|
||||
continue;
|
||||
if (loaded) {
|
||||
Report("%s: called_from_lib suppression '%s' is matched against"
|
||||
" 2 libraries: '%s' and '%s'\n",
|
||||
SanitizerToolName, lib->templ, lib->name, mod.full_name());
|
||||
Die();
|
||||
}
|
||||
loaded = true;
|
||||
if (lib->loaded)
|
||||
continue;
|
||||
VReport(1,
|
||||
"Matched called_from_lib suppression '%s' against library"
|
||||
" '%s'\n",
|
||||
lib->templ, mod.full_name());
|
||||
lib->loaded = true;
|
||||
lib->name = internal_strdup(mod.full_name());
|
||||
const uptr idx =
|
||||
atomic_load(&ignored_ranges_count_, memory_order_relaxed);
|
||||
CHECK_LT(idx, ARRAY_SIZE(ignored_code_ranges_));
|
||||
ignored_code_ranges_[idx].begin = range.beg;
|
||||
ignored_code_ranges_[idx].end = range.end;
|
||||
atomic_store(&ignored_ranges_count_, idx + 1, memory_order_release);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lib->loaded && !loaded) {
|
||||
Report("%s: library '%s' that was matched against called_from_lib"
|
||||
" suppression '%s' is unloaded\n",
|
||||
SanitizerToolName, lib->name, lib->templ);
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
// Track instrumented ranges.
|
||||
if (track_instrumented_libs_) {
|
||||
for (const auto &mod : modules) {
|
||||
if (!mod.instrumented())
|
||||
continue;
|
||||
for (const auto &range : mod.ranges()) {
|
||||
if (!range.executable)
|
||||
continue;
|
||||
if (IsPcInstrumented(range.beg) && IsPcInstrumented(range.end - 1))
|
||||
continue;
|
||||
VReport(1, "Adding instrumented range %p-%p from library '%s'\n",
|
||||
range.beg, range.end, mod.full_name());
|
||||
const uptr idx =
|
||||
atomic_load(&instrumented_ranges_count_, memory_order_relaxed);
|
||||
CHECK_LT(idx, ARRAY_SIZE(instrumented_code_ranges_));
|
||||
instrumented_code_ranges_[idx].begin = range.beg;
|
||||
instrumented_code_ranges_[idx].end = range.end;
|
||||
atomic_store(&instrumented_ranges_count_, idx + 1,
|
||||
memory_order_release);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LibIgnore::OnLibraryUnloaded() {
|
||||
OnLibraryLoaded(nullptr);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC ||
|
||||
// SANITIZER_NETBSD
|
||||
115
lib/tsan/sanitizer_common/sanitizer_libignore.h
Normal file
115
lib/tsan/sanitizer_common/sanitizer_libignore.h
Normal file
@@ -0,0 +1,115 @@
|
||||
//===-- sanitizer_libignore.h -----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// LibIgnore allows to ignore all interceptors called from a particular set
|
||||
// of dynamic libraries. LibIgnore can be initialized with several templates
|
||||
// of names of libraries to be ignored. It finds code ranges for the libraries;
|
||||
// and checks whether the provided PC value belongs to the code ranges.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_LIBIGNORE_H
|
||||
#define SANITIZER_LIBIGNORE_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_atomic.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
class LibIgnore {
|
||||
public:
|
||||
explicit LibIgnore(LinkerInitialized);
|
||||
|
||||
// Must be called during initialization.
|
||||
void AddIgnoredLibrary(const char *name_templ);
|
||||
void IgnoreNoninstrumentedModules(bool enable) {
|
||||
track_instrumented_libs_ = enable;
|
||||
}
|
||||
|
||||
// Must be called after a new dynamic library is loaded.
|
||||
void OnLibraryLoaded(const char *name);
|
||||
|
||||
// Must be called after a dynamic library is unloaded.
|
||||
void OnLibraryUnloaded();
|
||||
|
||||
// Checks whether the provided PC belongs to one of the ignored libraries or
|
||||
// the PC should be ignored because it belongs to an non-instrumented module
|
||||
// (when ignore_noninstrumented_modules=1). Also returns true via
|
||||
// "pc_in_ignored_lib" if the PC is in an ignored library, false otherwise.
|
||||
bool IsIgnored(uptr pc, bool *pc_in_ignored_lib) const;
|
||||
|
||||
// Checks whether the provided PC belongs to an instrumented module.
|
||||
bool IsPcInstrumented(uptr pc) const;
|
||||
|
||||
private:
|
||||
struct Lib {
|
||||
char *templ;
|
||||
char *name;
|
||||
char *real_name; // target of symlink
|
||||
bool loaded;
|
||||
};
|
||||
|
||||
struct LibCodeRange {
|
||||
uptr begin;
|
||||
uptr end;
|
||||
};
|
||||
|
||||
inline bool IsInRange(uptr pc, const LibCodeRange &range) const {
|
||||
return (pc >= range.begin && pc < range.end);
|
||||
}
|
||||
|
||||
static const uptr kMaxIgnoredRanges = 128;
|
||||
static const uptr kMaxInstrumentedRanges = 1024;
|
||||
static const uptr kMaxLibs = 1024;
|
||||
|
||||
// Hot part:
|
||||
atomic_uintptr_t ignored_ranges_count_;
|
||||
LibCodeRange ignored_code_ranges_[kMaxIgnoredRanges];
|
||||
|
||||
atomic_uintptr_t instrumented_ranges_count_;
|
||||
LibCodeRange instrumented_code_ranges_[kMaxInstrumentedRanges];
|
||||
|
||||
// Cold part:
|
||||
BlockingMutex mutex_;
|
||||
uptr count_;
|
||||
Lib libs_[kMaxLibs];
|
||||
bool track_instrumented_libs_;
|
||||
|
||||
// Disallow copying of LibIgnore objects.
|
||||
LibIgnore(const LibIgnore&); // not implemented
|
||||
void operator = (const LibIgnore&); // not implemented
|
||||
};
|
||||
|
||||
inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
|
||||
const uptr n = atomic_load(&ignored_ranges_count_, memory_order_acquire);
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
if (IsInRange(pc, ignored_code_ranges_[i])) {
|
||||
*pc_in_ignored_lib = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*pc_in_ignored_lib = false;
|
||||
if (track_instrumented_libs_ && !IsPcInstrumented(pc))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool LibIgnore::IsPcInstrumented(uptr pc) const {
|
||||
const uptr n = atomic_load(&instrumented_ranges_count_, memory_order_acquire);
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
if (IsInRange(pc, instrumented_code_ranges_[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_LIBIGNORE_H
|
||||
2269
lib/tsan/sanitizer_common/sanitizer_linux.cpp
Normal file
2269
lib/tsan/sanitizer_common/sanitizer_linux.cpp
Normal file
File diff suppressed because it is too large
Load Diff
162
lib/tsan/sanitizer_common/sanitizer_linux.h
Normal file
162
lib/tsan/sanitizer_common/sanitizer_linux.h
Normal file
@@ -0,0 +1,162 @@
|
||||
//===-- sanitizer_linux.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Linux-specific syscall wrappers and classes.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_LINUX_H
|
||||
#define SANITIZER_LINUX_H
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
|
||||
SANITIZER_OPENBSD || SANITIZER_SOLARIS
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_platform_limits_freebsd.h"
|
||||
#include "sanitizer_platform_limits_netbsd.h"
|
||||
#include "sanitizer_platform_limits_openbsd.h"
|
||||
#include "sanitizer_platform_limits_posix.h"
|
||||
#include "sanitizer_platform_limits_solaris.h"
|
||||
#include "sanitizer_posix.h"
|
||||
|
||||
struct link_map; // Opaque type returned by dlopen().
|
||||
struct utsname;
|
||||
|
||||
namespace __sanitizer {
|
||||
// Dirent structure for getdents(). Note that this structure is different from
|
||||
// the one in <dirent.h>, which is used by readdir().
|
||||
struct linux_dirent;
|
||||
|
||||
struct ProcSelfMapsBuff {
|
||||
char *data;
|
||||
uptr mmaped_size;
|
||||
uptr len;
|
||||
};
|
||||
|
||||
struct MemoryMappingLayoutData {
|
||||
ProcSelfMapsBuff proc_self_maps;
|
||||
const char *current;
|
||||
};
|
||||
|
||||
void ReadProcMaps(ProcSelfMapsBuff *proc_maps);
|
||||
|
||||
// Syscall wrappers.
|
||||
uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count);
|
||||
uptr internal_sigaltstack(const void* ss, void* oss);
|
||||
uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
|
||||
__sanitizer_sigset_t *oldset);
|
||||
uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp);
|
||||
|
||||
// Linux-only syscalls.
|
||||
#if SANITIZER_LINUX
|
||||
uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
|
||||
// Used only by sanitizer_stoptheworld. Signal handlers that are actually used
|
||||
// (like the process-wide error reporting SEGV handler) must use
|
||||
// internal_sigaction instead.
|
||||
int internal_sigaction_norestorer(int signum, const void *act, void *oldact);
|
||||
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
|
||||
#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) \
|
||||
|| defined(__powerpc64__) || defined(__s390__) || defined(__i386__) \
|
||||
|| defined(__arm__)
|
||||
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
int *parent_tidptr, void *newtls, int *child_tidptr);
|
||||
#endif
|
||||
int internal_uname(struct utsname *buf);
|
||||
#elif SANITIZER_FREEBSD
|
||||
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
|
||||
#elif SANITIZER_NETBSD
|
||||
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
|
||||
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
|
||||
#endif // SANITIZER_LINUX
|
||||
|
||||
// This class reads thread IDs from /proc/<pid>/task using only syscalls.
|
||||
class ThreadLister {
|
||||
public:
|
||||
explicit ThreadLister(pid_t pid);
|
||||
~ThreadLister();
|
||||
enum Result {
|
||||
Error,
|
||||
Incomplete,
|
||||
Ok,
|
||||
};
|
||||
Result ListThreads(InternalMmapVector<tid_t> *threads);
|
||||
|
||||
private:
|
||||
bool IsAlive(int tid);
|
||||
|
||||
pid_t pid_;
|
||||
int descriptor_ = -1;
|
||||
InternalMmapVector<char> buffer_;
|
||||
};
|
||||
|
||||
// Exposed for testing.
|
||||
uptr ThreadDescriptorSize();
|
||||
uptr ThreadSelf();
|
||||
uptr ThreadSelfOffset();
|
||||
|
||||
// Matches a library's file name against a base name (stripping path and version
|
||||
// information).
|
||||
bool LibraryNameIs(const char *full_name, const char *base_name);
|
||||
|
||||
// Call cb for each region mapped by map.
|
||||
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr));
|
||||
|
||||
// Releases memory pages entirely within the [beg, end] address range.
|
||||
// The pages no longer count toward RSS; reads are guaranteed to return 0.
|
||||
// Requires (but does not verify!) that pages are MAP_PRIVATE.
|
||||
INLINE void ReleaseMemoryPagesToOSAndZeroFill(uptr beg, uptr end) {
|
||||
// man madvise on Linux promises zero-fill for anonymous private pages.
|
||||
// Testing shows the same behaviour for private (but not anonymous) mappings
|
||||
// of shm_open() files, as long as the underlying file is untouched.
|
||||
CHECK(SANITIZER_LINUX);
|
||||
ReleaseMemoryPagesToOS(beg, end);
|
||||
}
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
|
||||
#if defined(__aarch64__)
|
||||
# define __get_tls() \
|
||||
({ void** __v; __asm__("mrs %0, tpidr_el0" : "=r"(__v)); __v; })
|
||||
#elif defined(__arm__)
|
||||
# define __get_tls() \
|
||||
({ void** __v; __asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__v)); __v; })
|
||||
#elif defined(__mips__)
|
||||
// On mips32r1, this goes via a kernel illegal instruction trap that's
|
||||
// optimized for v1.
|
||||
# define __get_tls() \
|
||||
({ register void** __v asm("v1"); \
|
||||
__asm__(".set push\n" \
|
||||
".set mips32r2\n" \
|
||||
"rdhwr %0,$29\n" \
|
||||
".set pop\n" : "=r"(__v)); \
|
||||
__v; })
|
||||
#elif defined(__i386__)
|
||||
# define __get_tls() \
|
||||
({ void** __v; __asm__("movl %%gs:0, %0" : "=r"(__v)); __v; })
|
||||
#elif defined(__x86_64__)
|
||||
# define __get_tls() \
|
||||
({ void** __v; __asm__("mov %%fs:0, %0" : "=r"(__v)); __v; })
|
||||
#else
|
||||
#error "Unsupported architecture."
|
||||
#endif
|
||||
|
||||
// The Android Bionic team has allocated a TLS slot for sanitizers starting
|
||||
// with Q, given that Android currently doesn't support ELF TLS. It is used to
|
||||
// store sanitizer thread specific data.
|
||||
static const int TLS_SLOT_SANITIZER = 6;
|
||||
|
||||
ALWAYS_INLINE uptr *get_android_tls_ptr() {
|
||||
return reinterpret_cast<uptr *>(&__get_tls()[TLS_SLOT_SANITIZER]);
|
||||
}
|
||||
|
||||
#endif // SANITIZER_ANDROID
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif
|
||||
#endif // SANITIZER_LINUX_H
|
||||
222
lib/tsan/sanitizer_common/sanitizer_linux_s390.cpp
Normal file
222
lib/tsan/sanitizer_common/sanitizer_linux_s390.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
//===-- sanitizer_linux_s390.cpp ------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries and implements s390-linux-specific functions from
|
||||
// sanitizer_libc.h.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_LINUX && SANITIZER_S390
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_linux.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// --------------- sanitizer_libc.h
|
||||
uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
|
||||
u64 offset) {
|
||||
struct s390_mmap_params {
|
||||
unsigned long addr;
|
||||
unsigned long length;
|
||||
unsigned long prot;
|
||||
unsigned long flags;
|
||||
unsigned long fd;
|
||||
unsigned long offset;
|
||||
} params = {
|
||||
(unsigned long)addr,
|
||||
(unsigned long)length,
|
||||
(unsigned long)prot,
|
||||
(unsigned long)flags,
|
||||
(unsigned long)fd,
|
||||
# ifdef __s390x__
|
||||
(unsigned long)offset,
|
||||
# else
|
||||
(unsigned long)(offset / 4096),
|
||||
# endif
|
||||
};
|
||||
# ifdef __s390x__
|
||||
return syscall(__NR_mmap, ¶ms);
|
||||
# else
|
||||
return syscall(__NR_mmap2, ¶ms);
|
||||
# endif
|
||||
}
|
||||
|
||||
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||
int *parent_tidptr, void *newtls, int *child_tidptr) {
|
||||
if (!fn || !child_stack)
|
||||
return -EINVAL;
|
||||
CHECK_EQ(0, (uptr)child_stack % 16);
|
||||
// Minimum frame size.
|
||||
#ifdef __s390x__
|
||||
child_stack = (char *)child_stack - 160;
|
||||
#else
|
||||
child_stack = (char *)child_stack - 96;
|
||||
#endif
|
||||
// Terminate unwind chain.
|
||||
((unsigned long *)child_stack)[0] = 0;
|
||||
// And pass parameters.
|
||||
((unsigned long *)child_stack)[1] = (uptr)fn;
|
||||
((unsigned long *)child_stack)[2] = (uptr)arg;
|
||||
register long res __asm__("r2");
|
||||
register void *__cstack __asm__("r2") = child_stack;
|
||||
register int __flags __asm__("r3") = flags;
|
||||
register int * __ptidptr __asm__("r4") = parent_tidptr;
|
||||
register int * __ctidptr __asm__("r5") = child_tidptr;
|
||||
register void * __newtls __asm__("r6") = newtls;
|
||||
|
||||
__asm__ __volatile__(
|
||||
/* Clone. */
|
||||
"svc %1\n"
|
||||
|
||||
/* if (%r2 != 0)
|
||||
* return;
|
||||
*/
|
||||
#ifdef __s390x__
|
||||
"cghi %%r2, 0\n"
|
||||
#else
|
||||
"chi %%r2, 0\n"
|
||||
#endif
|
||||
"jne 1f\n"
|
||||
|
||||
/* Call "fn(arg)". */
|
||||
#ifdef __s390x__
|
||||
"lmg %%r1, %%r2, 8(%%r15)\n"
|
||||
#else
|
||||
"lm %%r1, %%r2, 4(%%r15)\n"
|
||||
#endif
|
||||
"basr %%r14, %%r1\n"
|
||||
|
||||
/* Call _exit(%r2). */
|
||||
"svc %2\n"
|
||||
|
||||
/* Return to parent. */
|
||||
"1:\n"
|
||||
: "=r" (res)
|
||||
: "i"(__NR_clone), "i"(__NR_exit),
|
||||
"r"(__cstack),
|
||||
"r"(__flags),
|
||||
"r"(__ptidptr),
|
||||
"r"(__ctidptr),
|
||||
"r"(__newtls)
|
||||
: "memory", "cc");
|
||||
return res;
|
||||
}
|
||||
|
||||
#if SANITIZER_S390_64
|
||||
static bool FixedCVE_2016_2143() {
|
||||
// Try to determine if the running kernel has a fix for CVE-2016-2143,
|
||||
// return false if in doubt (better safe than sorry). Distros may want to
|
||||
// adjust this for their own kernels.
|
||||
struct utsname buf;
|
||||
unsigned int major, minor, patch = 0;
|
||||
// This should never fail, but just in case...
|
||||
if (internal_uname(&buf))
|
||||
return false;
|
||||
const char *ptr = buf.release;
|
||||
major = internal_simple_strtoll(ptr, &ptr, 10);
|
||||
// At least first 2 should be matched.
|
||||
if (ptr[0] != '.')
|
||||
return false;
|
||||
minor = internal_simple_strtoll(ptr+1, &ptr, 10);
|
||||
// Third is optional.
|
||||
if (ptr[0] == '.')
|
||||
patch = internal_simple_strtoll(ptr+1, &ptr, 10);
|
||||
if (major < 3) {
|
||||
if (major == 2 && minor == 6 && patch == 32 && ptr[0] == '-' &&
|
||||
internal_strstr(ptr, ".el6")) {
|
||||
// Check RHEL6
|
||||
int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
|
||||
if (r1 >= 657) // 2.6.32-657.el6 or later
|
||||
return true;
|
||||
if (r1 == 642 && ptr[0] == '.') {
|
||||
int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
|
||||
if (r2 >= 9) // 2.6.32-642.9.1.el6 or later
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// <3.0 is bad.
|
||||
return false;
|
||||
} else if (major == 3) {
|
||||
// 3.2.79+ is OK.
|
||||
if (minor == 2 && patch >= 79)
|
||||
return true;
|
||||
// 3.12.58+ is OK.
|
||||
if (minor == 12 && patch >= 58)
|
||||
return true;
|
||||
if (minor == 10 && patch == 0 && ptr[0] == '-' &&
|
||||
internal_strstr(ptr, ".el7")) {
|
||||
// Check RHEL7
|
||||
int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
|
||||
if (r1 >= 426) // 3.10.0-426.el7 or later
|
||||
return true;
|
||||
if (r1 == 327 && ptr[0] == '.') {
|
||||
int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
|
||||
if (r2 >= 27) // 3.10.0-327.27.1.el7 or later
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Otherwise, bad.
|
||||
return false;
|
||||
} else if (major == 4) {
|
||||
// 4.1.21+ is OK.
|
||||
if (minor == 1 && patch >= 21)
|
||||
return true;
|
||||
// 4.4.6+ is OK.
|
||||
if (minor == 4 && patch >= 6)
|
||||
return true;
|
||||
if (minor == 4 && patch == 0 && ptr[0] == '-' &&
|
||||
internal_strstr(buf.version, "Ubuntu")) {
|
||||
// Check Ubuntu 16.04
|
||||
int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
|
||||
if (r1 >= 13) // 4.4.0-13 or later
|
||||
return true;
|
||||
}
|
||||
// Otherwise, OK if 4.5+.
|
||||
return minor >= 5;
|
||||
} else {
|
||||
// Linux 5 and up are fine.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void AvoidCVE_2016_2143() {
|
||||
// Older kernels are affected by CVE-2016-2143 - they will crash hard
|
||||
// if someone uses 4-level page tables (ie. virtual addresses >= 4TB)
|
||||
// and fork() in the same process. Unfortunately, sanitizers tend to
|
||||
// require such addresses. Since this is very likely to crash the whole
|
||||
// machine (sanitizers themselves use fork() for llvm-symbolizer, for one),
|
||||
// abort the process at initialization instead.
|
||||
if (FixedCVE_2016_2143())
|
||||
return;
|
||||
if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143"))
|
||||
return;
|
||||
Report(
|
||||
"ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using ASan,\n"
|
||||
"MSan, TSan, DFSan or LSan with such kernel can and will crash your\n"
|
||||
"machine, or worse.\n"
|
||||
"\n"
|
||||
"If you are certain your kernel is not vulnerable (you have compiled it\n"
|
||||
"yourself, or are using an unrecognized distribution kernel), you can\n"
|
||||
"override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n"
|
||||
"with any value.\n");
|
||||
Die();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_LINUX && SANITIZER_S390
|
||||
166
lib/tsan/sanitizer_common/sanitizer_list.h
Normal file
166
lib/tsan/sanitizer_common/sanitizer_list.h
Normal file
@@ -0,0 +1,166 @@
|
||||
//===-- sanitizer_list.h ----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains implementation of a list class to be used by
|
||||
// ThreadSanitizer, etc run-times.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_LIST_H
|
||||
#define SANITIZER_LIST_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Intrusive singly-linked list with size(), push_back(), push_front()
|
||||
// pop_front(), append_front() and append_back().
|
||||
// This class should be a POD (so that it can be put into TLS)
|
||||
// and an object with all zero fields should represent a valid empty list.
|
||||
// This class does not have a CTOR, so clear() should be called on all
|
||||
// non-zero-initialized objects before using.
|
||||
template<class Item>
|
||||
struct IntrusiveList {
|
||||
friend class Iterator;
|
||||
|
||||
void clear() {
|
||||
first_ = last_ = nullptr;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
bool empty() const { return size_ == 0; }
|
||||
uptr size() const { return size_; }
|
||||
|
||||
void push_back(Item *x) {
|
||||
if (empty()) {
|
||||
x->next = nullptr;
|
||||
first_ = last_ = x;
|
||||
size_ = 1;
|
||||
} else {
|
||||
x->next = nullptr;
|
||||
last_->next = x;
|
||||
last_ = x;
|
||||
size_++;
|
||||
}
|
||||
}
|
||||
|
||||
void push_front(Item *x) {
|
||||
if (empty()) {
|
||||
x->next = nullptr;
|
||||
first_ = last_ = x;
|
||||
size_ = 1;
|
||||
} else {
|
||||
x->next = first_;
|
||||
first_ = x;
|
||||
size_++;
|
||||
}
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
CHECK(!empty());
|
||||
first_ = first_->next;
|
||||
if (!first_)
|
||||
last_ = nullptr;
|
||||
size_--;
|
||||
}
|
||||
|
||||
void extract(Item *prev, Item *x) {
|
||||
CHECK(!empty());
|
||||
CHECK_NE(prev, nullptr);
|
||||
CHECK_NE(x, nullptr);
|
||||
CHECK_EQ(prev->next, x);
|
||||
prev->next = x->next;
|
||||
if (last_ == x)
|
||||
last_ = prev;
|
||||
size_--;
|
||||
}
|
||||
|
||||
Item *front() { return first_; }
|
||||
const Item *front() const { return first_; }
|
||||
Item *back() { return last_; }
|
||||
const Item *back() const { return last_; }
|
||||
|
||||
void append_front(IntrusiveList<Item> *l) {
|
||||
CHECK_NE(this, l);
|
||||
if (l->empty())
|
||||
return;
|
||||
if (empty()) {
|
||||
*this = *l;
|
||||
} else if (!l->empty()) {
|
||||
l->last_->next = first_;
|
||||
first_ = l->first_;
|
||||
size_ += l->size();
|
||||
}
|
||||
l->clear();
|
||||
}
|
||||
|
||||
void append_back(IntrusiveList<Item> *l) {
|
||||
CHECK_NE(this, l);
|
||||
if (l->empty())
|
||||
return;
|
||||
if (empty()) {
|
||||
*this = *l;
|
||||
} else {
|
||||
last_->next = l->first_;
|
||||
last_ = l->last_;
|
||||
size_ += l->size();
|
||||
}
|
||||
l->clear();
|
||||
}
|
||||
|
||||
void CheckConsistency() {
|
||||
if (size_ == 0) {
|
||||
CHECK_EQ(first_, 0);
|
||||
CHECK_EQ(last_, 0);
|
||||
} else {
|
||||
uptr count = 0;
|
||||
for (Item *i = first_; ; i = i->next) {
|
||||
count++;
|
||||
if (i == last_) break;
|
||||
}
|
||||
CHECK_EQ(size(), count);
|
||||
CHECK_EQ(last_->next, 0);
|
||||
}
|
||||
}
|
||||
|
||||
template<class ItemTy>
|
||||
class IteratorBase {
|
||||
public:
|
||||
explicit IteratorBase(ItemTy *current) : current_(current) {}
|
||||
IteratorBase &operator++() {
|
||||
current_ = current_->next;
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(IteratorBase other) const {
|
||||
return current_ != other.current_;
|
||||
}
|
||||
ItemTy &operator*() {
|
||||
return *current_;
|
||||
}
|
||||
private:
|
||||
ItemTy *current_;
|
||||
};
|
||||
|
||||
typedef IteratorBase<Item> Iterator;
|
||||
typedef IteratorBase<const Item> ConstIterator;
|
||||
|
||||
Iterator begin() { return Iterator(first_); }
|
||||
Iterator end() { return Iterator(0); }
|
||||
|
||||
ConstIterator begin() const { return ConstIterator(first_); }
|
||||
ConstIterator end() const { return ConstIterator(0); }
|
||||
|
||||
// private, don't use directly.
|
||||
uptr size_;
|
||||
Item *first_;
|
||||
Item *last_;
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_LIST_H
|
||||
@@ -0,0 +1,76 @@
|
||||
//===-- sanitizer_local_address_space_view.h --------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// `LocalAddressSpaceView` provides the local (i.e. target and current address
|
||||
// space are the same) implementation of the `AddressSpaveView` interface which
|
||||
// provides a simple interface to load memory from another process (i.e.
|
||||
// out-of-process)
|
||||
//
|
||||
// The `AddressSpaceView` interface requires that the type can be used as a
|
||||
// template parameter to objects that wish to be able to operate in an
|
||||
// out-of-process manner. In normal usage, objects are in-process and are thus
|
||||
// instantiated with the `LocalAddressSpaceView` type. This type is used to
|
||||
// load any pointers in instance methods. This implementation is effectively
|
||||
// a no-op. When an object is to be used in an out-of-process manner it is
|
||||
// instansiated with the `RemoteAddressSpaceView` type.
|
||||
//
|
||||
// By making `AddressSpaceView` a template parameter of an object, it can
|
||||
// change its implementation at compile time which has no run time overhead.
|
||||
// This also allows unifying in-process and out-of-process code which avoids
|
||||
// code duplication.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_LOCAL_ADDRES_SPACE_VIEW_H
|
||||
#define SANITIZER_LOCAL_ADDRES_SPACE_VIEW_H
|
||||
|
||||
namespace __sanitizer {
|
||||
struct LocalAddressSpaceView {
|
||||
// Load memory `sizeof(T) * num_elements` bytes of memory from the target
|
||||
// process (always local for this implementation) starting at address
|
||||
// `target_address`. The local copy of this memory is returned as a pointer.
|
||||
// The caller should not write to this memory. The behaviour when doing so is
|
||||
// undefined. Callers should use `LoadWritable()` to get access to memory
|
||||
// that is writable.
|
||||
//
|
||||
// The lifetime of loaded memory is implementation defined.
|
||||
template <typename T>
|
||||
static const T *Load(const T *target_address, uptr num_elements = 1) {
|
||||
// The target address space is the local address space so
|
||||
// nothing needs to be copied. Just return the pointer.
|
||||
return target_address;
|
||||
}
|
||||
|
||||
// Load memory `sizeof(T) * num_elements` bytes of memory from the target
|
||||
// process (always local for this implementation) starting at address
|
||||
// `target_address`. The local copy of this memory is returned as a pointer.
|
||||
// The memory returned may be written to.
|
||||
//
|
||||
// Writes made to the returned memory will be visible in the memory returned
|
||||
// by subsequent `Load()` or `LoadWritable()` calls provided the
|
||||
// `target_address` parameter is the same. It is not guaranteed that the
|
||||
// memory returned by previous calls to `Load()` will contain any performed
|
||||
// writes. If two or more overlapping regions of memory are loaded via
|
||||
// separate calls to `LoadWritable()`, it is implementation defined whether
|
||||
// writes made to the region returned by one call are visible in the regions
|
||||
// returned by other calls.
|
||||
//
|
||||
// Given the above it is recommended to load the largest possible object
|
||||
// that requires modification (e.g. a class) rather than individual fields
|
||||
// from a class to avoid issues with overlapping writable regions.
|
||||
//
|
||||
// The lifetime of loaded memory is implementation defined.
|
||||
template <typename T>
|
||||
static T *LoadWritable(T *target_address, uptr num_elements = 1) {
|
||||
// The target address space is the local address space so
|
||||
// nothing needs to be copied. Just return the pointer.
|
||||
return target_address;
|
||||
}
|
||||
};
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif
|
||||
1228
lib/tsan/sanitizer_common/sanitizer_mac.cpp
Normal file
1228
lib/tsan/sanitizer_common/sanitizer_mac.cpp
Normal file
File diff suppressed because it is too large
Load Diff
84
lib/tsan/sanitizer_common/sanitizer_mac.h
Normal file
84
lib/tsan/sanitizer_common/sanitizer_mac.h
Normal file
@@ -0,0 +1,84 @@
|
||||
//===-- sanitizer_mac.h -----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between various sanitizers' runtime libraries and
|
||||
// provides definitions for OSX-specific functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_MAC_H
|
||||
#define SANITIZER_MAC_H
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_MAC
|
||||
#include "sanitizer_posix.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
struct MemoryMappingLayoutData {
|
||||
int current_image;
|
||||
u32 current_magic;
|
||||
u32 current_filetype;
|
||||
ModuleArch current_arch;
|
||||
u8 current_uuid[kModuleUUIDSize];
|
||||
int current_load_cmd_count;
|
||||
const char *current_load_cmd_addr;
|
||||
bool current_instrumented;
|
||||
};
|
||||
|
||||
template <typename VersionType>
|
||||
struct VersionBase {
|
||||
u16 major;
|
||||
u16 minor;
|
||||
|
||||
VersionBase(u16 major, u16 minor) : major(major), minor(minor) {}
|
||||
|
||||
bool operator==(const VersionType &other) const {
|
||||
return major == other.major && minor == other.minor;
|
||||
}
|
||||
bool operator>=(const VersionType &other) const {
|
||||
return major > other.major ||
|
||||
(major == other.major && minor >= other.minor);
|
||||
}
|
||||
};
|
||||
|
||||
struct MacosVersion : VersionBase<MacosVersion> {
|
||||
MacosVersion(u16 major, u16 minor) : VersionBase(major, minor) {}
|
||||
};
|
||||
|
||||
struct DarwinKernelVersion : VersionBase<DarwinKernelVersion> {
|
||||
DarwinKernelVersion(u16 major, u16 minor) : VersionBase(major, minor) {}
|
||||
};
|
||||
|
||||
MacosVersion GetMacosAlignedVersion();
|
||||
DarwinKernelVersion GetDarwinKernelVersion();
|
||||
|
||||
char **GetEnviron();
|
||||
|
||||
void RestrictMemoryToMaxAddress(uptr max_address);
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
extern "C" {
|
||||
static char __crashreporter_info_buff__[__sanitizer::kErrorMessageBufferSize] =
|
||||
{};
|
||||
static const char *__crashreporter_info__ __attribute__((__used__)) =
|
||||
&__crashreporter_info_buff__[0];
|
||||
asm(".desc ___crashreporter_info__, 0x10");
|
||||
} // extern "C"
|
||||
|
||||
namespace __sanitizer {
|
||||
static BlockingMutex crashreporter_info_mutex(LINKER_INITIALIZED);
|
||||
|
||||
INLINE void CRAppendCrashLogMessage(const char *msg) {
|
||||
BlockingMutexLock l(&crashreporter_info_mutex);
|
||||
internal_strlcat(__crashreporter_info_buff__, msg,
|
||||
sizeof(__crashreporter_info_buff__)); }
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_MAC
|
||||
#endif // SANITIZER_MAC_H
|
||||
420
lib/tsan/sanitizer_common/sanitizer_malloc_mac.inc
Normal file
420
lib/tsan/sanitizer_common/sanitizer_malloc_mac.inc
Normal file
@@ -0,0 +1,420 @@
|
||||
//===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains Mac-specific malloc interceptors and a custom zone
|
||||
// implementation, which together replace the system allocator.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#if !SANITIZER_MAC
|
||||
#error "This file should only be compiled on Darwin."
|
||||
#endif
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#include <dlfcn.h>
|
||||
#include <malloc/malloc.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_mac.h"
|
||||
|
||||
// Similar code is used in Google Perftools,
|
||||
// https://github.com/gperftools/gperftools.
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
extern malloc_zone_t sanitizer_zone;
|
||||
|
||||
struct sanitizer_malloc_introspection_t : public malloc_introspection_t {
|
||||
// IMPORTANT: Do not change the order, alignment, or types of these fields to
|
||||
// maintain binary compatibility. You should only add fields to this struct.
|
||||
|
||||
// Used to track changes to the allocator that will affect
|
||||
// zone enumeration.
|
||||
u64 allocator_enumeration_version;
|
||||
uptr allocator_ptr;
|
||||
uptr allocator_size;
|
||||
};
|
||||
|
||||
u64 GetMallocZoneAllocatorEnumerationVersion() {
|
||||
// This represents the current allocator ABI version.
|
||||
// This field should be incremented every time the Allocator
|
||||
// ABI changes in a way that breaks allocator enumeration.
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
|
||||
vm_size_t start_size, unsigned zone_flags) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
uptr page_size = GetPageSizeCached();
|
||||
uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
|
||||
COMMON_MALLOC_MEMALIGN(page_size, allocated_size);
|
||||
malloc_zone_t *new_zone = (malloc_zone_t *)p;
|
||||
internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));
|
||||
new_zone->zone_name = NULL; // The name will be changed anyway.
|
||||
// Prevent the client app from overwriting the zone contents.
|
||||
// Library functions that need to modify the zone will set PROT_WRITE on it.
|
||||
// This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
|
||||
mprotect(new_zone, allocated_size, PROT_READ);
|
||||
// We're explicitly *NOT* registering the zone.
|
||||
return new_zone;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, malloc_destroy_zone, malloc_zone_t *zone) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
// We don't need to do anything here. We're not registering new zones, so we
|
||||
// don't to unregister. Just un-mprotect and free() the zone.
|
||||
uptr page_size = GetPageSizeCached();
|
||||
uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
|
||||
mprotect(zone, allocated_size, PROT_READ | PROT_WRITE);
|
||||
if (zone->zone_name) {
|
||||
COMMON_MALLOC_FREE((void *)zone->zone_name);
|
||||
}
|
||||
COMMON_MALLOC_FREE(zone);
|
||||
}
|
||||
|
||||
INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
return &sanitizer_zone;
|
||||
}
|
||||
|
||||
INTERCEPTOR(malloc_zone_t *, malloc_zone_from_ptr, const void *ptr) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
size_t size = sanitizer_zone.size(&sanitizer_zone, ptr);
|
||||
if (size) { // Claimed by sanitizer zone?
|
||||
return &sanitizer_zone;
|
||||
}
|
||||
return REAL(malloc_zone_from_ptr)(ptr);
|
||||
}
|
||||
|
||||
INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
|
||||
// FIXME: ASan should support purgeable allocations.
|
||||
// https://github.com/google/sanitizers/issues/139
|
||||
COMMON_MALLOC_ENTER();
|
||||
return &sanitizer_zone;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
|
||||
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
|
||||
// for now.
|
||||
COMMON_MALLOC_ENTER();
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
|
||||
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
|
||||
// for now.
|
||||
COMMON_MALLOC_ENTER();
|
||||
// Must return 0 if the contents were not purged since the last call to
|
||||
// malloc_make_purgeable().
|
||||
return 0;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
// Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)|
|
||||
// bytes.
|
||||
size_t buflen =
|
||||
sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0);
|
||||
InternalScopedString new_name(buflen);
|
||||
if (name && zone->introspect == sanitizer_zone.introspect) {
|
||||
new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
|
||||
name = new_name.data();
|
||||
}
|
||||
|
||||
// Call the system malloc's implementation for both external and our zones,
|
||||
// since that appropriately changes VM region protections on the zone.
|
||||
REAL(malloc_set_zone_name)(zone, name);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, malloc, size_t size) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
COMMON_MALLOC_MALLOC(size);
|
||||
return p;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, free, void *ptr) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
if (!ptr) return;
|
||||
COMMON_MALLOC_FREE(ptr);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
COMMON_MALLOC_REALLOC(ptr, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
COMMON_MALLOC_CALLOC(nmemb, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, valloc, size_t size) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
COMMON_MALLOC_VALLOC(size);
|
||||
return p;
|
||||
}
|
||||
|
||||
INTERCEPTOR(size_t, malloc_good_size, size_t size) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
CHECK(memptr);
|
||||
COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size);
|
||||
return res;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
|
||||
// wrappers, as they are basically copied from there.
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
|
||||
COMMON_MALLOC_SIZE(ptr);
|
||||
return size;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
COMMON_MALLOC_MALLOC(size);
|
||||
return p;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
|
||||
if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
|
||||
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
|
||||
const size_t kCallocPoolSize = 1024;
|
||||
static uptr calloc_memory_for_dlsym[kCallocPoolSize];
|
||||
static size_t allocated;
|
||||
size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
|
||||
void *mem = (void*)&calloc_memory_for_dlsym[allocated];
|
||||
allocated += size_in_words;
|
||||
CHECK(allocated < kCallocPoolSize);
|
||||
return mem;
|
||||
}
|
||||
COMMON_MALLOC_CALLOC(nmemb, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
COMMON_MALLOC_VALLOC(size);
|
||||
return p;
|
||||
}
|
||||
|
||||
// TODO(glider): the allocation callbacks need to be refactored.
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
|
||||
if (!ptr) return;
|
||||
COMMON_MALLOC_FREE(ptr);
|
||||
}
|
||||
|
||||
#define GET_ZONE_FOR_PTR(ptr) \
|
||||
malloc_zone_t *zone_ptr = WRAP(malloc_zone_from_ptr)(ptr); \
|
||||
const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
|
||||
if (!ptr) {
|
||||
COMMON_MALLOC_MALLOC(new_size);
|
||||
return p;
|
||||
} else {
|
||||
COMMON_MALLOC_SIZE(ptr);
|
||||
if (size) {
|
||||
COMMON_MALLOC_REALLOC(ptr, new_size);
|
||||
return p;
|
||||
} else {
|
||||
// We can't recover from reallocating an unknown address, because
|
||||
// this would require reading at most |new_size| bytes from
|
||||
// potentially unaccessible memory.
|
||||
GET_ZONE_FOR_PTR(ptr);
|
||||
COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_mz_destroy(malloc_zone_t* zone) {
|
||||
// A no-op -- we will not be destroyed!
|
||||
Report("__sanitizer_mz_destroy() called -- ignoring\n");
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
|
||||
COMMON_MALLOC_ENTER();
|
||||
COMMON_MALLOC_MEMALIGN(align, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
// This public API exists purely for testing purposes.
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
malloc_zone_t* __sanitizer_mz_default_zone() {
|
||||
return &sanitizer_zone;
|
||||
}
|
||||
|
||||
// This function is currently unused, and we build with -Werror.
|
||||
#if 0
|
||||
void __sanitizer_mz_free_definite_size(
|
||||
malloc_zone_t* zone, void *ptr, size_t size) {
|
||||
// TODO(glider): check that |size| is valid.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef COMMON_MALLOC_HAS_ZONE_ENUMERATOR
|
||||
#error "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be defined"
|
||||
#endif
|
||||
static_assert((COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 0 ||
|
||||
(COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 1,
|
||||
"COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be 0 or 1");
|
||||
|
||||
#if COMMON_MALLOC_HAS_ZONE_ENUMERATOR
|
||||
// Forward declare and expect the implementation to provided by
|
||||
// includer.
|
||||
kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
|
||||
vm_address_t zone_address, memory_reader_t reader,
|
||||
vm_range_recorder_t recorder);
|
||||
#else
|
||||
// Provide stub implementation that fails.
|
||||
kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask,
|
||||
vm_address_t zone_address, memory_reader_t reader,
|
||||
vm_range_recorder_t recorder) {
|
||||
// Not supported.
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
|
||||
#error "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be defined"
|
||||
#endif
|
||||
static_assert((COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 0 ||
|
||||
(COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 1,
|
||||
"COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be 0 or 1");
|
||||
#if COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
|
||||
// Forward declare and expect the implementation to provided by
|
||||
// includer.
|
||||
void mi_extra_init(
|
||||
sanitizer_malloc_introspection_t *mi);
|
||||
#else
|
||||
void mi_extra_init(
|
||||
sanitizer_malloc_introspection_t *mi) {
|
||||
// Just zero initialize the fields.
|
||||
mi->allocator_ptr = 0;
|
||||
mi->allocator_size = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t mi_good_size(malloc_zone_t *zone, size_t size) {
|
||||
// I think it's always safe to return size, but we maybe could do better.
|
||||
return size;
|
||||
}
|
||||
|
||||
boolean_t mi_check(malloc_zone_t *zone) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void mi_print(malloc_zone_t *zone, boolean_t verbose) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void mi_log(malloc_zone_t *zone, void *address) {
|
||||
// I don't think we support anything like this
|
||||
}
|
||||
|
||||
void mi_force_lock(malloc_zone_t *zone) {
|
||||
COMMON_MALLOC_FORCE_LOCK();
|
||||
}
|
||||
|
||||
void mi_force_unlock(malloc_zone_t *zone) {
|
||||
COMMON_MALLOC_FORCE_UNLOCK();
|
||||
}
|
||||
|
||||
void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
|
||||
COMMON_MALLOC_FILL_STATS(zone, stats);
|
||||
}
|
||||
|
||||
boolean_t mi_zone_locked(malloc_zone_t *zone) {
|
||||
// UNIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
namespace COMMON_MALLOC_NAMESPACE {
|
||||
|
||||
void InitMallocZoneFields() {
|
||||
static sanitizer_malloc_introspection_t sanitizer_zone_introspection;
|
||||
// Ok to use internal_memset, these places are not performance-critical.
|
||||
internal_memset(&sanitizer_zone_introspection, 0,
|
||||
sizeof(sanitizer_zone_introspection));
|
||||
|
||||
sanitizer_zone_introspection.enumerator = &mi_enumerator;
|
||||
sanitizer_zone_introspection.good_size = &mi_good_size;
|
||||
sanitizer_zone_introspection.check = &mi_check;
|
||||
sanitizer_zone_introspection.print = &mi_print;
|
||||
sanitizer_zone_introspection.log = &mi_log;
|
||||
sanitizer_zone_introspection.force_lock = &mi_force_lock;
|
||||
sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
|
||||
sanitizer_zone_introspection.statistics = &mi_statistics;
|
||||
sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
|
||||
|
||||
// Set current allocator enumeration version.
|
||||
sanitizer_zone_introspection.allocator_enumeration_version =
|
||||
GetMallocZoneAllocatorEnumerationVersion();
|
||||
|
||||
// Perform any sanitizer specific initialization.
|
||||
mi_extra_init(&sanitizer_zone_introspection);
|
||||
|
||||
internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
|
||||
|
||||
// Use version 6 for OSX >= 10.6.
|
||||
sanitizer_zone.version = 6;
|
||||
sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
|
||||
sanitizer_zone.size = &__sanitizer_mz_size;
|
||||
sanitizer_zone.malloc = &__sanitizer_mz_malloc;
|
||||
sanitizer_zone.calloc = &__sanitizer_mz_calloc;
|
||||
sanitizer_zone.valloc = &__sanitizer_mz_valloc;
|
||||
sanitizer_zone.free = &__sanitizer_mz_free;
|
||||
sanitizer_zone.realloc = &__sanitizer_mz_realloc;
|
||||
sanitizer_zone.destroy = &__sanitizer_mz_destroy;
|
||||
sanitizer_zone.batch_malloc = 0;
|
||||
sanitizer_zone.batch_free = 0;
|
||||
sanitizer_zone.free_definite_size = 0;
|
||||
sanitizer_zone.memalign = &__sanitizer_mz_memalign;
|
||||
sanitizer_zone.introspect = &sanitizer_zone_introspection;
|
||||
}
|
||||
|
||||
void ReplaceSystemMalloc() {
|
||||
InitMallocZoneFields();
|
||||
|
||||
// Register the zone.
|
||||
malloc_zone_register(&sanitizer_zone);
|
||||
}
|
||||
|
||||
} // namespace COMMON_MALLOC_NAMESPACE
|
||||
223
lib/tsan/sanitizer_common/sanitizer_mutex.h
Normal file
223
lib/tsan/sanitizer_common/sanitizer_mutex.h
Normal file
@@ -0,0 +1,223 @@
|
||||
//===-- sanitizer_mutex.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_MUTEX_H
|
||||
#define SANITIZER_MUTEX_H
|
||||
|
||||
#include "sanitizer_atomic.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_libc.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
class StaticSpinMutex {
|
||||
public:
|
||||
void Init() {
|
||||
atomic_store(&state_, 0, memory_order_relaxed);
|
||||
}
|
||||
|
||||
void Lock() {
|
||||
if (TryLock())
|
||||
return;
|
||||
LockSlow();
|
||||
}
|
||||
|
||||
bool TryLock() {
|
||||
return atomic_exchange(&state_, 1, memory_order_acquire) == 0;
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
atomic_store(&state_, 0, memory_order_release);
|
||||
}
|
||||
|
||||
void CheckLocked() {
|
||||
CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1);
|
||||
}
|
||||
|
||||
private:
|
||||
atomic_uint8_t state_;
|
||||
|
||||
void NOINLINE LockSlow() {
|
||||
for (int i = 0;; i++) {
|
||||
if (i < 10)
|
||||
proc_yield(10);
|
||||
else
|
||||
internal_sched_yield();
|
||||
if (atomic_load(&state_, memory_order_relaxed) == 0
|
||||
&& atomic_exchange(&state_, 1, memory_order_acquire) == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SpinMutex : public StaticSpinMutex {
|
||||
public:
|
||||
SpinMutex() {
|
||||
Init();
|
||||
}
|
||||
|
||||
private:
|
||||
SpinMutex(const SpinMutex&);
|
||||
void operator=(const SpinMutex&);
|
||||
};
|
||||
|
||||
class BlockingMutex {
|
||||
public:
|
||||
explicit constexpr BlockingMutex(LinkerInitialized)
|
||||
: opaque_storage_ {0, }, owner_ {0} {}
|
||||
BlockingMutex();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
// This function does not guarantee an explicit check that the calling thread
|
||||
// is the thread which owns the mutex. This behavior, while more strictly
|
||||
// correct, causes problems in cases like StopTheWorld, where a parent thread
|
||||
// owns the mutex but a child checks that it is locked. Rather than
|
||||
// maintaining complex state to work around those situations, the check only
|
||||
// checks that the mutex is owned, and assumes callers to be generally
|
||||
// well-behaved.
|
||||
void CheckLocked();
|
||||
|
||||
private:
|
||||
// Solaris mutex_t has a member that requires 64-bit alignment.
|
||||
ALIGNED(8) uptr opaque_storage_[10];
|
||||
uptr owner_; // for debugging
|
||||
};
|
||||
|
||||
// Reader-writer spin mutex.
|
||||
class RWMutex {
|
||||
public:
|
||||
RWMutex() {
|
||||
atomic_store(&state_, kUnlocked, memory_order_relaxed);
|
||||
}
|
||||
|
||||
~RWMutex() {
|
||||
CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
|
||||
}
|
||||
|
||||
void Lock() {
|
||||
u32 cmp = kUnlocked;
|
||||
if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
|
||||
memory_order_acquire))
|
||||
return;
|
||||
LockSlow();
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
u32 prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
|
||||
DCHECK_NE(prev & kWriteLock, 0);
|
||||
(void)prev;
|
||||
}
|
||||
|
||||
void ReadLock() {
|
||||
u32 prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
|
||||
if ((prev & kWriteLock) == 0)
|
||||
return;
|
||||
ReadLockSlow();
|
||||
}
|
||||
|
||||
void ReadUnlock() {
|
||||
u32 prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
|
||||
DCHECK_EQ(prev & kWriteLock, 0);
|
||||
DCHECK_GT(prev & ~kWriteLock, 0);
|
||||
(void)prev;
|
||||
}
|
||||
|
||||
void CheckLocked() {
|
||||
CHECK_NE(atomic_load(&state_, memory_order_relaxed), kUnlocked);
|
||||
}
|
||||
|
||||
private:
|
||||
atomic_uint32_t state_;
|
||||
|
||||
enum {
|
||||
kUnlocked = 0,
|
||||
kWriteLock = 1,
|
||||
kReadLock = 2
|
||||
};
|
||||
|
||||
void NOINLINE LockSlow() {
|
||||
for (int i = 0;; i++) {
|
||||
if (i < 10)
|
||||
proc_yield(10);
|
||||
else
|
||||
internal_sched_yield();
|
||||
u32 cmp = atomic_load(&state_, memory_order_relaxed);
|
||||
if (cmp == kUnlocked &&
|
||||
atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
|
||||
memory_order_acquire))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void NOINLINE ReadLockSlow() {
|
||||
for (int i = 0;; i++) {
|
||||
if (i < 10)
|
||||
proc_yield(10);
|
||||
else
|
||||
internal_sched_yield();
|
||||
u32 prev = atomic_load(&state_, memory_order_acquire);
|
||||
if ((prev & kWriteLock) == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RWMutex(const RWMutex&);
|
||||
void operator = (const RWMutex&);
|
||||
};
|
||||
|
||||
template<typename MutexType>
|
||||
class GenericScopedLock {
|
||||
public:
|
||||
explicit GenericScopedLock(MutexType *mu)
|
||||
: mu_(mu) {
|
||||
mu_->Lock();
|
||||
}
|
||||
|
||||
~GenericScopedLock() {
|
||||
mu_->Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
MutexType *mu_;
|
||||
|
||||
GenericScopedLock(const GenericScopedLock&);
|
||||
void operator=(const GenericScopedLock&);
|
||||
};
|
||||
|
||||
template<typename MutexType>
|
||||
class GenericScopedReadLock {
|
||||
public:
|
||||
explicit GenericScopedReadLock(MutexType *mu)
|
||||
: mu_(mu) {
|
||||
mu_->ReadLock();
|
||||
}
|
||||
|
||||
~GenericScopedReadLock() {
|
||||
mu_->ReadUnlock();
|
||||
}
|
||||
|
||||
private:
|
||||
MutexType *mu_;
|
||||
|
||||
GenericScopedReadLock(const GenericScopedReadLock&);
|
||||
void operator=(const GenericScopedReadLock&);
|
||||
};
|
||||
|
||||
typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
|
||||
typedef GenericScopedLock<BlockingMutex> BlockingMutexLock;
|
||||
typedef GenericScopedLock<RWMutex> RWMutexLock;
|
||||
typedef GenericScopedReadLock<RWMutex> RWMutexReadLock;
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_MUTEX_H
|
||||
343
lib/tsan/sanitizer_common/sanitizer_netbsd.cpp
Normal file
343
lib/tsan/sanitizer_common/sanitizer_netbsd.cpp
Normal file
@@ -0,0 +1,343 @@
|
||||
//===-- sanitizer_netbsd.cpp ----------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between Sanitizer run-time libraries and implements
|
||||
// NetBSD-specific functions from sanitizer_libc.h.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_NETBSD
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_getauxval.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_linux.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
#include "sanitizer_procmaps.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/exec.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <link.h>
|
||||
#include <lwp.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" void *__mmap(void *, size_t, int, int, int, int,
|
||||
off_t) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" int __sysctl(const int *, unsigned int, void *, size_t *,
|
||||
const void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" int _sys_close(int) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" int _sys_open(const char *, int, ...) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" ssize_t _sys_read(int, void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" ssize_t _sys_write(int, const void *,
|
||||
size_t) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" int __ftruncate(int, int, off_t) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" ssize_t _sys_readlink(const char *, char *,
|
||||
size_t) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" int _sys_sched_yield() SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" int _sys___nanosleep50(const void *,
|
||||
void *) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" int _sys_execve(const char *, char *const[],
|
||||
char *const[]) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" off_t __lseek(int, int, off_t, int) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" int __fork() SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" int _sys___sigprocmask14(int, const void *,
|
||||
void *) SANITIZER_WEAK_ATTRIBUTE;
|
||||
extern "C" int _sys___wait450(int wpid, int *, int,
|
||||
void *) SANITIZER_WEAK_ATTRIBUTE;
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
static void *GetRealLibcAddress(const char *symbol) {
|
||||
void *real = dlsym(RTLD_NEXT, symbol);
|
||||
if (!real)
|
||||
real = dlsym(RTLD_DEFAULT, symbol);
|
||||
if (!real) {
|
||||
Printf("GetRealLibcAddress failed for symbol=%s", symbol);
|
||||
Die();
|
||||
}
|
||||
return real;
|
||||
}
|
||||
|
||||
#define _REAL(func, ...) real##_##func(__VA_ARGS__)
|
||||
#define DEFINE__REAL(ret_type, func, ...) \
|
||||
static ret_type (*real_##func)(__VA_ARGS__) = NULL; \
|
||||
if (!real_##func) { \
|
||||
real_##func = (ret_type(*)(__VA_ARGS__))GetRealLibcAddress(#func); \
|
||||
} \
|
||||
CHECK(real_##func);
|
||||
|
||||
// --------------- sanitizer_libc.h
|
||||
uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
|
||||
u64 offset) {
|
||||
CHECK(&__mmap);
|
||||
return (uptr)__mmap(addr, length, prot, flags, fd, 0, offset);
|
||||
}
|
||||
|
||||
uptr internal_munmap(void *addr, uptr length) {
|
||||
DEFINE__REAL(int, munmap, void *a, uptr b);
|
||||
return _REAL(munmap, addr, length);
|
||||
}
|
||||
|
||||
int internal_mprotect(void *addr, uptr length, int prot) {
|
||||
DEFINE__REAL(int, mprotect, void *a, uptr b, int c);
|
||||
return _REAL(mprotect, addr, length, prot);
|
||||
}
|
||||
|
||||
uptr internal_close(fd_t fd) {
|
||||
CHECK(&_sys_close);
|
||||
return _sys_close(fd);
|
||||
}
|
||||
|
||||
uptr internal_open(const char *filename, int flags) {
|
||||
CHECK(&_sys_open);
|
||||
return _sys_open(filename, flags);
|
||||
}
|
||||
|
||||
uptr internal_open(const char *filename, int flags, u32 mode) {
|
||||
CHECK(&_sys_open);
|
||||
return _sys_open(filename, flags, mode);
|
||||
}
|
||||
|
||||
uptr internal_read(fd_t fd, void *buf, uptr count) {
|
||||
sptr res;
|
||||
CHECK(&_sys_read);
|
||||
HANDLE_EINTR(res, (sptr)_sys_read(fd, buf, (size_t)count));
|
||||
return res;
|
||||
}
|
||||
|
||||
uptr internal_write(fd_t fd, const void *buf, uptr count) {
|
||||
sptr res;
|
||||
CHECK(&_sys_write);
|
||||
HANDLE_EINTR(res, (sptr)_sys_write(fd, buf, count));
|
||||
return res;
|
||||
}
|
||||
|
||||
uptr internal_ftruncate(fd_t fd, uptr size) {
|
||||
sptr res;
|
||||
CHECK(&__ftruncate);
|
||||
HANDLE_EINTR(res, __ftruncate(fd, 0, (s64)size));
|
||||
return res;
|
||||
}
|
||||
|
||||
uptr internal_stat(const char *path, void *buf) {
|
||||
DEFINE__REAL(int, __stat50, const char *a, void *b);
|
||||
return _REAL(__stat50, path, buf);
|
||||
}
|
||||
|
||||
uptr internal_lstat(const char *path, void *buf) {
|
||||
DEFINE__REAL(int, __lstat50, const char *a, void *b);
|
||||
return _REAL(__lstat50, path, buf);
|
||||
}
|
||||
|
||||
uptr internal_fstat(fd_t fd, void *buf) {
|
||||
DEFINE__REAL(int, __fstat50, int a, void *b);
|
||||
return _REAL(__fstat50, fd, buf);
|
||||
}
|
||||
|
||||
uptr internal_filesize(fd_t fd) {
|
||||
struct stat st;
|
||||
if (internal_fstat(fd, &st))
|
||||
return -1;
|
||||
return (uptr)st.st_size;
|
||||
}
|
||||
|
||||
uptr internal_dup(int oldfd) {
|
||||
DEFINE__REAL(int, dup, int a);
|
||||
return _REAL(dup, oldfd);
|
||||
}
|
||||
|
||||
uptr internal_dup2(int oldfd, int newfd) {
|
||||
DEFINE__REAL(int, dup2, int a, int b);
|
||||
return _REAL(dup2, oldfd, newfd);
|
||||
}
|
||||
|
||||
uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
|
||||
CHECK(&_sys_readlink);
|
||||
return (uptr)_sys_readlink(path, buf, bufsize);
|
||||
}
|
||||
|
||||
uptr internal_unlink(const char *path) {
|
||||
DEFINE__REAL(int, unlink, const char *a);
|
||||
return _REAL(unlink, path);
|
||||
}
|
||||
|
||||
uptr internal_rename(const char *oldpath, const char *newpath) {
|
||||
DEFINE__REAL(int, rename, const char *a, const char *b);
|
||||
return _REAL(rename, oldpath, newpath);
|
||||
}
|
||||
|
||||
uptr internal_sched_yield() {
|
||||
CHECK(&_sys_sched_yield);
|
||||
return _sys_sched_yield();
|
||||
}
|
||||
|
||||
void internal__exit(int exitcode) {
|
||||
DEFINE__REAL(void, _exit, int a);
|
||||
_REAL(_exit, exitcode);
|
||||
Die(); // Unreachable.
|
||||
}
|
||||
|
||||
unsigned int internal_sleep(unsigned int seconds) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = seconds;
|
||||
ts.tv_nsec = 0;
|
||||
CHECK(&_sys___nanosleep50);
|
||||
int res = _sys___nanosleep50(&ts, &ts);
|
||||
if (res)
|
||||
return ts.tv_sec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uptr internal_execve(const char *filename, char *const argv[],
|
||||
char *const envp[]) {
|
||||
CHECK(&_sys_execve);
|
||||
return _sys_execve(filename, argv, envp);
|
||||
}
|
||||
|
||||
tid_t GetTid() {
|
||||
DEFINE__REAL(int, _lwp_self);
|
||||
return _REAL(_lwp_self);
|
||||
}
|
||||
|
||||
int TgKill(pid_t pid, tid_t tid, int sig) {
|
||||
DEFINE__REAL(int, _lwp_kill, int a, int b);
|
||||
(void)pid;
|
||||
return _REAL(_lwp_kill, tid, sig);
|
||||
}
|
||||
|
||||
u64 NanoTime() {
|
||||
timeval tv;
|
||||
DEFINE__REAL(int, __gettimeofday50, void *a, void *b);
|
||||
internal_memset(&tv, 0, sizeof(tv));
|
||||
_REAL(__gettimeofday50, &tv, 0);
|
||||
return (u64)tv.tv_sec * 1000 * 1000 * 1000 + tv.tv_usec * 1000;
|
||||
}
|
||||
|
||||
uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
|
||||
DEFINE__REAL(int, __clock_gettime50, __sanitizer_clockid_t a, void *b);
|
||||
return _REAL(__clock_gettime50, clk_id, tp);
|
||||
}
|
||||
|
||||
uptr internal_ptrace(int request, int pid, void *addr, int data) {
|
||||
DEFINE__REAL(int, ptrace, int a, int b, void *c, int d);
|
||||
return _REAL(ptrace, request, pid, addr, data);
|
||||
}
|
||||
|
||||
uptr internal_waitpid(int pid, int *status, int options) {
|
||||
CHECK(&_sys___wait450);
|
||||
return _sys___wait450(pid, status, options, 0 /* rusage */);
|
||||
}
|
||||
|
||||
uptr internal_getpid() {
|
||||
DEFINE__REAL(int, getpid);
|
||||
return _REAL(getpid);
|
||||
}
|
||||
|
||||
uptr internal_getppid() {
|
||||
DEFINE__REAL(int, getppid);
|
||||
return _REAL(getppid);
|
||||
}
|
||||
|
||||
int internal_dlinfo(void *handle, int request, void *p) {
|
||||
DEFINE__REAL(int, dlinfo, void *a, int b, void *c);
|
||||
return _REAL(dlinfo, handle, request, p);
|
||||
}
|
||||
|
||||
uptr internal_getdents(fd_t fd, void *dirp, unsigned int count) {
|
||||
DEFINE__REAL(int, __getdents30, int a, void *b, size_t c);
|
||||
return _REAL(__getdents30, fd, dirp, count);
|
||||
}
|
||||
|
||||
uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
|
||||
CHECK(&__lseek);
|
||||
return __lseek(fd, 0, offset, whence);
|
||||
}
|
||||
|
||||
uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
|
||||
Printf("internal_prctl not implemented for NetBSD");
|
||||
Die();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uptr internal_sigaltstack(const void *ss, void *oss) {
|
||||
DEFINE__REAL(int, __sigaltstack14, const void *a, void *b);
|
||||
return _REAL(__sigaltstack14, ss, oss);
|
||||
}
|
||||
|
||||
int internal_fork() {
|
||||
CHECK(&__fork);
|
||||
return __fork();
|
||||
}
|
||||
|
||||
int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
|
||||
uptr *oldlenp, const void *newp, uptr newlen) {
|
||||
CHECK(&__sysctl);
|
||||
return __sysctl(name, namelen, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
|
||||
}
|
||||
|
||||
int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
|
||||
const void *newp, uptr newlen) {
|
||||
DEFINE__REAL(int, sysctlbyname, const char *a, void *b, size_t *c,
|
||||
const void *d, size_t e);
|
||||
return _REAL(sysctlbyname, sname, oldp, (size_t *)oldlenp, newp,
|
||||
(size_t)newlen);
|
||||
}
|
||||
|
||||
uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
|
||||
__sanitizer_sigset_t *oldset) {
|
||||
CHECK(&_sys___sigprocmask14);
|
||||
return _sys___sigprocmask14(how, set, oldset);
|
||||
}
|
||||
|
||||
void internal_sigfillset(__sanitizer_sigset_t *set) {
|
||||
DEFINE__REAL(int, __sigfillset14, const void *a);
|
||||
(void)_REAL(__sigfillset14, set);
|
||||
}
|
||||
|
||||
void internal_sigemptyset(__sanitizer_sigset_t *set) {
|
||||
DEFINE__REAL(int, __sigemptyset14, const void *a);
|
||||
(void)_REAL(__sigemptyset14, set);
|
||||
}
|
||||
|
||||
void internal_sigdelset(__sanitizer_sigset_t *set, int signo) {
|
||||
DEFINE__REAL(int, __sigdelset14, const void *a, int b);
|
||||
(void)_REAL(__sigdelset14, set, signo);
|
||||
}
|
||||
|
||||
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags,
|
||||
void *arg) {
|
||||
DEFINE__REAL(int, clone, int (*a)(void *b), void *c, int d, void *e);
|
||||
|
||||
return _REAL(clone, fn, child_stack, flags, arg);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif
|
||||
115
lib/tsan/sanitizer_common/sanitizer_openbsd.cpp
Normal file
115
lib/tsan/sanitizer_common/sanitizer_openbsd.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
//===-- sanitizer_openbsd.cpp ---------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between various sanitizers' runtime libraries and
|
||||
// implements Solaris-specific functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_OPENBSD
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
#include "sanitizer_platform_limits_posix.h"
|
||||
#include "sanitizer_procmaps.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern char **environ;
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
uptr internal_mmap(void *addr, size_t length, int prot, int flags, int fd,
|
||||
u64 offset) {
|
||||
return (uptr)mmap(addr, length, prot, flags, fd, offset);
|
||||
}
|
||||
|
||||
uptr internal_munmap(void *addr, uptr length) { return munmap(addr, length); }
|
||||
|
||||
int internal_mprotect(void *addr, uptr length, int prot) {
|
||||
return mprotect(addr, length, prot);
|
||||
}
|
||||
|
||||
int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
|
||||
const void *newp, uptr newlen) {
|
||||
Printf("internal_sysctlbyname not implemented for OpenBSD");
|
||||
Die();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
|
||||
// On OpenBSD we cannot get the full path
|
||||
struct kinfo_proc kp;
|
||||
uptr kl;
|
||||
const int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
|
||||
if (internal_sysctl(Mib, ARRAY_SIZE(Mib), &kp, &kl, NULL, 0) != -1)
|
||||
return internal_snprintf(buf,
|
||||
(KI_MAXCOMLEN < buf_len ? KI_MAXCOMLEN : buf_len),
|
||||
"%s", kp.p_comm);
|
||||
return (uptr)0;
|
||||
}
|
||||
|
||||
static void GetArgsAndEnv(char ***argv, char ***envp) {
|
||||
uptr nargv;
|
||||
uptr nenv;
|
||||
int argvmib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV};
|
||||
int envmib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ENV};
|
||||
if (internal_sysctl(argvmib, 4, NULL, &nargv, NULL, 0) == -1) {
|
||||
Printf("sysctl KERN_PROC_NARGV failed\n");
|
||||
Die();
|
||||
}
|
||||
if (internal_sysctl(envmib, 4, NULL, &nenv, NULL, 0) == -1) {
|
||||
Printf("sysctl KERN_PROC_NENV failed\n");
|
||||
Die();
|
||||
}
|
||||
if (internal_sysctl(argvmib, 4, &argv, &nargv, NULL, 0) == -1) {
|
||||
Printf("sysctl KERN_PROC_ARGV failed\n");
|
||||
Die();
|
||||
}
|
||||
if (internal_sysctl(envmib, 4, &envp, &nenv, NULL, 0) == -1) {
|
||||
Printf("sysctl KERN_PROC_ENV failed\n");
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
char **GetArgv() {
|
||||
char **argv, **envp;
|
||||
GetArgsAndEnv(&argv, &envp);
|
||||
return argv;
|
||||
}
|
||||
|
||||
char **GetEnviron() {
|
||||
char **argv, **envp;
|
||||
GetArgsAndEnv(&argv, &envp);
|
||||
return envp;
|
||||
}
|
||||
|
||||
void ReExec() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_OPENBSD
|
||||
18
lib/tsan/sanitizer_common/sanitizer_persistent_allocator.cpp
Normal file
18
lib/tsan/sanitizer_common/sanitizer_persistent_allocator.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
//===-- sanitizer_persistent_allocator.cpp ----------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "sanitizer_persistent_allocator.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
PersistentAllocator thePersistentAllocator;
|
||||
|
||||
} // namespace __sanitizer
|
||||
71
lib/tsan/sanitizer_common/sanitizer_persistent_allocator.h
Normal file
71
lib/tsan/sanitizer_common/sanitizer_persistent_allocator.h
Normal file
@@ -0,0 +1,71 @@
|
||||
//===-- sanitizer_persistent_allocator.h ------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// A fast memory allocator that does not support free() nor realloc().
|
||||
// All allocations are forever.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_PERSISTENT_ALLOCATOR_H
|
||||
#define SANITIZER_PERSISTENT_ALLOCATOR_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
#include "sanitizer_atomic.h"
|
||||
#include "sanitizer_common.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
class PersistentAllocator {
|
||||
public:
|
||||
void *alloc(uptr size);
|
||||
|
||||
private:
|
||||
void *tryAlloc(uptr size);
|
||||
StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator.
|
||||
atomic_uintptr_t region_pos; // Region allocator for Node's.
|
||||
atomic_uintptr_t region_end;
|
||||
};
|
||||
|
||||
inline void *PersistentAllocator::tryAlloc(uptr size) {
|
||||
// Optimisic lock-free allocation, essentially try to bump the region ptr.
|
||||
for (;;) {
|
||||
uptr cmp = atomic_load(®ion_pos, memory_order_acquire);
|
||||
uptr end = atomic_load(®ion_end, memory_order_acquire);
|
||||
if (cmp == 0 || cmp + size > end) return nullptr;
|
||||
if (atomic_compare_exchange_weak(®ion_pos, &cmp, cmp + size,
|
||||
memory_order_acquire))
|
||||
return (void *)cmp;
|
||||
}
|
||||
}
|
||||
|
||||
inline void *PersistentAllocator::alloc(uptr size) {
|
||||
// First, try to allocate optimisitically.
|
||||
void *s = tryAlloc(size);
|
||||
if (s) return s;
|
||||
// If failed, lock, retry and alloc new superblock.
|
||||
SpinMutexLock l(&mtx);
|
||||
for (;;) {
|
||||
s = tryAlloc(size);
|
||||
if (s) return s;
|
||||
atomic_store(®ion_pos, 0, memory_order_relaxed);
|
||||
uptr allocsz = 64 * 1024;
|
||||
if (allocsz < size) allocsz = size;
|
||||
uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
|
||||
atomic_store(®ion_end, mem + allocsz, memory_order_release);
|
||||
atomic_store(®ion_pos, mem, memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
extern PersistentAllocator thePersistentAllocator;
|
||||
inline void *PersistentAlloc(uptr sz) {
|
||||
return thePersistentAllocator.alloc(sz);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_PERSISTENT_ALLOCATOR_H
|
||||
24
lib/tsan/sanitizer_common/sanitizer_placement_new.h
Normal file
24
lib/tsan/sanitizer_common/sanitizer_placement_new.h
Normal file
@@ -0,0 +1,24 @@
|
||||
//===-- sanitizer_placement_new.h -------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries.
|
||||
//
|
||||
// The file provides 'placement new'.
|
||||
// Do not include it into header files, only into source files.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_PLACEMENT_NEW_H
|
||||
#define SANITIZER_PLACEMENT_NEW_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
inline void *operator new(__sanitizer::operator_new_size_type sz, void *p) {
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif // SANITIZER_PLACEMENT_NEW_H
|
||||
364
lib/tsan/sanitizer_common/sanitizer_platform.h
Normal file
364
lib/tsan/sanitizer_common/sanitizer_platform.h
Normal file
@@ -0,0 +1,364 @@
|
||||
//===-- sanitizer_platform.h ------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Common platform macros.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_PLATFORM_H
|
||||
#define SANITIZER_PLATFORM_H
|
||||
|
||||
#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \
|
||||
!defined(__OpenBSD__) && !defined(__APPLE__) && !defined(_WIN32) && \
|
||||
!defined(__Fuchsia__) && !defined(__rtems__) && \
|
||||
!(defined(__sun__) && defined(__svr4__))
|
||||
# error "This operating system is not supported"
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
# define SANITIZER_LINUX 1
|
||||
#else
|
||||
# define SANITIZER_LINUX 0
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
# define SANITIZER_FREEBSD 1
|
||||
#else
|
||||
# define SANITIZER_FREEBSD 0
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
# define SANITIZER_NETBSD 1
|
||||
#else
|
||||
# define SANITIZER_NETBSD 0
|
||||
#endif
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
# define SANITIZER_OPENBSD 1
|
||||
#else
|
||||
# define SANITIZER_OPENBSD 0
|
||||
#endif
|
||||
|
||||
#if defined(__sun__) && defined(__svr4__)
|
||||
# define SANITIZER_SOLARIS 1
|
||||
#else
|
||||
# define SANITIZER_SOLARIS 0
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define SANITIZER_MAC 1
|
||||
# include <TargetConditionals.h>
|
||||
# if TARGET_OS_IPHONE
|
||||
# define SANITIZER_IOS 1
|
||||
# else
|
||||
# define SANITIZER_IOS 0
|
||||
# endif
|
||||
# if TARGET_OS_SIMULATOR
|
||||
# define SANITIZER_IOSSIM 1
|
||||
# else
|
||||
# define SANITIZER_IOSSIM 0
|
||||
# endif
|
||||
#else
|
||||
# define SANITIZER_MAC 0
|
||||
# define SANITIZER_IOS 0
|
||||
# define SANITIZER_IOSSIM 0
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && TARGET_OS_IPHONE && TARGET_OS_WATCH
|
||||
# define SANITIZER_WATCHOS 1
|
||||
#else
|
||||
# define SANITIZER_WATCHOS 0
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && TARGET_OS_IPHONE && TARGET_OS_TV
|
||||
# define SANITIZER_TVOS 1
|
||||
#else
|
||||
# define SANITIZER_TVOS 0
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define SANITIZER_WINDOWS 1
|
||||
#else
|
||||
# define SANITIZER_WINDOWS 0
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
# define SANITIZER_WINDOWS64 1
|
||||
#else
|
||||
# define SANITIZER_WINDOWS64 0
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
# define SANITIZER_ANDROID 1
|
||||
#else
|
||||
# define SANITIZER_ANDROID 0
|
||||
#endif
|
||||
|
||||
#if defined(__Fuchsia__)
|
||||
# define SANITIZER_FUCHSIA 1
|
||||
#else
|
||||
# define SANITIZER_FUCHSIA 0
|
||||
#endif
|
||||
|
||||
#if defined(__rtems__)
|
||||
# define SANITIZER_RTEMS 1
|
||||
#else
|
||||
# define SANITIZER_RTEMS 0
|
||||
#endif
|
||||
|
||||
#define SANITIZER_POSIX \
|
||||
(SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC || \
|
||||
SANITIZER_NETBSD || SANITIZER_OPENBSD || SANITIZER_SOLARIS)
|
||||
|
||||
#if __LP64__ || defined(_WIN64)
|
||||
# define SANITIZER_WORDSIZE 64
|
||||
#else
|
||||
# define SANITIZER_WORDSIZE 32
|
||||
#endif
|
||||
|
||||
#if SANITIZER_WORDSIZE == 64
|
||||
# define FIRST_32_SECOND_64(a, b) (b)
|
||||
#else
|
||||
# define FIRST_32_SECOND_64(a, b) (a)
|
||||
#endif
|
||||
|
||||
#if defined(__x86_64__) && !defined(_LP64)
|
||||
# define SANITIZER_X32 1
|
||||
#else
|
||||
# define SANITIZER_X32 0
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(_M_IX86)
|
||||
# define SANITIZER_I386 1
|
||||
#else
|
||||
# define SANITIZER_I386 0
|
||||
#endif
|
||||
|
||||
#if defined(__mips__)
|
||||
# define SANITIZER_MIPS 1
|
||||
# if defined(__mips64)
|
||||
# define SANITIZER_MIPS32 0
|
||||
# define SANITIZER_MIPS64 1
|
||||
# else
|
||||
# define SANITIZER_MIPS32 1
|
||||
# define SANITIZER_MIPS64 0
|
||||
# endif
|
||||
#else
|
||||
# define SANITIZER_MIPS 0
|
||||
# define SANITIZER_MIPS32 0
|
||||
# define SANITIZER_MIPS64 0
|
||||
#endif
|
||||
|
||||
#if defined(__s390__)
|
||||
# define SANITIZER_S390 1
|
||||
# if defined(__s390x__)
|
||||
# define SANITIZER_S390_31 0
|
||||
# define SANITIZER_S390_64 1
|
||||
# else
|
||||
# define SANITIZER_S390_31 1
|
||||
# define SANITIZER_S390_64 0
|
||||
# endif
|
||||
#else
|
||||
# define SANITIZER_S390 0
|
||||
# define SANITIZER_S390_31 0
|
||||
# define SANITIZER_S390_64 0
|
||||
#endif
|
||||
|
||||
#if defined(__powerpc__)
|
||||
# define SANITIZER_PPC 1
|
||||
# if defined(__powerpc64__)
|
||||
# define SANITIZER_PPC32 0
|
||||
# define SANITIZER_PPC64 1
|
||||
// 64-bit PPC has two ABIs (v1 and v2). The old powerpc64 target is
|
||||
// big-endian, and uses v1 ABI (known for its function descriptors),
|
||||
// while the new powerpc64le target is little-endian and uses v2.
|
||||
// In theory, you could convince gcc to compile for their evil twins
|
||||
// (eg. big-endian v2), but you won't find such combinations in the wild
|
||||
// (it'd require bootstrapping a whole system, which would be quite painful
|
||||
// - there's no target triple for that). LLVM doesn't support them either.
|
||||
# if _CALL_ELF == 2
|
||||
# define SANITIZER_PPC64V1 0
|
||||
# define SANITIZER_PPC64V2 1
|
||||
# else
|
||||
# define SANITIZER_PPC64V1 1
|
||||
# define SANITIZER_PPC64V2 0
|
||||
# endif
|
||||
# else
|
||||
# define SANITIZER_PPC32 1
|
||||
# define SANITIZER_PPC64 0
|
||||
# define SANITIZER_PPC64V1 0
|
||||
# define SANITIZER_PPC64V2 0
|
||||
# endif
|
||||
#else
|
||||
# define SANITIZER_PPC 0
|
||||
# define SANITIZER_PPC32 0
|
||||
# define SANITIZER_PPC64 0
|
||||
# define SANITIZER_PPC64V1 0
|
||||
# define SANITIZER_PPC64V2 0
|
||||
#endif
|
||||
|
||||
#if defined(__arm__)
|
||||
# define SANITIZER_ARM 1
|
||||
#else
|
||||
# define SANITIZER_ARM 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_SOLARIS && SANITIZER_WORDSIZE == 32
|
||||
# define SANITIZER_SOLARIS32 1
|
||||
#else
|
||||
# define SANITIZER_SOLARIS32 0
|
||||
#endif
|
||||
|
||||
#if defined(__myriad2__)
|
||||
# define SANITIZER_MYRIAD2 1
|
||||
#else
|
||||
# define SANITIZER_MYRIAD2 0
|
||||
#endif
|
||||
|
||||
// By default we allow to use SizeClassAllocator64 on 64-bit platform.
|
||||
// But in some cases (e.g. AArch64's 39-bit address space) SizeClassAllocator64
|
||||
// does not work well and we need to fallback to SizeClassAllocator32.
|
||||
// For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or
|
||||
// change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here.
|
||||
#ifndef SANITIZER_CAN_USE_ALLOCATOR64
|
||||
# if (SANITIZER_ANDROID && defined(__aarch64__)) || SANITIZER_FUCHSIA
|
||||
# define SANITIZER_CAN_USE_ALLOCATOR64 1
|
||||
# elif defined(__mips64) || defined(__aarch64__)
|
||||
# define SANITIZER_CAN_USE_ALLOCATOR64 0
|
||||
# else
|
||||
# define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// The range of addresses which can be returned my mmap.
|
||||
// FIXME: this value should be different on different platforms. Larger values
|
||||
// will still work but will consume more memory for TwoLevelByteMap.
|
||||
#if defined(__mips__)
|
||||
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40)
|
||||
#elif defined(__aarch64__)
|
||||
# if SANITIZER_MAC
|
||||
// Darwin iOS/ARM64 has a 36-bit VMA, 64GiB VM
|
||||
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 36)
|
||||
# else
|
||||
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 48)
|
||||
# endif
|
||||
#elif defined(__sparc__)
|
||||
#define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 52)
|
||||
#else
|
||||
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
|
||||
#endif
|
||||
|
||||
// Whether the addresses are sign-extended from the VMA range to the word.
|
||||
// The SPARC64 Linux port implements this to split the VMA space into two
|
||||
// non-contiguous halves with a huge hole in the middle.
|
||||
#if defined(__sparc__) && SANITIZER_WORDSIZE == 64
|
||||
#define SANITIZER_SIGN_EXTENDED_ADDRESSES 1
|
||||
#else
|
||||
#define SANITIZER_SIGN_EXTENDED_ADDRESSES 0
|
||||
#endif
|
||||
|
||||
// The AArch64 and RISC-V linux ports use the canonical syscall set as
|
||||
// mandated by the upstream linux community for all new ports. Other ports
|
||||
// may still use legacy syscalls.
|
||||
#ifndef SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
|
||||
# if (defined(__aarch64__) || defined(__riscv)) && SANITIZER_LINUX
|
||||
# define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 1
|
||||
# else
|
||||
# define SANITIZER_USES_CANONICAL_LINUX_SYSCALLS 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// udi16 syscalls can only be used when the following conditions are
|
||||
// met:
|
||||
// * target is one of arm32, x86-32, sparc32, sh or m68k
|
||||
// * libc version is libc5, glibc-2.0, glibc-2.1 or glibc-2.2 to 2.15
|
||||
// built against > linux-2.2 kernel headers
|
||||
// Since we don't want to include libc headers here, we check the
|
||||
// target only.
|
||||
#if defined(__arm__) || SANITIZER_X32 || defined(__sparc__)
|
||||
#define SANITIZER_USES_UID16_SYSCALLS 1
|
||||
#else
|
||||
#define SANITIZER_USES_UID16_SYSCALLS 0
|
||||
#endif
|
||||
|
||||
#if defined(__mips__)
|
||||
# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10)
|
||||
#else
|
||||
# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12)
|
||||
#endif
|
||||
|
||||
/// \macro MSC_PREREQ
|
||||
/// \brief Is the compiler MSVC of at least the specified version?
|
||||
/// The common \param version values to check for are:
|
||||
/// * 1800: Microsoft Visual Studio 2013 / 12.0
|
||||
/// * 1900: Microsoft Visual Studio 2015 / 14.0
|
||||
#ifdef _MSC_VER
|
||||
# define MSC_PREREQ(version) (_MSC_VER >= (version))
|
||||
#else
|
||||
# define MSC_PREREQ(version) 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_MAC && !(defined(__arm64__) && SANITIZER_IOS)
|
||||
# define SANITIZER_NON_UNIQUE_TYPEINFO 0
|
||||
#else
|
||||
# define SANITIZER_NON_UNIQUE_TYPEINFO 1
|
||||
#endif
|
||||
|
||||
// On linux, some architectures had an ABI transition from 64-bit long double
|
||||
// (ie. same as double) to 128-bit long double. On those, glibc symbols
|
||||
// involving long doubles come in two versions, and we need to pass the
|
||||
// correct one to dlvsym when intercepting them.
|
||||
#if SANITIZER_LINUX && (SANITIZER_S390 || SANITIZER_PPC32 || SANITIZER_PPC64V1)
|
||||
#define SANITIZER_NLDBL_VERSION "GLIBC_2.4"
|
||||
#endif
|
||||
|
||||
#if SANITIZER_GO == 0
|
||||
# define SANITIZER_GO 0
|
||||
#endif
|
||||
|
||||
// On PowerPC and ARM Thumb, calling pthread_exit() causes LSan to detect leaks.
|
||||
// pthread_exit() performs unwinding that leads to dlopen'ing libgcc_s.so.
|
||||
// dlopen mallocs "libgcc_s.so" string which confuses LSan, it fails to realize
|
||||
// that this allocation happens in dynamic linker and should be ignored.
|
||||
#if SANITIZER_PPC || defined(__thumb__)
|
||||
# define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 1
|
||||
#else
|
||||
# define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_NETBSD || \
|
||||
SANITIZER_OPENBSD || SANITIZER_SOLARIS
|
||||
# define SANITIZER_MADVISE_DONTNEED MADV_FREE
|
||||
#else
|
||||
# define SANITIZER_MADVISE_DONTNEED MADV_DONTNEED
|
||||
#endif
|
||||
|
||||
// Older gcc have issues aligning to a constexpr, and require an integer.
|
||||
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56859 among others.
|
||||
#if defined(__powerpc__) || defined(__powerpc64__)
|
||||
# define SANITIZER_CACHE_LINE_SIZE 128
|
||||
#else
|
||||
# define SANITIZER_CACHE_LINE_SIZE 64
|
||||
#endif
|
||||
|
||||
// Enable offline markup symbolizer for Fuchsia and RTEMS.
|
||||
#if SANITIZER_FUCHSIA || SANITIZER_RTEMS
|
||||
#define SANITIZER_SYMBOLIZER_MARKUP 1
|
||||
#else
|
||||
#define SANITIZER_SYMBOLIZER_MARKUP 0
|
||||
#endif
|
||||
|
||||
// Enable ability to support sanitizer initialization that is
|
||||
// compatible with the sanitizer library being loaded via
|
||||
// `dlopen()`.
|
||||
#if SANITIZER_MAC
|
||||
#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 1
|
||||
#else
|
||||
#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 0
|
||||
#endif
|
||||
|
||||
#endif // SANITIZER_PLATFORM_H
|
||||
609
lib/tsan/sanitizer_common/sanitizer_platform_interceptors.h
Normal file
609
lib/tsan/sanitizer_common/sanitizer_platform_interceptors.h
Normal file
@@ -0,0 +1,609 @@
|
||||
//===-- sanitizer_platform_interceptors.h -----------------------*- C++ -*-===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines macro telling whether sanitizer tools can/should intercept
|
||||
// given library functions on a given platform.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
|
||||
#define SANITIZER_PLATFORM_INTERCEPTORS_H
|
||||
|
||||
#include "sanitizer_glibc_version.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
#if SANITIZER_POSIX
|
||||
# define SI_POSIX 1
|
||||
#else
|
||||
# define SI_POSIX 0
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_WINDOWS
|
||||
# define SI_WINDOWS 0
|
||||
#else
|
||||
# define SI_WINDOWS 1
|
||||
#endif
|
||||
|
||||
#if SI_WINDOWS && SI_POSIX
|
||||
# error "Windows is not POSIX!"
|
||||
#endif
|
||||
|
||||
#if SI_POSIX
|
||||
# include "sanitizer_platform_limits_freebsd.h"
|
||||
# include "sanitizer_platform_limits_netbsd.h"
|
||||
# include "sanitizer_platform_limits_openbsd.h"
|
||||
# include "sanitizer_platform_limits_posix.h"
|
||||
# include "sanitizer_platform_limits_solaris.h"
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||
# define SI_LINUX_NOT_ANDROID 1
|
||||
#else
|
||||
# define SI_LINUX_NOT_ANDROID 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
# define SI_ANDROID 1
|
||||
#else
|
||||
# define SI_ANDROID 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_FREEBSD
|
||||
# define SI_FREEBSD 1
|
||||
#else
|
||||
# define SI_FREEBSD 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_NETBSD
|
||||
# define SI_NETBSD 1
|
||||
#else
|
||||
# define SI_NETBSD 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_OPENBSD
|
||||
#define SI_OPENBSD 1
|
||||
#else
|
||||
#define SI_OPENBSD 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
# define SI_LINUX 1
|
||||
#else
|
||||
# define SI_LINUX 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_MAC
|
||||
# define SI_MAC 1
|
||||
# define SI_NOT_MAC 0
|
||||
#else
|
||||
# define SI_MAC 0
|
||||
# define SI_NOT_MAC 1
|
||||
#endif
|
||||
|
||||
#if SANITIZER_IOS
|
||||
# define SI_IOS 1
|
||||
#else
|
||||
# define SI_IOS 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_IOSSIM
|
||||
# define SI_IOSSIM 1
|
||||
#else
|
||||
# define SI_IOSSIM 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_WATCHOS
|
||||
# define SI_WATCHOS 1
|
||||
#else
|
||||
# define SI_WATCHOS 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_TVOS
|
||||
# define SI_TVOS 1
|
||||
#else
|
||||
# define SI_TVOS 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_FUCHSIA
|
||||
# define SI_NOT_FUCHSIA 0
|
||||
#else
|
||||
# define SI_NOT_FUCHSIA 1
|
||||
#endif
|
||||
|
||||
#if SANITIZER_RTEMS
|
||||
# define SI_NOT_RTEMS 0
|
||||
#else
|
||||
# define SI_NOT_RTEMS 1
|
||||
#endif
|
||||
|
||||
#if SANITIZER_SOLARIS
|
||||
# define SI_SOLARIS 1
|
||||
#else
|
||||
# define SI_SOLARIS 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_SOLARIS32
|
||||
# define SI_SOLARIS32 1
|
||||
#else
|
||||
# define SI_SOLARIS32 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_POSIX && !SANITIZER_MAC
|
||||
# define SI_POSIX_NOT_MAC 1
|
||||
#else
|
||||
# define SI_POSIX_NOT_MAC 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX && !SANITIZER_FREEBSD
|
||||
# define SI_LINUX_NOT_FREEBSD 1
|
||||
# else
|
||||
# define SI_LINUX_NOT_FREEBSD 0
|
||||
#endif
|
||||
|
||||
#define SANITIZER_INTERCEPT_STRLEN SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_STRNLEN (SI_NOT_MAC && SI_NOT_FUCHSIA)
|
||||
#define SANITIZER_INTERCEPT_STRCMP SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_STRSTR SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_STRCASESTR SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_STRTOK SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_STRCHR SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_STRCHRNUL SI_POSIX_NOT_MAC
|
||||
#define SANITIZER_INTERCEPT_STRRCHR SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_STRSPN SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_STRPBRK SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_STRCASECMP SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_MEMSET 1
|
||||
#define SANITIZER_INTERCEPT_MEMMOVE 1
|
||||
#define SANITIZER_INTERCEPT_MEMCPY 1
|
||||
#define SANITIZER_INTERCEPT_MEMCMP SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_BCMP \
|
||||
SANITIZER_INTERCEPT_MEMCMP && \
|
||||
((SI_POSIX && _GNU_SOURCE) || SI_NETBSD || SI_OPENBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_STRNDUP SI_POSIX
|
||||
#define SANITIZER_INTERCEPT___STRNDUP SI_LINUX_NOT_FREEBSD
|
||||
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
|
||||
__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070
|
||||
# define SI_MAC_DEPLOYMENT_BELOW_10_7 1
|
||||
#else
|
||||
# define SI_MAC_DEPLOYMENT_BELOW_10_7 0
|
||||
#endif
|
||||
// memmem on Darwin doesn't exist on 10.6
|
||||
// FIXME: enable memmem on Windows.
|
||||
#define SANITIZER_INTERCEPT_MEMMEM (SI_POSIX && !SI_MAC_DEPLOYMENT_BELOW_10_7)
|
||||
#define SANITIZER_INTERCEPT_MEMCHR SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_MEMRCHR \
|
||||
(SI_FREEBSD || SI_LINUX || SI_NETBSD || SI_OPENBSD)
|
||||
|
||||
#define SANITIZER_INTERCEPT_READ SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PREAD SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_WRITE SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PWRITE SI_POSIX
|
||||
|
||||
#define SANITIZER_INTERCEPT_FREAD SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_FWRITE SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_FGETS SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_FPUTS SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PUTS SI_POSIX
|
||||
|
||||
#define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID || SI_SOLARIS32
|
||||
#define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID || SI_SOLARIS32
|
||||
|
||||
#define SANITIZER_INTERCEPT_READV SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_WRITEV SI_POSIX
|
||||
|
||||
#define SANITIZER_INTERCEPT_PREADV \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID
|
||||
|
||||
#define SANITIZER_INTERCEPT_PRCTL SI_LINUX
|
||||
|
||||
#define SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_STRPTIME SI_POSIX
|
||||
|
||||
#define SANITIZER_INTERCEPT_SCANF SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX_NOT_ANDROID
|
||||
|
||||
#ifndef SANITIZER_INTERCEPT_PRINTF
|
||||
# define SANITIZER_INTERCEPT_PRINTF SI_POSIX
|
||||
# define SANITIZER_INTERCEPT_PRINTF_L (SI_FREEBSD || SI_NETBSD)
|
||||
# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID
|
||||
#endif
|
||||
|
||||
#define SANITIZER_INTERCEPT___PRINTF_CHK \
|
||||
(SANITIZER_INTERCEPT_PRINTF && SI_LINUX_NOT_ANDROID)
|
||||
|
||||
#define SANITIZER_INTERCEPT_FREXP SI_NOT_FUCHSIA
|
||||
#define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_POSIX
|
||||
|
||||
#define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
|
||||
SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_GETPWENT \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
|
||||
SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_FGETGRENT_R \
|
||||
(SI_FREEBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_GETPWENT_R \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_FGETPWENT_R \
|
||||
(SI_FREEBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_SETPWENT \
|
||||
(SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_CLOCK_GETTIME \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_CLOCK_GETCPUCLOCKID SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_GETITIMER SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_TIME SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_GLOB SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_GLOB64 SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_WAIT SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_INET SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM (SI_POSIX && !SI_OPENBSD)
|
||||
#define SANITIZER_INTERCEPT_GETADDRINFO SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_GETNAMEINFO SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_GETSOCKNAME SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_GETHOSTBYNAME2 SI_POSIX && !SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R \
|
||||
(SI_FREEBSD || SI_LINUX || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R \
|
||||
(SI_FREEBSD || SI_LINUX_NOT_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R \
|
||||
(SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_GETHOSTENT_R \
|
||||
(SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_GETSOCKOPT SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_ACCEPT SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_ACCEPT4 \
|
||||
(SI_LINUX_NOT_ANDROID || SI_NETBSD || SI_OPENBSD)
|
||||
#define SANITIZER_INTERCEPT_PACCEPT SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_MODF SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_RECVMSG SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_SENDMSG SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_RECVMMSG SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_SENDMMSG SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_SYSMSG SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_GETPEERNAME SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_IOCTL SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_INET_ATON SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_SYSINFO SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_READDIR SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID || SI_SOLARIS32
|
||||
#if SI_LINUX_NOT_ANDROID && \
|
||||
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \
|
||||
defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
|
||||
defined(__s390__))
|
||||
#define SANITIZER_INTERCEPT_PTRACE 1
|
||||
#else
|
||||
#define SANITIZER_INTERCEPT_PTRACE 0
|
||||
#endif
|
||||
#define SANITIZER_INTERCEPT_SETLOCALE SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_GETCWD SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_STRTOIMAX SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_MBSTOWCS SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_MBSNRTOWCS \
|
||||
(SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_WCSTOMBS SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_STRXFRM SI_POSIX
|
||||
#define SANITIZER_INTERCEPT___STRXFRM_L SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_WCSXFRM SI_POSIX
|
||||
#define SANITIZER_INTERCEPT___WCSXFRM_L SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_WCSNRTOMBS \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
|
||||
SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_WCRTOMB \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
|
||||
SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_WCTOMB \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
|
||||
SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_REALPATH SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME \
|
||||
(SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_CONFSTR \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
|
||||
SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_SCHED_GETPARAM SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_STRERROR SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_STRERROR_R SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_SCANDIR \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID || SI_SOLARIS32
|
||||
#define SANITIZER_INTERCEPT_GETGROUPS SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_POLL SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_WORDEXP \
|
||||
(SI_FREEBSD || SI_NETBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID || \
|
||||
SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_SIGWAIT SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_SIGSETOPS \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_SIGPENDING SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_SIGPROCMASK SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_SIGMASK SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_BACKTRACE \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_STATFS \
|
||||
(SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_STATFS64 \
|
||||
(((SI_MAC && !TARGET_CPU_ARM64) && !SI_IOS) || SI_LINUX_NOT_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_STATVFS \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_INITGROUPS SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_ETHER_NTOA_ATON (SI_POSIX && !SI_OPENBSD)
|
||||
#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) || \
|
||||
SI_NETBSD || SI_OPENBSD || SI_SOLARIS) // NOLINT
|
||||
#define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET_SCHED (SI_POSIX && !SI_OPENBSD)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED \
|
||||
(SI_POSIX && !SI_NETBSD && !SI_OPENBSD)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE (SI_POSIX && !SI_OPENBSD)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL \
|
||||
(SI_MAC || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING \
|
||||
(SI_MAC || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST \
|
||||
(SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED \
|
||||
(SI_POSIX && !SI_NETBSD && !SI_OPENBSD)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED \
|
||||
(SI_POSIX && !SI_NETBSD && !SI_OPENBSD)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK \
|
||||
(SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED \
|
||||
(SI_LINUX_NOT_ANDROID && !SI_NETBSD && !SI_OPENBSD)
|
||||
#define SANITIZER_INTERCEPT_THR_EXIT SI_FREEBSD
|
||||
#define SANITIZER_INTERCEPT_TMPNAM SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_TTYNAME SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_TTYNAME_R SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_TEMPNAM SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_SINCOS SI_LINUX || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_REMQUO SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_REMQUOL (SI_POSIX && !SI_NETBSD)
|
||||
#define SANITIZER_INTERCEPT_LGAMMA SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_LGAMMAL (SI_POSIX && !SI_NETBSD)
|
||||
#define SANITIZER_INTERCEPT_LGAMMA_R (SI_FREEBSD || SI_LINUX || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_RAND_R \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
|
||||
SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_ICONV \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_TIMES SI_POSIX
|
||||
|
||||
// FIXME: getline seems to be available on OSX 10.7
|
||||
#define SANITIZER_INTERCEPT_GETLINE \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
|
||||
#define SANITIZER_INTERCEPT__EXIT \
|
||||
(SI_LINUX || SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_SOLARIS)
|
||||
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_MUTEX SI_POSIX
|
||||
#define SANITIZER_INTERCEPT___PTHREAD_MUTEX SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT___LIBC_MUTEX SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_GETNAME_NP \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
|
||||
#define SANITIZER_INTERCEPT_TLS_GET_ADDR \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
|
||||
|
||||
#define SANITIZER_INTERCEPT_LISTXATTR SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_GETXATTR SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_GETRESID SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_GETIFADDRS \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_MAC || \
|
||||
SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_IF_INDEXTONAME \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_MAC || \
|
||||
SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_CAPGET SI_LINUX_NOT_ANDROID
|
||||
#if SI_LINUX && defined(__arm__)
|
||||
#define SANITIZER_INTERCEPT_AEABI_MEM 1
|
||||
#else
|
||||
#define SANITIZER_INTERCEPT_AEABI_MEM 0
|
||||
#endif
|
||||
#define SANITIZER_INTERCEPT___BZERO SI_MAC || SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_BZERO SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_FTIME \
|
||||
(!SI_FREEBSD && !SI_NETBSD && !SI_OPENBSD && SI_POSIX)
|
||||
#define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID || SI_SOLARIS
|
||||
#define SANITIZER_INTERCEPT_TSEARCH \
|
||||
(SI_LINUX_NOT_ANDROID || SI_MAC || SI_NETBSD || SI_OPENBSD || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_FOPEN SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_FOPEN64 SI_LINUX_NOT_ANDROID || SI_SOLARIS32
|
||||
#define SANITIZER_INTERCEPT_OPEN_MEMSTREAM \
|
||||
(SI_LINUX_NOT_ANDROID || SI_NETBSD || SI_OPENBSD || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_FFLUSH SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_FCLOSE SI_POSIX
|
||||
|
||||
#ifndef SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
|
||||
#define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE \
|
||||
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_MAC || \
|
||||
SI_SOLARIS)
|
||||
#endif
|
||||
|
||||
#define SANITIZER_INTERCEPT_GETPASS \
|
||||
(SI_LINUX_NOT_ANDROID || SI_MAC || SI_NETBSD || SI_OPENBSD)
|
||||
#define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID
|
||||
|
||||
#define SANITIZER_INTERCEPT_MLOCKX SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_FOPENCOOKIE SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_SEM \
|
||||
(SI_LINUX || SI_FREEBSD || SI_NETBSD || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_SETCANCEL SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_MINCORE \
|
||||
(SI_LINUX || SI_NETBSD || SI_OPENBSD || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_PROCESS_VM_READV SI_LINUX
|
||||
#define SANITIZER_INTERCEPT_CTERMID \
|
||||
(SI_LINUX || SI_MAC || SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_CTERMID_R (SI_MAC || SI_FREEBSD || SI_SOLARIS)
|
||||
|
||||
#define SANITIZER_INTERCEPTOR_HOOKS \
|
||||
(SI_LINUX || SI_MAC || SI_WINDOWS || SI_NETBSD)
|
||||
#define SANITIZER_INTERCEPT_RECV_RECVFROM SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_SEND_SENDTO SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_EVENTFD_READ_WRITE SI_LINUX
|
||||
|
||||
#define SANITIZER_INTERCEPT_STAT \
|
||||
(SI_FREEBSD || SI_MAC || SI_ANDROID || SI_NETBSD || SI_OPENBSD || SI_SOLARIS)
|
||||
#define SANITIZER_INTERCEPT_LSTAT (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT___XSTAT (!SANITIZER_INTERCEPT_STAT && SI_POSIX)
|
||||
#define SANITIZER_INTERCEPT___XSTAT64 SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT___LXSTAT SANITIZER_INTERCEPT___XSTAT
|
||||
#define SANITIZER_INTERCEPT___LXSTAT64 SI_LINUX_NOT_ANDROID
|
||||
|
||||
#define SANITIZER_INTERCEPT_UTMP \
|
||||
(SI_POSIX && !SI_MAC && !SI_FREEBSD && !SI_NETBSD)
|
||||
#define SANITIZER_INTERCEPT_UTMPX \
|
||||
(SI_LINUX_NOT_ANDROID || SI_MAC || SI_FREEBSD || SI_NETBSD)
|
||||
|
||||
#define SANITIZER_INTERCEPT_GETLOADAVG \
|
||||
(SI_LINUX_NOT_ANDROID || SI_MAC || SI_FREEBSD || SI_NETBSD || SI_OPENBSD)
|
||||
|
||||
#define SANITIZER_INTERCEPT_MMAP SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_MMAP64 SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO \
|
||||
(!SI_FREEBSD && !SI_MAC && !SI_NETBSD && !SI_OPENBSD && SI_NOT_FUCHSIA && \
|
||||
SI_NOT_RTEMS)
|
||||
#define SANITIZER_INTERCEPT_MEMALIGN \
|
||||
(!SI_FREEBSD && !SI_MAC && !SI_NETBSD && !SI_OPENBSD && SI_NOT_RTEMS)
|
||||
#define SANITIZER_INTERCEPT_PVALLOC \
|
||||
(!SI_FREEBSD && !SI_MAC && !SI_NETBSD && !SI_OPENBSD && SI_NOT_FUCHSIA && \
|
||||
SI_NOT_RTEMS)
|
||||
#define SANITIZER_INTERCEPT_CFREE \
|
||||
(!SI_FREEBSD && !SI_MAC && !SI_NETBSD && !SI_OPENBSD && SI_NOT_FUCHSIA && \
|
||||
SI_NOT_RTEMS)
|
||||
#define SANITIZER_INTERCEPT_REALLOCARRAY SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_ALIGNED_ALLOC (!SI_MAC && SI_NOT_RTEMS)
|
||||
#define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE \
|
||||
(!SI_MAC && !SI_OPENBSD && !SI_NETBSD)
|
||||
#define SANITIZER_INTERCEPT_MCHECK_MPROBE SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_WCSCAT SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_WCSDUP SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_SIGNAL_AND_SIGACTION (!SI_WINDOWS && SI_NOT_FUCHSIA)
|
||||
#define SANITIZER_INTERCEPT_BSD_SIGNAL SI_ANDROID
|
||||
|
||||
#define SANITIZER_INTERCEPT_ACCT (SI_NETBSD || SI_OPENBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_USER_FROM_UID SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_UID_FROM_USER SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_GROUP_FROM_GID SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_GID_FROM_GROUP SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_ACCESS (SI_NETBSD || SI_OPENBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_FACCESSAT (SI_NETBSD || SI_OPENBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_GETGROUPLIST (SI_NETBSD || SI_OPENBSD)
|
||||
#define SANITIZER_INTERCEPT_STRLCPY \
|
||||
(SI_NETBSD || SI_FREEBSD || SI_OPENBSD || SI_MAC || SI_ANDROID)
|
||||
|
||||
#define SANITIZER_INTERCEPT_NAME_TO_HANDLE_AT SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_OPEN_BY_HANDLE_AT SI_LINUX_NOT_ANDROID
|
||||
|
||||
#define SANITIZER_INTERCEPT_READLINK SI_POSIX
|
||||
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
|
||||
__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101000
|
||||
# define SI_MAC_DEPLOYMENT_BELOW_10_10 1
|
||||
#else
|
||||
# define SI_MAC_DEPLOYMENT_BELOW_10_10 0
|
||||
#endif
|
||||
#define SANITIZER_INTERCEPT_READLINKAT \
|
||||
(SI_POSIX && !SI_MAC_DEPLOYMENT_BELOW_10_10)
|
||||
|
||||
#define SANITIZER_INTERCEPT_DEVNAME (SI_NETBSD || SI_OPENBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_DEVNAME_R (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_FGETLN (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_STRMODE (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_TTYENT SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_PROTOENT (SI_NETBSD || SI_LINUX)
|
||||
#define SANITIZER_INTERCEPT_PROTOENT_R (SI_LINUX_NOT_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_NETENT SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_SETVBUF (SI_NETBSD || SI_FREEBSD || \
|
||||
SI_LINUX || SI_MAC)
|
||||
#define SANITIZER_INTERCEPT_GETMNTINFO (SI_NETBSD || SI_FREEBSD || SI_MAC)
|
||||
#define SANITIZER_INTERCEPT_MI_VECTOR_HASH SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_GETVFSSTAT SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_REGEX (SI_NETBSD || SI_FREEBSD || SI_LINUX)
|
||||
#define SANITIZER_INTERCEPT_REGEXSUB SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_FTS (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_SYSCTL (SI_NETBSD || SI_FREEBSD || SI_MAC)
|
||||
#define SANITIZER_INTERCEPT_ASYSCTL SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_SYSCTLGETMIBINFO SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_NL_LANGINFO (SI_NETBSD || SI_FREEBSD || SI_MAC)
|
||||
#define SANITIZER_INTERCEPT_MODCTL SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_CAPSICUM SI_FREEBSD
|
||||
#define SANITIZER_INTERCEPT_STRTONUM (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_FPARSELN SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_STATVFS1 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_STRTOI SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_CAPSICUM SI_FREEBSD
|
||||
#define SANITIZER_INTERCEPT_SHA1 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_MD4 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_RMD160 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_MD5 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_FSEEK (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_MD2 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_SHA2 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_CDB SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_VIS (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_POPEN SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_POPENVE SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_PCLOSE SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_FUNOPEN (SI_NETBSD || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT_FUNOPEN2 SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_GETFSENT (SI_FREEBSD || SI_NETBSD || SI_MAC)
|
||||
#define SANITIZER_INTERCEPT_ARC4RANDOM (SI_FREEBSD || SI_NETBSD || SI_MAC)
|
||||
#define SANITIZER_INTERCEPT_FDEVNAME SI_FREEBSD
|
||||
#define SANITIZER_INTERCEPT_GETUSERSHELL (SI_POSIX && !SI_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_SL_INIT (SI_FREEBSD || SI_NETBSD)
|
||||
#define SANITIZER_INTERCEPT_CRYPT (SI_POSIX && !SI_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_CRYPT_R (SI_LINUX && !SI_ANDROID)
|
||||
|
||||
#define SANITIZER_INTERCEPT_GETRANDOM \
|
||||
((SI_LINUX && __GLIBC_PREREQ(2, 25)) || SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT___CXA_ATEXIT SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_ATEXIT SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_PTHREAD_ATFORK SI_NETBSD
|
||||
#define SANITIZER_INTERCEPT_GETENTROPY SI_FREEBSD
|
||||
#define SANITIZER_INTERCEPT_QSORT \
|
||||
(SI_POSIX && !SI_IOSSIM && !SI_WATCHOS && !SI_TVOS && !SI_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_QSORT_R (SI_LINUX && !SI_ANDROID)
|
||||
// sigaltstack on i386 macOS cannot be intercepted due to setjmp()
|
||||
// calling it and assuming that it does not clobber registers.
|
||||
#define SANITIZER_INTERCEPT_SIGALTSTACK \
|
||||
(SI_POSIX && !(SANITIZER_MAC && SANITIZER_I386))
|
||||
#define SANITIZER_INTERCEPT_UNAME (SI_POSIX && !SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT___XUNAME SI_FREEBSD
|
||||
|
||||
#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
|
||||
531
lib/tsan/sanitizer_common/sanitizer_platform_limits_freebsd.cpp
Normal file
531
lib/tsan/sanitizer_common/sanitizer_platform_limits_freebsd.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
//===-- sanitizer_platform_limits_freebsd.cpp -----------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer common code.
|
||||
//
|
||||
// Sizes and layouts of platform-specific FreeBSD data structures.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_FREEBSD
|
||||
|
||||
#include <sys/capsicum.h>
|
||||
#include <sys/consio.h>
|
||||
#include <sys/filio.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/kbio.h>
|
||||
#include <sys/link_elf.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mqueue.h>
|
||||
#include <sys/msg.h>
|
||||
#include <sys/mtio.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/soundcard.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timeb.h>
|
||||
#include <sys/times.h>
|
||||
#include <sys/timespec.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/utsname.h>
|
||||
//
|
||||
#include <arpa/inet.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if.h>
|
||||
#include <net/ppp_defs.h>
|
||||
#include <net/route.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_mroute.h>
|
||||
//
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fstab.h>
|
||||
#include <fts.h>
|
||||
#include <glob.h>
|
||||
#include <grp.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <limits.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <regex.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stringlist.h>
|
||||
#include <term.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <utime.h>
|
||||
#include <utmpx.h>
|
||||
#include <vis.h>
|
||||
#include <wchar.h>
|
||||
#include <wordexp.h>
|
||||
|
||||
#define _KERNEL // to declare 'shminfo' structure
|
||||
#include <sys/shm.h>
|
||||
#undef _KERNEL
|
||||
|
||||
#undef INLINE // to avoid clashes with sanitizers' definitions
|
||||
|
||||
#undef IOC_DIRMASK
|
||||
|
||||
// Include these after system headers to avoid name clashes and ambiguities.
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_platform_limits_freebsd.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
void *__sanitizer_get_link_map_by_dlopen_handle(void *handle) {
|
||||
void *p = nullptr;
|
||||
return internal_dlinfo(handle, RTLD_DI_LINKMAP, &p) == 0 ? p : nullptr;
|
||||
}
|
||||
|
||||
unsigned struct_cap_rights_sz = sizeof(cap_rights_t);
|
||||
unsigned struct_utsname_sz = sizeof(struct utsname);
|
||||
unsigned struct_stat_sz = sizeof(struct stat);
|
||||
unsigned struct_rusage_sz = sizeof(struct rusage);
|
||||
unsigned struct_tm_sz = sizeof(struct tm);
|
||||
unsigned struct_passwd_sz = sizeof(struct passwd);
|
||||
unsigned struct_group_sz = sizeof(struct group);
|
||||
unsigned siginfo_t_sz = sizeof(siginfo_t);
|
||||
unsigned struct_sigaction_sz = sizeof(struct sigaction);
|
||||
unsigned struct_stack_t_sz = sizeof(stack_t);
|
||||
unsigned struct_itimerval_sz = sizeof(struct itimerval);
|
||||
unsigned pthread_t_sz = sizeof(pthread_t);
|
||||
unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
|
||||
unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
|
||||
unsigned pid_t_sz = sizeof(pid_t);
|
||||
unsigned timeval_sz = sizeof(timeval);
|
||||
unsigned uid_t_sz = sizeof(uid_t);
|
||||
unsigned gid_t_sz = sizeof(gid_t);
|
||||
unsigned fpos_t_sz = sizeof(fpos_t);
|
||||
unsigned mbstate_t_sz = sizeof(mbstate_t);
|
||||
unsigned sigset_t_sz = sizeof(sigset_t);
|
||||
unsigned struct_timezone_sz = sizeof(struct timezone);
|
||||
unsigned struct_tms_sz = sizeof(struct tms);
|
||||
unsigned struct_sigevent_sz = sizeof(struct sigevent);
|
||||
unsigned struct_sched_param_sz = sizeof(struct sched_param);
|
||||
unsigned struct_statfs_sz = sizeof(struct statfs);
|
||||
unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
|
||||
unsigned ucontext_t_sz = sizeof(ucontext_t);
|
||||
unsigned struct_rlimit_sz = sizeof(struct rlimit);
|
||||
unsigned struct_timespec_sz = sizeof(struct timespec);
|
||||
unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
|
||||
unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
|
||||
unsigned struct_timeb_sz = sizeof(struct timeb);
|
||||
unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
|
||||
unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
|
||||
unsigned struct_statvfs_sz = sizeof(struct statvfs);
|
||||
unsigned struct_shminfo_sz = sizeof(struct shminfo);
|
||||
unsigned struct_shm_info_sz = sizeof(struct shm_info);
|
||||
unsigned struct_regmatch_sz = sizeof(regmatch_t);
|
||||
unsigned struct_regex_sz = sizeof(regex_t);
|
||||
unsigned struct_fstab_sz = sizeof(struct fstab);
|
||||
unsigned struct_FTS_sz = sizeof(FTS);
|
||||
unsigned struct_FTSENT_sz = sizeof(FTSENT);
|
||||
unsigned struct_StringList_sz = sizeof(StringList);
|
||||
|
||||
const uptr sig_ign = (uptr)SIG_IGN;
|
||||
const uptr sig_dfl = (uptr)SIG_DFL;
|
||||
const uptr sig_err = (uptr)SIG_ERR;
|
||||
const uptr sa_siginfo = (uptr)SA_SIGINFO;
|
||||
|
||||
int shmctl_ipc_stat = (int)IPC_STAT;
|
||||
int shmctl_ipc_info = (int)IPC_INFO;
|
||||
int shmctl_shm_info = (int)SHM_INFO;
|
||||
int shmctl_shm_stat = (int)SHM_STAT;
|
||||
unsigned struct_utmpx_sz = sizeof(struct utmpx);
|
||||
|
||||
int map_fixed = MAP_FIXED;
|
||||
|
||||
int af_inet = (int)AF_INET;
|
||||
int af_inet6 = (int)AF_INET6;
|
||||
|
||||
uptr __sanitizer_in_addr_sz(int af) {
|
||||
if (af == AF_INET)
|
||||
return sizeof(struct in_addr);
|
||||
else if (af == AF_INET6)
|
||||
return sizeof(struct in6_addr);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
|
||||
int glob_nomatch = GLOB_NOMATCH;
|
||||
int glob_altdirfunc = GLOB_ALTDIRFUNC;
|
||||
|
||||
unsigned path_max = PATH_MAX;
|
||||
|
||||
// ioctl arguments
|
||||
unsigned struct_ifreq_sz = sizeof(struct ifreq);
|
||||
unsigned struct_termios_sz = sizeof(struct termios);
|
||||
unsigned struct_winsize_sz = sizeof(struct winsize);
|
||||
#if SOUND_VERSION >= 0x040000
|
||||
unsigned struct_copr_buffer_sz = 0;
|
||||
unsigned struct_copr_debug_buf_sz = 0;
|
||||
unsigned struct_copr_msg_sz = 0;
|
||||
#else
|
||||
unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer);
|
||||
unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf);
|
||||
unsigned struct_copr_msg_sz = sizeof(struct copr_msg);
|
||||
#endif
|
||||
unsigned struct_midi_info_sz = sizeof(struct midi_info);
|
||||
unsigned struct_mtget_sz = sizeof(struct mtget);
|
||||
unsigned struct_mtop_sz = sizeof(struct mtop);
|
||||
unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument);
|
||||
unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec);
|
||||
unsigned struct_synth_info_sz = sizeof(struct synth_info);
|
||||
unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
|
||||
unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
|
||||
unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
|
||||
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
|
||||
const unsigned long __sanitizer_bufsiz = BUFSIZ;
|
||||
|
||||
const unsigned IOCTL_NOT_PRESENT = 0;
|
||||
|
||||
unsigned IOCTL_FIOASYNC = FIOASYNC;
|
||||
unsigned IOCTL_FIOCLEX = FIOCLEX;
|
||||
unsigned IOCTL_FIOGETOWN = FIOGETOWN;
|
||||
unsigned IOCTL_FIONBIO = FIONBIO;
|
||||
unsigned IOCTL_FIONCLEX = FIONCLEX;
|
||||
unsigned IOCTL_FIOSETOWN = FIOSETOWN;
|
||||
unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
|
||||
unsigned IOCTL_SIOCATMARK = SIOCATMARK;
|
||||
unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
|
||||
unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
|
||||
unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
|
||||
unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
|
||||
unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
|
||||
unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
|
||||
unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
|
||||
unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
|
||||
unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
|
||||
unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
|
||||
unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
|
||||
unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
|
||||
unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
|
||||
unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
|
||||
unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
|
||||
unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
|
||||
unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
|
||||
unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
|
||||
unsigned IOCTL_TIOCCONS = TIOCCONS;
|
||||
unsigned IOCTL_TIOCEXCL = TIOCEXCL;
|
||||
unsigned IOCTL_TIOCGETD = TIOCGETD;
|
||||
unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
|
||||
unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
|
||||
unsigned IOCTL_TIOCMBIC = TIOCMBIC;
|
||||
unsigned IOCTL_TIOCMBIS = TIOCMBIS;
|
||||
unsigned IOCTL_TIOCMGET = TIOCMGET;
|
||||
unsigned IOCTL_TIOCMSET = TIOCMSET;
|
||||
unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
|
||||
unsigned IOCTL_TIOCNXCL = TIOCNXCL;
|
||||
unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
|
||||
unsigned IOCTL_TIOCPKT = TIOCPKT;
|
||||
unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
|
||||
unsigned IOCTL_TIOCSETD = TIOCSETD;
|
||||
unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
|
||||
unsigned IOCTL_TIOCSTI = TIOCSTI;
|
||||
unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
|
||||
unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
|
||||
unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
|
||||
unsigned IOCTL_MTIOCGET = MTIOCGET;
|
||||
unsigned IOCTL_MTIOCTOP = MTIOCTOP;
|
||||
unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
|
||||
unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS;
|
||||
unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK;
|
||||
unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST;
|
||||
unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET;
|
||||
unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT;
|
||||
unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT;
|
||||
unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED;
|
||||
unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO;
|
||||
unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE;
|
||||
unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC;
|
||||
unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE;
|
||||
unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR;
|
||||
unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO;
|
||||
unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME;
|
||||
unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE;
|
||||
unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT;
|
||||
unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT;
|
||||
unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS;
|
||||
unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS;
|
||||
unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND;
|
||||
unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC;
|
||||
unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE;
|
||||
unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET;
|
||||
unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES;
|
||||
unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC;
|
||||
unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI;
|
||||
unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD;
|
||||
unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO;
|
||||
unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL;
|
||||
unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE;
|
||||
unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME;
|
||||
unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT;
|
||||
unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE;
|
||||
unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START;
|
||||
unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP;
|
||||
unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO;
|
||||
unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE;
|
||||
unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE;
|
||||
unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME;
|
||||
unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE;
|
||||
unsigned IOCTL_VT_GETMODE = VT_GETMODE;
|
||||
unsigned IOCTL_VT_OPENQRY = VT_OPENQRY;
|
||||
unsigned IOCTL_VT_RELDISP = VT_RELDISP;
|
||||
unsigned IOCTL_VT_SETMODE = VT_SETMODE;
|
||||
unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
|
||||
unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP;
|
||||
unsigned IOCTL_KDDISABIO = KDDISABIO;
|
||||
unsigned IOCTL_KDENABIO = KDENABIO;
|
||||
unsigned IOCTL_KDGETLED = KDGETLED;
|
||||
unsigned IOCTL_KDGETMODE = KDGETMODE;
|
||||
unsigned IOCTL_KDGKBMODE = KDGKBMODE;
|
||||
unsigned IOCTL_KDGKBTYPE = KDGKBTYPE;
|
||||
unsigned IOCTL_KDMKTONE = KDMKTONE;
|
||||
unsigned IOCTL_KDSETLED = KDSETLED;
|
||||
unsigned IOCTL_KDSETMODE = KDSETMODE;
|
||||
unsigned IOCTL_KDSKBMODE = KDSKBMODE;
|
||||
unsigned IOCTL_KIOCSOUND = KIOCSOUND;
|
||||
unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP;
|
||||
unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
|
||||
|
||||
const int si_SEGV_MAPERR = SEGV_MAPERR;
|
||||
const int si_SEGV_ACCERR = SEGV_ACCERR;
|
||||
const int unvis_valid = UNVIS_VALID;
|
||||
const int unvis_validpush = UNVIS_VALIDPUSH;
|
||||
} // namespace __sanitizer
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
|
||||
|
||||
COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
|
||||
CHECK_TYPE_SIZE(pthread_key_t);
|
||||
|
||||
// There are more undocumented fields in dl_phdr_info that we are not interested
|
||||
// in.
|
||||
COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
|
||||
|
||||
CHECK_TYPE_SIZE(glob_t);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
|
||||
|
||||
CHECK_TYPE_SIZE(addrinfo);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
|
||||
|
||||
CHECK_TYPE_SIZE(hostent);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_name);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_length);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
|
||||
|
||||
CHECK_TYPE_SIZE(iovec);
|
||||
CHECK_SIZE_AND_OFFSET(iovec, iov_base);
|
||||
CHECK_SIZE_AND_OFFSET(iovec, iov_len);
|
||||
|
||||
CHECK_TYPE_SIZE(msghdr);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
|
||||
|
||||
CHECK_TYPE_SIZE(cmsghdr);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_ino);
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
|
||||
|
||||
CHECK_TYPE_SIZE(ifconf);
|
||||
CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
|
||||
CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
|
||||
|
||||
CHECK_TYPE_SIZE(pollfd);
|
||||
CHECK_SIZE_AND_OFFSET(pollfd, fd);
|
||||
CHECK_SIZE_AND_OFFSET(pollfd, events);
|
||||
CHECK_SIZE_AND_OFFSET(pollfd, revents);
|
||||
|
||||
CHECK_TYPE_SIZE(nfds_t);
|
||||
|
||||
CHECK_TYPE_SIZE(sigset_t);
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
|
||||
// Can't write checks for sa_handler and sa_sigaction due to them being
|
||||
// preprocessor macros.
|
||||
CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
|
||||
|
||||
CHECK_TYPE_SIZE(wordexp_t);
|
||||
CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
|
||||
CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
|
||||
CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
|
||||
|
||||
CHECK_TYPE_SIZE(tm);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_sec);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_min);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_hour);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_mday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_mon);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_year);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_wday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_yday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_zone);
|
||||
|
||||
CHECK_TYPE_SIZE(ether_addr);
|
||||
|
||||
CHECK_TYPE_SIZE(ipc_perm);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, key);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
|
||||
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);
|
||||
|
||||
CHECK_TYPE_SIZE(shmid_ds);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
|
||||
|
||||
CHECK_TYPE_SIZE(clock_t);
|
||||
|
||||
CHECK_TYPE_SIZE(ifaddrs);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
|
||||
#undef ifa_dstaddr
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
|
||||
|
||||
CHECK_TYPE_SIZE(timeb);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, time);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, millitm);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, timezone);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, dstflag);
|
||||
|
||||
CHECK_TYPE_SIZE(passwd);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_name);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
|
||||
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
|
||||
|
||||
CHECK_TYPE_SIZE(group);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_name);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_passwd);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_gid);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_mem);
|
||||
|
||||
#if HAVE_RPC_XDR_H
|
||||
CHECK_TYPE_SIZE(XDR);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_op);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_ops);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_public);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_private);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_base);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_handy);
|
||||
COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE);
|
||||
COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE);
|
||||
COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE);
|
||||
#endif
|
||||
|
||||
CHECK_TYPE_SIZE(sem_t);
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_cap_rights_t) >= sizeof(cap_rights_t));
|
||||
#endif // SANITIZER_FREEBSD
|
||||
656
lib/tsan/sanitizer_common/sanitizer_platform_limits_freebsd.h
Normal file
656
lib/tsan/sanitizer_common/sanitizer_platform_limits_freebsd.h
Normal file
@@ -0,0 +1,656 @@
|
||||
//===-- sanitizer_platform_limits_freebsd.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer common code.
|
||||
//
|
||||
// Sizes and layouts of platform-specific FreeBSD data structures.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_PLATFORM_LIMITS_FREEBSD_H
|
||||
#define SANITIZER_PLATFORM_LIMITS_FREEBSD_H
|
||||
|
||||
#if SANITIZER_FREEBSD
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_platform.h"
|
||||
#include "sanitizer_platform_limits_posix.h"
|
||||
|
||||
// Get sys/_types.h, because that tells us whether 64-bit inodes are
|
||||
// used in struct dirent below.
|
||||
#include <sys/_types.h>
|
||||
|
||||
namespace __sanitizer {
|
||||
void *__sanitizer_get_link_map_by_dlopen_handle(void *handle);
|
||||
#define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
|
||||
(link_map *)__sanitizer_get_link_map_by_dlopen_handle(handle)
|
||||
|
||||
extern unsigned struct_utsname_sz;
|
||||
extern unsigned struct_stat_sz;
|
||||
#if defined(__powerpc64__)
|
||||
const unsigned struct___old_kernel_stat_sz = 0;
|
||||
#else
|
||||
const unsigned struct___old_kernel_stat_sz = 32;
|
||||
#endif
|
||||
extern unsigned struct_rusage_sz;
|
||||
extern unsigned siginfo_t_sz;
|
||||
extern unsigned struct_itimerval_sz;
|
||||
extern unsigned pthread_t_sz;
|
||||
extern unsigned pthread_mutex_t_sz;
|
||||
extern unsigned pthread_cond_t_sz;
|
||||
extern unsigned pid_t_sz;
|
||||
extern unsigned timeval_sz;
|
||||
extern unsigned uid_t_sz;
|
||||
extern unsigned gid_t_sz;
|
||||
extern unsigned fpos_t_sz;
|
||||
extern unsigned mbstate_t_sz;
|
||||
extern unsigned struct_timezone_sz;
|
||||
extern unsigned struct_tms_sz;
|
||||
extern unsigned struct_itimerspec_sz;
|
||||
extern unsigned struct_sigevent_sz;
|
||||
extern unsigned struct_stack_t_sz;
|
||||
extern unsigned struct_sched_param_sz;
|
||||
extern unsigned struct_statfs64_sz;
|
||||
extern unsigned struct_statfs_sz;
|
||||
extern unsigned struct_sockaddr_sz;
|
||||
extern unsigned ucontext_t_sz;
|
||||
extern unsigned struct_rlimit_sz;
|
||||
extern unsigned struct_utimbuf_sz;
|
||||
extern unsigned struct_timespec_sz;
|
||||
extern unsigned struct_regmatch_sz;
|
||||
extern unsigned struct_regex_sz;
|
||||
extern unsigned struct_FTS_sz;
|
||||
extern unsigned struct_FTSENT_sz;
|
||||
extern const int unvis_valid;
|
||||
extern const int unvis_validpush;
|
||||
|
||||
struct __sanitizer_iocb {
|
||||
u64 aio_data;
|
||||
u32 aio_key_or_aio_reserved1; // Simply crazy.
|
||||
u32 aio_reserved1_or_aio_key; // Luckily, we don't need these.
|
||||
u16 aio_lio_opcode;
|
||||
s16 aio_reqprio;
|
||||
u32 aio_fildes;
|
||||
u64 aio_buf;
|
||||
u64 aio_nbytes;
|
||||
s64 aio_offset;
|
||||
u64 aio_reserved2;
|
||||
u64 aio_reserved3;
|
||||
};
|
||||
|
||||
struct __sanitizer_io_event {
|
||||
u64 data;
|
||||
u64 obj;
|
||||
u64 res;
|
||||
u64 res2;
|
||||
};
|
||||
|
||||
const unsigned iocb_cmd_pread = 0;
|
||||
const unsigned iocb_cmd_pwrite = 1;
|
||||
const unsigned iocb_cmd_preadv = 7;
|
||||
const unsigned iocb_cmd_pwritev = 8;
|
||||
|
||||
struct __sanitizer___sysctl_args {
|
||||
int *name;
|
||||
int nlen;
|
||||
void *oldval;
|
||||
uptr *oldlenp;
|
||||
void *newval;
|
||||
uptr newlen;
|
||||
unsigned long ___unused[4];
|
||||
};
|
||||
|
||||
struct __sanitizer_ipc_perm {
|
||||
unsigned int cuid;
|
||||
unsigned int cgid;
|
||||
unsigned int uid;
|
||||
unsigned int gid;
|
||||
unsigned short mode;
|
||||
unsigned short seq;
|
||||
long key;
|
||||
};
|
||||
|
||||
#if !defined(__i386__)
|
||||
typedef long long __sanitizer_time_t;
|
||||
#else
|
||||
typedef long __sanitizer_time_t;
|
||||
#endif
|
||||
|
||||
struct __sanitizer_shmid_ds {
|
||||
__sanitizer_ipc_perm shm_perm;
|
||||
unsigned long shm_segsz;
|
||||
unsigned int shm_lpid;
|
||||
unsigned int shm_cpid;
|
||||
int shm_nattch;
|
||||
__sanitizer_time_t shm_atime;
|
||||
__sanitizer_time_t shm_dtime;
|
||||
__sanitizer_time_t shm_ctime;
|
||||
};
|
||||
|
||||
extern unsigned struct_msqid_ds_sz;
|
||||
extern unsigned struct_mq_attr_sz;
|
||||
extern unsigned struct_timeb_sz;
|
||||
extern unsigned struct_statvfs_sz;
|
||||
|
||||
struct __sanitizer_iovec {
|
||||
void *iov_base;
|
||||
uptr iov_len;
|
||||
};
|
||||
|
||||
struct __sanitizer_ifaddrs {
|
||||
struct __sanitizer_ifaddrs *ifa_next;
|
||||
char *ifa_name;
|
||||
unsigned int ifa_flags;
|
||||
void *ifa_addr; // (struct sockaddr *)
|
||||
void *ifa_netmask; // (struct sockaddr *)
|
||||
#undef ifa_dstaddr
|
||||
void *ifa_dstaddr; // (struct sockaddr *)
|
||||
void *ifa_data;
|
||||
};
|
||||
|
||||
typedef unsigned __sanitizer_pthread_key_t;
|
||||
|
||||
struct __sanitizer_passwd {
|
||||
char *pw_name;
|
||||
char *pw_passwd;
|
||||
int pw_uid;
|
||||
int pw_gid;
|
||||
__sanitizer_time_t pw_change;
|
||||
char *pw_class;
|
||||
char *pw_gecos;
|
||||
char *pw_dir;
|
||||
char *pw_shell;
|
||||
__sanitizer_time_t pw_expire;
|
||||
int pw_fields;
|
||||
};
|
||||
|
||||
struct __sanitizer_group {
|
||||
char *gr_name;
|
||||
char *gr_passwd;
|
||||
int gr_gid;
|
||||
char **gr_mem;
|
||||
};
|
||||
|
||||
typedef long __sanitizer_suseconds_t;
|
||||
|
||||
struct __sanitizer_timeval {
|
||||
__sanitizer_time_t tv_sec;
|
||||
__sanitizer_suseconds_t tv_usec;
|
||||
};
|
||||
|
||||
struct __sanitizer_itimerval {
|
||||
struct __sanitizer_timeval it_interval;
|
||||
struct __sanitizer_timeval it_value;
|
||||
};
|
||||
|
||||
struct __sanitizer_timeb {
|
||||
__sanitizer_time_t time;
|
||||
unsigned short millitm;
|
||||
short timezone;
|
||||
short dstflag;
|
||||
};
|
||||
|
||||
struct __sanitizer_ether_addr {
|
||||
u8 octet[6];
|
||||
};
|
||||
|
||||
struct __sanitizer_tm {
|
||||
int tm_sec;
|
||||
int tm_min;
|
||||
int tm_hour;
|
||||
int tm_mday;
|
||||
int tm_mon;
|
||||
int tm_year;
|
||||
int tm_wday;
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
long int tm_gmtoff;
|
||||
const char *tm_zone;
|
||||
};
|
||||
|
||||
struct __sanitizer_msghdr {
|
||||
void *msg_name;
|
||||
unsigned msg_namelen;
|
||||
struct __sanitizer_iovec *msg_iov;
|
||||
unsigned msg_iovlen;
|
||||
void *msg_control;
|
||||
unsigned msg_controllen;
|
||||
int msg_flags;
|
||||
};
|
||||
|
||||
struct __sanitizer_cmsghdr {
|
||||
unsigned cmsg_len;
|
||||
int cmsg_level;
|
||||
int cmsg_type;
|
||||
};
|
||||
|
||||
struct __sanitizer_dirent {
|
||||
#if defined(__INO64)
|
||||
unsigned long long d_fileno;
|
||||
unsigned long long d_off;
|
||||
#else
|
||||
unsigned int d_fileno;
|
||||
#endif
|
||||
unsigned short d_reclen;
|
||||
// more fields that we don't care about
|
||||
};
|
||||
|
||||
// 'clock_t' is 32 bits wide on x64 FreeBSD
|
||||
typedef int __sanitizer_clock_t;
|
||||
typedef int __sanitizer_clockid_t;
|
||||
|
||||
#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__) || \
|
||||
defined(__mips__)
|
||||
typedef unsigned __sanitizer___kernel_uid_t;
|
||||
typedef unsigned __sanitizer___kernel_gid_t;
|
||||
#else
|
||||
typedef unsigned short __sanitizer___kernel_uid_t;
|
||||
typedef unsigned short __sanitizer___kernel_gid_t;
|
||||
#endif
|
||||
typedef long long __sanitizer___kernel_off_t;
|
||||
|
||||
#if defined(__powerpc__) || defined(__mips__)
|
||||
typedef unsigned int __sanitizer___kernel_old_uid_t;
|
||||
typedef unsigned int __sanitizer___kernel_old_gid_t;
|
||||
#else
|
||||
typedef unsigned short __sanitizer___kernel_old_uid_t;
|
||||
typedef unsigned short __sanitizer___kernel_old_gid_t;
|
||||
#endif
|
||||
|
||||
typedef long long __sanitizer___kernel_loff_t;
|
||||
typedef struct {
|
||||
unsigned long fds_bits[1024 / (8 * sizeof(long))];
|
||||
} __sanitizer___kernel_fd_set;
|
||||
|
||||
// This thing depends on the platform. We are only interested in the upper
|
||||
// limit. Verified with a compiler assert in .cpp.
|
||||
union __sanitizer_pthread_attr_t {
|
||||
char size[128];
|
||||
void *align;
|
||||
};
|
||||
|
||||
const unsigned old_sigset_t_sz = sizeof(unsigned long);
|
||||
|
||||
struct __sanitizer_sigset_t {
|
||||
// uint32_t * 4
|
||||
unsigned int __bits[4];
|
||||
};
|
||||
|
||||
typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
|
||||
|
||||
struct __sanitizer_siginfo {
|
||||
// The size is determined by looking at sizeof of real siginfo_t on linux.
|
||||
u64 opaque[128 / sizeof(u64)];
|
||||
};
|
||||
|
||||
using __sanitizer_sighandler_ptr = void (*)(int sig);
|
||||
using __sanitizer_sigactionhandler_ptr = void (*)(int sig,
|
||||
__sanitizer_siginfo *siginfo,
|
||||
void *uctx);
|
||||
|
||||
struct __sanitizer_sigaction {
|
||||
union {
|
||||
__sanitizer_sigactionhandler_ptr sigaction;
|
||||
__sanitizer_sighandler_ptr handler;
|
||||
};
|
||||
int sa_flags;
|
||||
__sanitizer_sigset_t sa_mask;
|
||||
};
|
||||
|
||||
struct __sanitizer_sem_t {
|
||||
u32 data[4];
|
||||
};
|
||||
|
||||
extern const uptr sig_ign;
|
||||
extern const uptr sig_dfl;
|
||||
extern const uptr sig_err;
|
||||
extern const uptr sa_siginfo;
|
||||
|
||||
extern int af_inet;
|
||||
extern int af_inet6;
|
||||
uptr __sanitizer_in_addr_sz(int af);
|
||||
|
||||
struct __sanitizer_dl_phdr_info {
|
||||
uptr dlpi_addr;
|
||||
const char *dlpi_name;
|
||||
const void *dlpi_phdr;
|
||||
short dlpi_phnum;
|
||||
};
|
||||
|
||||
extern unsigned struct_ElfW_Phdr_sz;
|
||||
|
||||
struct __sanitizer_addrinfo {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
unsigned ai_addrlen;
|
||||
char *ai_canonname;
|
||||
void *ai_addr;
|
||||
struct __sanitizer_addrinfo *ai_next;
|
||||
};
|
||||
|
||||
struct __sanitizer_hostent {
|
||||
char *h_name;
|
||||
char **h_aliases;
|
||||
int h_addrtype;
|
||||
int h_length;
|
||||
char **h_addr_list;
|
||||
};
|
||||
|
||||
struct __sanitizer_pollfd {
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
|
||||
typedef unsigned __sanitizer_nfds_t;
|
||||
|
||||
struct __sanitizer_glob_t {
|
||||
uptr gl_pathc;
|
||||
uptr gl_matchc;
|
||||
uptr gl_offs;
|
||||
int gl_flags;
|
||||
char **gl_pathv;
|
||||
int (*gl_errfunc)(const char *, int);
|
||||
void (*gl_closedir)(void *dirp);
|
||||
struct dirent *(*gl_readdir)(void *dirp);
|
||||
void *(*gl_opendir)(const char *);
|
||||
int (*gl_lstat)(const char *, void * /* struct stat* */);
|
||||
int (*gl_stat)(const char *, void * /* struct stat* */);
|
||||
};
|
||||
|
||||
extern int glob_nomatch;
|
||||
extern int glob_altdirfunc;
|
||||
|
||||
extern unsigned path_max;
|
||||
|
||||
struct __sanitizer_wordexp_t {
|
||||
uptr we_wordc;
|
||||
char **we_wordv;
|
||||
uptr we_offs;
|
||||
char *we_strings;
|
||||
uptr we_nbytes;
|
||||
};
|
||||
|
||||
typedef void __sanitizer_FILE;
|
||||
|
||||
extern unsigned struct_shminfo_sz;
|
||||
extern unsigned struct_shm_info_sz;
|
||||
extern int shmctl_ipc_stat;
|
||||
extern int shmctl_ipc_info;
|
||||
extern int shmctl_shm_info;
|
||||
extern int shmctl_shm_stat;
|
||||
|
||||
extern unsigned struct_utmpx_sz;
|
||||
|
||||
extern int map_fixed;
|
||||
|
||||
// ioctl arguments
|
||||
struct __sanitizer_ifconf {
|
||||
int ifc_len;
|
||||
union {
|
||||
void *ifcu_req;
|
||||
} ifc_ifcu;
|
||||
};
|
||||
|
||||
#define IOC_NRBITS 8
|
||||
#define IOC_TYPEBITS 8
|
||||
#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__)
|
||||
#define IOC_SIZEBITS 13
|
||||
#define IOC_DIRBITS 3
|
||||
#define IOC_NONE 1U
|
||||
#define IOC_WRITE 4U
|
||||
#define IOC_READ 2U
|
||||
#else
|
||||
#define IOC_SIZEBITS 14
|
||||
#define IOC_DIRBITS 2
|
||||
#define IOC_NONE 0U
|
||||
#define IOC_WRITE 1U
|
||||
#define IOC_READ 2U
|
||||
#endif
|
||||
#define IOC_NRMASK ((1 << IOC_NRBITS) - 1)
|
||||
#define IOC_TYPEMASK ((1 << IOC_TYPEBITS) - 1)
|
||||
#define IOC_SIZEMASK ((1 << IOC_SIZEBITS) - 1)
|
||||
#if defined(IOC_DIRMASK)
|
||||
#undef IOC_DIRMASK
|
||||
#endif
|
||||
#define IOC_DIRMASK ((1 << IOC_DIRBITS) - 1)
|
||||
#define IOC_NRSHIFT 0
|
||||
#define IOC_TYPESHIFT (IOC_NRSHIFT + IOC_NRBITS)
|
||||
#define IOC_SIZESHIFT (IOC_TYPESHIFT + IOC_TYPEBITS)
|
||||
#define IOC_DIRSHIFT (IOC_SIZESHIFT + IOC_SIZEBITS)
|
||||
#define EVIOC_EV_MAX 0x1f
|
||||
#define EVIOC_ABS_MAX 0x3f
|
||||
|
||||
#define IOC_DIR(nr) (((nr) >> IOC_DIRSHIFT) & IOC_DIRMASK)
|
||||
#define IOC_TYPE(nr) (((nr) >> IOC_TYPESHIFT) & IOC_TYPEMASK)
|
||||
#define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK)
|
||||
#define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK)
|
||||
|
||||
extern unsigned struct_ifreq_sz;
|
||||
extern unsigned struct_termios_sz;
|
||||
extern unsigned struct_winsize_sz;
|
||||
|
||||
extern unsigned struct_copr_buffer_sz;
|
||||
extern unsigned struct_copr_debug_buf_sz;
|
||||
extern unsigned struct_copr_msg_sz;
|
||||
extern unsigned struct_midi_info_sz;
|
||||
extern unsigned struct_mtget_sz;
|
||||
extern unsigned struct_mtop_sz;
|
||||
extern unsigned struct_rtentry_sz;
|
||||
extern unsigned struct_sbi_instrument_sz;
|
||||
extern unsigned struct_seq_event_rec_sz;
|
||||
extern unsigned struct_synth_info_sz;
|
||||
extern unsigned struct_vt_mode_sz;
|
||||
|
||||
extern const unsigned long __sanitizer_bufsiz;
|
||||
extern unsigned struct_audio_buf_info_sz;
|
||||
extern unsigned struct_ppp_stats_sz;
|
||||
extern unsigned struct_sioc_sg_req_sz;
|
||||
extern unsigned struct_sioc_vif_req_sz;
|
||||
|
||||
// ioctl request identifiers
|
||||
|
||||
// A special value to mark ioctls that are not present on the target platform,
|
||||
// when it can not be determined without including any system headers.
|
||||
extern const unsigned IOCTL_NOT_PRESENT;
|
||||
|
||||
extern unsigned IOCTL_FIOASYNC;
|
||||
extern unsigned IOCTL_FIOCLEX;
|
||||
extern unsigned IOCTL_FIOGETOWN;
|
||||
extern unsigned IOCTL_FIONBIO;
|
||||
extern unsigned IOCTL_FIONCLEX;
|
||||
extern unsigned IOCTL_FIOSETOWN;
|
||||
extern unsigned IOCTL_SIOCADDMULTI;
|
||||
extern unsigned IOCTL_SIOCATMARK;
|
||||
extern unsigned IOCTL_SIOCDELMULTI;
|
||||
extern unsigned IOCTL_SIOCGIFADDR;
|
||||
extern unsigned IOCTL_SIOCGIFBRDADDR;
|
||||
extern unsigned IOCTL_SIOCGIFCONF;
|
||||
extern unsigned IOCTL_SIOCGIFDSTADDR;
|
||||
extern unsigned IOCTL_SIOCGIFFLAGS;
|
||||
extern unsigned IOCTL_SIOCGIFMETRIC;
|
||||
extern unsigned IOCTL_SIOCGIFMTU;
|
||||
extern unsigned IOCTL_SIOCGIFNETMASK;
|
||||
extern unsigned IOCTL_SIOCGPGRP;
|
||||
extern unsigned IOCTL_SIOCSIFADDR;
|
||||
extern unsigned IOCTL_SIOCSIFBRDADDR;
|
||||
extern unsigned IOCTL_SIOCSIFDSTADDR;
|
||||
extern unsigned IOCTL_SIOCSIFFLAGS;
|
||||
extern unsigned IOCTL_SIOCSIFMETRIC;
|
||||
extern unsigned IOCTL_SIOCSIFMTU;
|
||||
extern unsigned IOCTL_SIOCSIFNETMASK;
|
||||
extern unsigned IOCTL_SIOCSPGRP;
|
||||
extern unsigned IOCTL_TIOCCONS;
|
||||
extern unsigned IOCTL_TIOCEXCL;
|
||||
extern unsigned IOCTL_TIOCGETD;
|
||||
extern unsigned IOCTL_TIOCGPGRP;
|
||||
extern unsigned IOCTL_TIOCGWINSZ;
|
||||
extern unsigned IOCTL_TIOCMBIC;
|
||||
extern unsigned IOCTL_TIOCMBIS;
|
||||
extern unsigned IOCTL_TIOCMGET;
|
||||
extern unsigned IOCTL_TIOCMSET;
|
||||
extern unsigned IOCTL_TIOCNOTTY;
|
||||
extern unsigned IOCTL_TIOCNXCL;
|
||||
extern unsigned IOCTL_TIOCOUTQ;
|
||||
extern unsigned IOCTL_TIOCPKT;
|
||||
extern unsigned IOCTL_TIOCSCTTY;
|
||||
extern unsigned IOCTL_TIOCSETD;
|
||||
extern unsigned IOCTL_TIOCSPGRP;
|
||||
extern unsigned IOCTL_TIOCSTI;
|
||||
extern unsigned IOCTL_TIOCSWINSZ;
|
||||
extern unsigned IOCTL_SIOCGETSGCNT;
|
||||
extern unsigned IOCTL_SIOCGETVIFCNT;
|
||||
extern unsigned IOCTL_MTIOCGET;
|
||||
extern unsigned IOCTL_MTIOCTOP;
|
||||
extern unsigned IOCTL_SIOCADDRT;
|
||||
extern unsigned IOCTL_SIOCDELRT;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_GETFMTS;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_NONBLOCK;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_POST;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_RESET;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_SETFMT;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_SPEED;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_STEREO;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE;
|
||||
extern unsigned IOCTL_SNDCTL_DSP_SYNC;
|
||||
extern unsigned IOCTL_SNDCTL_FM_4OP_ENABLE;
|
||||
extern unsigned IOCTL_SNDCTL_FM_LOAD_INSTR;
|
||||
extern unsigned IOCTL_SNDCTL_MIDI_INFO;
|
||||
extern unsigned IOCTL_SNDCTL_MIDI_PRETIME;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_CTRLRATE;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_NRMIDIS;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_PANIC;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_PERCMODE;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_RESET;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_SYNC;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_TESTMIDI;
|
||||
extern unsigned IOCTL_SNDCTL_SEQ_THRESHOLD;
|
||||
extern unsigned IOCTL_SNDCTL_SYNTH_INFO;
|
||||
extern unsigned IOCTL_SNDCTL_SYNTH_MEMAVL;
|
||||
extern unsigned IOCTL_SNDCTL_TMR_CONTINUE;
|
||||
extern unsigned IOCTL_SNDCTL_TMR_METRONOME;
|
||||
extern unsigned IOCTL_SNDCTL_TMR_SELECT;
|
||||
extern unsigned IOCTL_SNDCTL_TMR_SOURCE;
|
||||
extern unsigned IOCTL_SNDCTL_TMR_START;
|
||||
extern unsigned IOCTL_SNDCTL_TMR_STOP;
|
||||
extern unsigned IOCTL_SNDCTL_TMR_TEMPO;
|
||||
extern unsigned IOCTL_SNDCTL_TMR_TIMEBASE;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_ALTPCM;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_BASS;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_CAPS;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_CD;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_DEVMASK;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_ENHANCE;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_IGAIN;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_IMIX;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_LINE1;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_LINE2;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_LINE3;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_LINE;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_LOUD;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_MIC;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_MUTE;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_OGAIN;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_PCM;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_RECLEV;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_RECMASK;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_RECSRC;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_SPEAKER;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_SYNTH;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_TREBLE;
|
||||
extern unsigned IOCTL_SOUND_MIXER_READ_VOLUME;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_BASS;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_CD;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_IMIX;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE1;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE2;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE3;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_LOUD;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_MIC;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_MUTE;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_PCM;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE;
|
||||
extern unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME;
|
||||
extern unsigned IOCTL_SOUND_PCM_READ_BITS;
|
||||
extern unsigned IOCTL_SOUND_PCM_READ_CHANNELS;
|
||||
extern unsigned IOCTL_SOUND_PCM_READ_FILTER;
|
||||
extern unsigned IOCTL_SOUND_PCM_READ_RATE;
|
||||
extern unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS;
|
||||
extern unsigned IOCTL_SOUND_PCM_WRITE_FILTER;
|
||||
extern unsigned IOCTL_VT_ACTIVATE;
|
||||
extern unsigned IOCTL_VT_GETMODE;
|
||||
extern unsigned IOCTL_VT_OPENQRY;
|
||||
extern unsigned IOCTL_VT_RELDISP;
|
||||
extern unsigned IOCTL_VT_SETMODE;
|
||||
extern unsigned IOCTL_VT_WAITACTIVE;
|
||||
extern unsigned IOCTL_GIO_SCRNMAP;
|
||||
extern unsigned IOCTL_KDDISABIO;
|
||||
extern unsigned IOCTL_KDENABIO;
|
||||
extern unsigned IOCTL_KDGETLED;
|
||||
extern unsigned IOCTL_KDGETMODE;
|
||||
extern unsigned IOCTL_KDGKBMODE;
|
||||
extern unsigned IOCTL_KDGKBTYPE;
|
||||
extern unsigned IOCTL_KDMKTONE;
|
||||
extern unsigned IOCTL_KDSETLED;
|
||||
extern unsigned IOCTL_KDSETMODE;
|
||||
extern unsigned IOCTL_KDSKBMODE;
|
||||
|
||||
extern const int si_SEGV_MAPERR;
|
||||
extern const int si_SEGV_ACCERR;
|
||||
|
||||
struct __sanitizer_cap_rights {
|
||||
u64 cr_rights[2];
|
||||
};
|
||||
|
||||
typedef struct __sanitizer_cap_rights __sanitizer_cap_rights_t;
|
||||
extern unsigned struct_cap_rights_sz;
|
||||
|
||||
extern unsigned struct_fstab_sz;
|
||||
extern unsigned struct_StringList_sz;
|
||||
} // namespace __sanitizer
|
||||
|
||||
#define CHECK_TYPE_SIZE(TYPE) \
|
||||
COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE))
|
||||
|
||||
#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \
|
||||
COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *)NULL)->MEMBER) == \
|
||||
sizeof(((CLASS *)NULL)->MEMBER)); \
|
||||
COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \
|
||||
offsetof(CLASS, MEMBER))
|
||||
|
||||
// For sigaction, which is a function and struct at the same time,
|
||||
// and thus requires explicit "struct" in sizeof() expression.
|
||||
#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \
|
||||
COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *)NULL)->MEMBER) == \
|
||||
sizeof(((struct CLASS *)NULL)->MEMBER)); \
|
||||
COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \
|
||||
offsetof(struct CLASS, MEMBER))
|
||||
|
||||
#define SIGACTION_SYMNAME sigaction
|
||||
|
||||
#endif
|
||||
|
||||
#endif // SANITIZER_FREEBSD
|
||||
108
lib/tsan/sanitizer_common/sanitizer_platform_limits_linux.cpp
Normal file
108
lib/tsan/sanitizer_common/sanitizer_platform_limits_linux.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
//===-- sanitizer_platform_limits_linux.cpp -------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer common code.
|
||||
//
|
||||
// Sizes and layouts of linux kernel data structures.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// This is a separate compilation unit for linux headers that conflict with
|
||||
// userspace headers.
|
||||
// Most "normal" includes go in sanitizer_platform_limits_posix.cpp
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_LINUX
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_platform_limits_posix.h"
|
||||
|
||||
// For offsetof -> __builtin_offsetof definition.
|
||||
#include <stddef.h>
|
||||
|
||||
// With old kernels (and even new kernels on powerpc) asm/stat.h uses types that
|
||||
// are not defined anywhere in userspace headers. Fake them. This seems to work
|
||||
// fine with newer headers, too.
|
||||
#include <linux/posix_types.h>
|
||||
#if defined(__x86_64__) || defined(__mips__)
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
#define ino_t __kernel_ino_t
|
||||
#define mode_t __kernel_mode_t
|
||||
#define nlink_t __kernel_nlink_t
|
||||
#define uid_t __kernel_uid_t
|
||||
#define gid_t __kernel_gid_t
|
||||
#define off_t __kernel_off_t
|
||||
#define time_t __kernel_time_t
|
||||
// This header seems to contain the definitions of _kernel_ stat* structs.
|
||||
#include <asm/stat.h>
|
||||
#undef ino_t
|
||||
#undef mode_t
|
||||
#undef nlink_t
|
||||
#undef uid_t
|
||||
#undef gid_t
|
||||
#undef off_t
|
||||
#endif
|
||||
|
||||
#include <linux/aio_abi.h>
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
#include <sys/statfs.h>
|
||||
#include <linux/perf_event.h>
|
||||
#endif
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
namespace __sanitizer {
|
||||
#if !SANITIZER_ANDROID
|
||||
unsigned struct_statfs64_sz = sizeof(struct statfs64);
|
||||
#endif
|
||||
} // namespace __sanitizer
|
||||
|
||||
#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__)\
|
||||
&& !defined(__mips__) && !defined(__s390__)\
|
||||
&& !defined(__sparc__) && !defined(__riscv)
|
||||
COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
|
||||
#endif
|
||||
|
||||
COMPILER_CHECK(struct_kernel_stat_sz == sizeof(struct stat));
|
||||
|
||||
#if defined(__i386__)
|
||||
COMPILER_CHECK(struct_kernel_stat64_sz == sizeof(struct stat64));
|
||||
#endif
|
||||
|
||||
CHECK_TYPE_SIZE(io_event);
|
||||
CHECK_SIZE_AND_OFFSET(io_event, data);
|
||||
CHECK_SIZE_AND_OFFSET(io_event, obj);
|
||||
CHECK_SIZE_AND_OFFSET(io_event, res);
|
||||
CHECK_SIZE_AND_OFFSET(io_event, res2);
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
COMPILER_CHECK(sizeof(struct __sanitizer_perf_event_attr) <=
|
||||
sizeof(struct perf_event_attr));
|
||||
CHECK_SIZE_AND_OFFSET(perf_event_attr, type);
|
||||
CHECK_SIZE_AND_OFFSET(perf_event_attr, size);
|
||||
#endif
|
||||
|
||||
COMPILER_CHECK(iocb_cmd_pread == IOCB_CMD_PREAD);
|
||||
COMPILER_CHECK(iocb_cmd_pwrite == IOCB_CMD_PWRITE);
|
||||
#if !SANITIZER_ANDROID
|
||||
COMPILER_CHECK(iocb_cmd_preadv == IOCB_CMD_PREADV);
|
||||
COMPILER_CHECK(iocb_cmd_pwritev == IOCB_CMD_PWRITEV);
|
||||
#endif
|
||||
|
||||
CHECK_TYPE_SIZE(iocb);
|
||||
CHECK_SIZE_AND_OFFSET(iocb, aio_data);
|
||||
// Skip aio_key, it's weird.
|
||||
CHECK_SIZE_AND_OFFSET(iocb, aio_lio_opcode);
|
||||
CHECK_SIZE_AND_OFFSET(iocb, aio_reqprio);
|
||||
CHECK_SIZE_AND_OFFSET(iocb, aio_fildes);
|
||||
CHECK_SIZE_AND_OFFSET(iocb, aio_buf);
|
||||
CHECK_SIZE_AND_OFFSET(iocb, aio_nbytes);
|
||||
CHECK_SIZE_AND_OFFSET(iocb, aio_offset);
|
||||
|
||||
#endif // SANITIZER_LINUX
|
||||
2586
lib/tsan/sanitizer_common/sanitizer_platform_limits_netbsd.cpp
Normal file
2586
lib/tsan/sanitizer_common/sanitizer_platform_limits_netbsd.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2422
lib/tsan/sanitizer_common/sanitizer_platform_limits_netbsd.h
Normal file
2422
lib/tsan/sanitizer_common/sanitizer_platform_limits_netbsd.h
Normal file
File diff suppressed because it is too large
Load Diff
279
lib/tsan/sanitizer_common/sanitizer_platform_limits_openbsd.cpp
Normal file
279
lib/tsan/sanitizer_common/sanitizer_platform_limits_openbsd.cpp
Normal file
@@ -0,0 +1,279 @@
|
||||
//===-- sanitizer_platform_limits_openbsd.cpp -----------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer common code.
|
||||
//
|
||||
// Sizes and layouts of platform-specific NetBSD data structures.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_OPENBSD
|
||||
#include <arpa/inet.h>
|
||||
#include <dirent.h>
|
||||
#include <glob.h>
|
||||
#include <grp.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <limits.h>
|
||||
#include <link_elf.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <net/ppp_defs.h>
|
||||
#include <net/route.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_mroute.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <soundcard.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/filio.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/msg.h>
|
||||
#include <sys/mtio.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/times.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <term.h>
|
||||
#include <time.h>
|
||||
#include <utime.h>
|
||||
#include <utmp.h>
|
||||
#include <wchar.h>
|
||||
|
||||
// Include these after system headers to avoid name clashes and ambiguities.
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_platform_limits_openbsd.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
unsigned struct_utsname_sz = sizeof(struct utsname);
|
||||
unsigned struct_stat_sz = sizeof(struct stat);
|
||||
unsigned struct_rusage_sz = sizeof(struct rusage);
|
||||
unsigned struct_tm_sz = sizeof(struct tm);
|
||||
unsigned struct_passwd_sz = sizeof(struct passwd);
|
||||
unsigned struct_group_sz = sizeof(struct group);
|
||||
unsigned siginfo_t_sz = sizeof(siginfo_t);
|
||||
unsigned struct_sigaction_sz = sizeof(struct sigaction);
|
||||
unsigned struct_stack_t_sz = sizeof(stack_t);
|
||||
unsigned struct_itimerval_sz = sizeof(struct itimerval);
|
||||
unsigned pthread_t_sz = sizeof(pthread_t);
|
||||
unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
|
||||
unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
|
||||
unsigned pid_t_sz = sizeof(pid_t);
|
||||
unsigned timeval_sz = sizeof(timeval);
|
||||
unsigned uid_t_sz = sizeof(uid_t);
|
||||
unsigned gid_t_sz = sizeof(gid_t);
|
||||
unsigned mbstate_t_sz = sizeof(mbstate_t);
|
||||
unsigned sigset_t_sz = sizeof(sigset_t);
|
||||
unsigned struct_timezone_sz = sizeof(struct timezone);
|
||||
unsigned struct_tms_sz = sizeof(struct tms);
|
||||
unsigned struct_sched_param_sz = sizeof(struct sched_param);
|
||||
unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
|
||||
unsigned struct_rlimit_sz = sizeof(struct rlimit);
|
||||
unsigned struct_timespec_sz = sizeof(struct timespec);
|
||||
unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
|
||||
unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
|
||||
unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
|
||||
unsigned struct_statvfs_sz = sizeof(struct statvfs);
|
||||
|
||||
const uptr sig_ign = (uptr)SIG_IGN;
|
||||
const uptr sig_dfl = (uptr)SIG_DFL;
|
||||
const uptr sig_err = (uptr)SIG_ERR;
|
||||
const uptr sa_siginfo = (uptr)SA_SIGINFO;
|
||||
|
||||
int shmctl_ipc_stat = (int)IPC_STAT;
|
||||
|
||||
unsigned struct_utmp_sz = sizeof(struct utmp);
|
||||
|
||||
int map_fixed = MAP_FIXED;
|
||||
|
||||
int af_inet = (int)AF_INET;
|
||||
int af_inet6 = (int)AF_INET6;
|
||||
|
||||
uptr __sanitizer_in_addr_sz(int af) {
|
||||
if (af == AF_INET)
|
||||
return sizeof(struct in_addr);
|
||||
else if (af == AF_INET6)
|
||||
return sizeof(struct in6_addr);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
|
||||
|
||||
int glob_nomatch = GLOB_NOMATCH;
|
||||
int glob_altdirfunc = GLOB_ALTDIRFUNC;
|
||||
|
||||
unsigned path_max = PATH_MAX;
|
||||
|
||||
const int si_SEGV_MAPERR = SEGV_MAPERR;
|
||||
const int si_SEGV_ACCERR = SEGV_ACCERR;
|
||||
} // namespace __sanitizer
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
|
||||
|
||||
COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
|
||||
CHECK_TYPE_SIZE(pthread_key_t);
|
||||
|
||||
CHECK_TYPE_SIZE(dl_phdr_info);
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
|
||||
|
||||
CHECK_TYPE_SIZE(glob_t);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
|
||||
|
||||
CHECK_TYPE_SIZE(addrinfo);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_next);
|
||||
|
||||
CHECK_TYPE_SIZE(hostent);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_name);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_length);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
|
||||
|
||||
CHECK_TYPE_SIZE(iovec);
|
||||
CHECK_SIZE_AND_OFFSET(iovec, iov_base);
|
||||
CHECK_SIZE_AND_OFFSET(iovec, iov_len);
|
||||
|
||||
CHECK_TYPE_SIZE(msghdr);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
|
||||
|
||||
CHECK_TYPE_SIZE(cmsghdr);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_fileno);
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_off);
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
|
||||
|
||||
CHECK_TYPE_SIZE(ifconf);
|
||||
CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
|
||||
CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
|
||||
|
||||
CHECK_TYPE_SIZE(pollfd);
|
||||
CHECK_SIZE_AND_OFFSET(pollfd, fd);
|
||||
CHECK_SIZE_AND_OFFSET(pollfd, events);
|
||||
CHECK_SIZE_AND_OFFSET(pollfd, revents);
|
||||
|
||||
CHECK_TYPE_SIZE(nfds_t);
|
||||
|
||||
CHECK_TYPE_SIZE(sigset_t);
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
|
||||
// Can't write checks for sa_handler and sa_sigaction due to them being
|
||||
// preprocessor macros.
|
||||
CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
|
||||
|
||||
CHECK_TYPE_SIZE(tm);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_sec);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_min);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_hour);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_mday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_mon);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_year);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_wday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_yday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_zone);
|
||||
|
||||
CHECK_TYPE_SIZE(ipc_perm);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, key);
|
||||
|
||||
CHECK_TYPE_SIZE(shmid_ds);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, __shm_atimensec);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, __shm_dtimensec);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, __shm_ctimensec);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
|
||||
|
||||
CHECK_TYPE_SIZE(clock_t);
|
||||
|
||||
CHECK_TYPE_SIZE(ifaddrs);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
|
||||
// Compare against the union, because we can't reach into the union in a
|
||||
// compliant way.
|
||||
#ifdef ifa_dstaddr
|
||||
#undef ifa_dstaddr
|
||||
#endif
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
|
||||
|
||||
CHECK_TYPE_SIZE(passwd);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_name);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
|
||||
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
|
||||
|
||||
CHECK_TYPE_SIZE(group);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_name);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_passwd);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_gid);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_mem);
|
||||
|
||||
#endif // SANITIZER_OPENBSD
|
||||
382
lib/tsan/sanitizer_common/sanitizer_platform_limits_openbsd.h
Normal file
382
lib/tsan/sanitizer_common/sanitizer_platform_limits_openbsd.h
Normal file
@@ -0,0 +1,382 @@
|
||||
//===-- sanitizer_platform_limits_openbsd.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer common code.
|
||||
//
|
||||
// Sizes and layouts of platform-specific OpenBSD data structures.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_PLATFORM_LIMITS_OPENBSD_H
|
||||
#define SANITIZER_PLATFORM_LIMITS_OPENBSD_H
|
||||
|
||||
#if SANITIZER_OPENBSD
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#define _GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, shift) \
|
||||
((link_map *)((handle) == nullptr ? nullptr : ((char *)(handle) + (shift))))
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
|
||||
_GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, 312)
|
||||
#elif defined(__i386__)
|
||||
#define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
|
||||
_GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, 164)
|
||||
#endif
|
||||
|
||||
#define RLIMIT_AS RLIMIT_DATA
|
||||
|
||||
namespace __sanitizer {
|
||||
extern unsigned struct_utsname_sz;
|
||||
extern unsigned struct_stat_sz;
|
||||
extern unsigned struct_rusage_sz;
|
||||
extern unsigned siginfo_t_sz;
|
||||
extern unsigned struct_itimerval_sz;
|
||||
extern unsigned pthread_t_sz;
|
||||
extern unsigned pthread_mutex_t_sz;
|
||||
extern unsigned pthread_cond_t_sz;
|
||||
extern unsigned pid_t_sz;
|
||||
extern unsigned timeval_sz;
|
||||
extern unsigned uid_t_sz;
|
||||
extern unsigned gid_t_sz;
|
||||
extern unsigned mbstate_t_sz;
|
||||
extern unsigned struct_timezone_sz;
|
||||
extern unsigned struct_tms_sz;
|
||||
extern unsigned struct_itimerspec_sz;
|
||||
extern unsigned struct_sigevent_sz;
|
||||
extern unsigned struct_stack_t_sz;
|
||||
extern unsigned struct_statfs_sz;
|
||||
extern unsigned struct_sockaddr_sz;
|
||||
|
||||
extern unsigned struct_rlimit_sz;
|
||||
extern unsigned struct_utimbuf_sz;
|
||||
extern unsigned struct_timespec_sz;
|
||||
|
||||
struct __sanitizer_iocb {
|
||||
u64 aio_offset;
|
||||
uptr aio_buf;
|
||||
long aio_nbytes;
|
||||
u32 aio_fildes;
|
||||
u32 aio_lio_opcode;
|
||||
long aio_reqprio;
|
||||
#if SANITIZER_WORDSIZE == 64
|
||||
u8 aio_sigevent[32];
|
||||
#else
|
||||
u8 aio_sigevent[20];
|
||||
#endif
|
||||
u32 _state;
|
||||
u32 _errno;
|
||||
long _retval;
|
||||
};
|
||||
|
||||
struct __sanitizer___sysctl_args {
|
||||
int *name;
|
||||
int nlen;
|
||||
void *oldval;
|
||||
uptr *oldlenp;
|
||||
void *newval;
|
||||
uptr newlen;
|
||||
};
|
||||
|
||||
struct __sanitizer_sem_t {
|
||||
uptr data[5];
|
||||
};
|
||||
|
||||
struct __sanitizer_ipc_perm {
|
||||
u32 cuid;
|
||||
u32 cgid;
|
||||
u32 uid;
|
||||
u32 gid;
|
||||
u32 mode;
|
||||
unsigned short seq;
|
||||
long key;
|
||||
};
|
||||
|
||||
struct __sanitizer_shmid_ds {
|
||||
__sanitizer_ipc_perm shm_perm;
|
||||
int shm_segsz;
|
||||
u32 shm_lpid;
|
||||
u32 shm_cpid;
|
||||
short shm_nattch;
|
||||
u64 shm_atime;
|
||||
long __shm_atimensec;
|
||||
u64 shm_dtime;
|
||||
long __shm_dtimensec;
|
||||
u64 shm_ctime;
|
||||
long __shm_ctimensec;
|
||||
void *_shm_internal;
|
||||
};
|
||||
|
||||
extern unsigned struct_msqid_ds_sz;
|
||||
extern unsigned struct_mq_attr_sz;
|
||||
extern unsigned struct_timex_sz;
|
||||
extern unsigned struct_statvfs_sz;
|
||||
|
||||
struct __sanitizer_iovec {
|
||||
void *iov_base;
|
||||
uptr iov_len;
|
||||
};
|
||||
|
||||
struct __sanitizer_ifaddrs {
|
||||
struct __sanitizer_ifaddrs *ifa_next;
|
||||
char *ifa_name;
|
||||
unsigned int ifa_flags;
|
||||
struct __sanitizer_sockaddr *ifa_addr; // (struct sockaddr *)
|
||||
struct __sanitizer_sockaddr *ifa_netmask; // (struct sockaddr *)
|
||||
struct __sanitizer_sockaddr *ifa_dstaddr; // (struct sockaddr *)
|
||||
void *ifa_data;
|
||||
};
|
||||
|
||||
typedef unsigned __sanitizer_pthread_key_t;
|
||||
|
||||
typedef long long __sanitizer_time_t;
|
||||
typedef int __sanitizer_suseconds_t;
|
||||
|
||||
struct __sanitizer_timeval {
|
||||
__sanitizer_time_t tv_sec;
|
||||
__sanitizer_suseconds_t tv_usec;
|
||||
};
|
||||
|
||||
struct __sanitizer_itimerval {
|
||||
struct __sanitizer_timeval it_interval;
|
||||
struct __sanitizer_timeval it_value;
|
||||
};
|
||||
|
||||
struct __sanitizer_passwd {
|
||||
char *pw_name;
|
||||
char *pw_passwd;
|
||||
int pw_uid;
|
||||
int pw_gid;
|
||||
__sanitizer_time_t pw_change;
|
||||
char *pw_class;
|
||||
char *pw_gecos;
|
||||
char *pw_dir;
|
||||
char *pw_shell;
|
||||
__sanitizer_time_t pw_expire;
|
||||
};
|
||||
|
||||
struct __sanitizer_group {
|
||||
char *gr_name;
|
||||
char *gr_passwd;
|
||||
int gr_gid;
|
||||
char **gr_mem;
|
||||
};
|
||||
|
||||
struct __sanitizer_ether_addr {
|
||||
u8 octet[6];
|
||||
};
|
||||
|
||||
struct __sanitizer_tm {
|
||||
int tm_sec;
|
||||
int tm_min;
|
||||
int tm_hour;
|
||||
int tm_mday;
|
||||
int tm_mon;
|
||||
int tm_year;
|
||||
int tm_wday;
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
long int tm_gmtoff;
|
||||
const char *tm_zone;
|
||||
};
|
||||
|
||||
struct __sanitizer_msghdr {
|
||||
void *msg_name;
|
||||
unsigned msg_namelen;
|
||||
struct __sanitizer_iovec *msg_iov;
|
||||
unsigned msg_iovlen;
|
||||
void *msg_control;
|
||||
unsigned msg_controllen;
|
||||
int msg_flags;
|
||||
};
|
||||
struct __sanitizer_cmsghdr {
|
||||
unsigned cmsg_len;
|
||||
int cmsg_level;
|
||||
int cmsg_type;
|
||||
};
|
||||
|
||||
struct __sanitizer_dirent {
|
||||
u64 d_fileno;
|
||||
u64 d_off;
|
||||
u16 d_reclen;
|
||||
};
|
||||
|
||||
typedef u64 __sanitizer_clock_t;
|
||||
typedef u32 __sanitizer_clockid_t;
|
||||
|
||||
typedef u32 __sanitizer___kernel_uid_t;
|
||||
typedef u32 __sanitizer___kernel_gid_t;
|
||||
typedef u64 __sanitizer___kernel_off_t;
|
||||
typedef struct {
|
||||
u32 fds_bits[8];
|
||||
} __sanitizer___kernel_fd_set;
|
||||
|
||||
typedef struct {
|
||||
unsigned int pta_magic;
|
||||
int pta_flags;
|
||||
void *pta_private;
|
||||
} __sanitizer_pthread_attr_t;
|
||||
|
||||
typedef unsigned int __sanitizer_sigset_t;
|
||||
|
||||
struct __sanitizer_siginfo {
|
||||
// The size is determined by looking at sizeof of real siginfo_t on linux.
|
||||
u64 opaque[128 / sizeof(u64)];
|
||||
};
|
||||
|
||||
using __sanitizer_sighandler_ptr = void (*)(int sig);
|
||||
using __sanitizer_sigactionhandler_ptr = void (*)(int sig,
|
||||
__sanitizer_siginfo *siginfo,
|
||||
void *uctx);
|
||||
|
||||
struct __sanitizer_sigaction {
|
||||
union {
|
||||
__sanitizer_sighandler_ptr handler;
|
||||
__sanitizer_sigactionhandler_ptr sigaction;
|
||||
};
|
||||
__sanitizer_sigset_t sa_mask;
|
||||
int sa_flags;
|
||||
};
|
||||
|
||||
typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
|
||||
|
||||
struct __sanitizer_kernel_sigaction_t {
|
||||
union {
|
||||
void (*handler)(int signo);
|
||||
void (*sigaction)(int signo, void *info, void *ctx);
|
||||
};
|
||||
unsigned long sa_flags;
|
||||
void (*sa_restorer)(void);
|
||||
__sanitizer_kernel_sigset_t sa_mask;
|
||||
};
|
||||
|
||||
extern const uptr sig_ign;
|
||||
extern const uptr sig_dfl;
|
||||
extern const uptr sig_err;
|
||||
extern const uptr sa_siginfo;
|
||||
|
||||
extern int af_inet;
|
||||
extern int af_inet6;
|
||||
uptr __sanitizer_in_addr_sz(int af);
|
||||
|
||||
struct __sanitizer_dl_phdr_info {
|
||||
#if SANITIZER_WORDSIZE == 64
|
||||
u64 dlpi_addr;
|
||||
#else
|
||||
u32 dlpi_addr;
|
||||
#endif
|
||||
const char *dlpi_name;
|
||||
const void *dlpi_phdr;
|
||||
#if SANITIZER_WORDSIZE == 64
|
||||
u32 dlpi_phnum;
|
||||
#else
|
||||
u16 dlpi_phnum;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern unsigned struct_ElfW_Phdr_sz;
|
||||
|
||||
struct __sanitizer_addrinfo {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
unsigned ai_addrlen;
|
||||
struct __sanitizer_sockaddr *ai_addr;
|
||||
char *ai_canonname;
|
||||
struct __sanitizer_addrinfo *ai_next;
|
||||
};
|
||||
|
||||
struct __sanitizer_hostent {
|
||||
char *h_name;
|
||||
char **h_aliases;
|
||||
int h_addrtype;
|
||||
int h_length;
|
||||
char **h_addr_list;
|
||||
};
|
||||
|
||||
struct __sanitizer_pollfd {
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
|
||||
typedef unsigned __sanitizer_nfds_t;
|
||||
|
||||
struct __sanitizer_glob_t {
|
||||
int gl_pathc;
|
||||
int gl_matchc;
|
||||
int gl_offs;
|
||||
int gl_flags;
|
||||
char **gl_pathv;
|
||||
void **gl_statv;
|
||||
int (*gl_errfunc)(const char *, int);
|
||||
void (*gl_closedir)(void *dirp);
|
||||
struct dirent *(*gl_readdir)(void *dirp);
|
||||
void *(*gl_opendir)(const char *);
|
||||
int (*gl_lstat)(const char *, void * /* struct stat* */);
|
||||
int (*gl_stat)(const char *, void * /* struct stat* */);
|
||||
};
|
||||
|
||||
extern int glob_nomatch;
|
||||
extern int glob_altdirfunc;
|
||||
|
||||
extern unsigned path_max;
|
||||
|
||||
typedef char __sanitizer_FILE;
|
||||
#define SANITIZER_HAS_STRUCT_FILE 0
|
||||
|
||||
extern int shmctl_ipc_stat;
|
||||
|
||||
// This simplifies generic code
|
||||
#define struct_shminfo_sz -1
|
||||
#define struct_shm_info_sz -1
|
||||
#define shmctl_shm_stat -1
|
||||
#define shmctl_ipc_info -1
|
||||
#define shmctl_shm_info -1
|
||||
|
||||
extern unsigned struct_utmp_sz;
|
||||
extern unsigned struct_utmpx_sz;
|
||||
|
||||
extern int map_fixed;
|
||||
|
||||
// ioctl arguments
|
||||
struct __sanitizer_ifconf {
|
||||
int ifc_len;
|
||||
union {
|
||||
void *ifcu_req;
|
||||
} ifc_ifcu;
|
||||
};
|
||||
|
||||
extern const int si_SEGV_MAPERR;
|
||||
extern const int si_SEGV_ACCERR;
|
||||
} // namespace __sanitizer
|
||||
|
||||
#define CHECK_TYPE_SIZE(TYPE) \
|
||||
COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE))
|
||||
|
||||
#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \
|
||||
COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *)NULL)->MEMBER) == \
|
||||
sizeof(((CLASS *)NULL)->MEMBER)); \
|
||||
COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \
|
||||
offsetof(CLASS, MEMBER))
|
||||
|
||||
// For sigaction, which is a function and struct at the same time,
|
||||
// and thus requires explicit "struct" in sizeof() expression.
|
||||
#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \
|
||||
COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *)NULL)->MEMBER) == \
|
||||
sizeof(((struct CLASS *)NULL)->MEMBER)); \
|
||||
COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \
|
||||
offsetof(struct CLASS, MEMBER))
|
||||
|
||||
#define SIGACTION_SYMNAME __sigaction14
|
||||
|
||||
#endif // SANITIZER_OPENBSD
|
||||
|
||||
#endif
|
||||
1275
lib/tsan/sanitizer_common/sanitizer_platform_limits_posix.cpp
Normal file
1275
lib/tsan/sanitizer_common/sanitizer_platform_limits_posix.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1453
lib/tsan/sanitizer_common/sanitizer_platform_limits_posix.h
Normal file
1453
lib/tsan/sanitizer_common/sanitizer_platform_limits_posix.h
Normal file
File diff suppressed because it is too large
Load Diff
366
lib/tsan/sanitizer_common/sanitizer_platform_limits_solaris.cpp
Normal file
366
lib/tsan/sanitizer_common/sanitizer_platform_limits_solaris.cpp
Normal file
@@ -0,0 +1,366 @@
|
||||
//===-- sanitizer_platform_limits_solaris.cpp -----------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer common code.
|
||||
//
|
||||
// Sizes and layouts of platform-specific Solaris data structures.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_SOLARIS
|
||||
#include <arpa/inet.h>
|
||||
#include <dirent.h>
|
||||
#include <glob.h>
|
||||
#include <grp.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <limits.h>
|
||||
#include <link.h>
|
||||
#include <net/if.h>
|
||||
#include <net/route.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/ip_mroute.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <rpc/xdr.h>
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/ethernet.h>
|
||||
#include <sys/filio.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mtio.h>
|
||||
#include <sys/ptyvar.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timeb.h>
|
||||
#include <sys/times.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <utmp.h>
|
||||
#include <utmpx.h>
|
||||
#include <wchar.h>
|
||||
#include <wordexp.h>
|
||||
|
||||
// Include these after system headers to avoid name clashes and ambiguities.
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_platform_limits_solaris.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
unsigned struct_utsname_sz = sizeof(struct utsname);
|
||||
unsigned struct_stat_sz = sizeof(struct stat);
|
||||
unsigned struct_stat64_sz = sizeof(struct stat64);
|
||||
unsigned struct_rusage_sz = sizeof(struct rusage);
|
||||
unsigned struct_tm_sz = sizeof(struct tm);
|
||||
unsigned struct_passwd_sz = sizeof(struct passwd);
|
||||
unsigned struct_group_sz = sizeof(struct group);
|
||||
unsigned siginfo_t_sz = sizeof(siginfo_t);
|
||||
unsigned struct_sigaction_sz = sizeof(struct sigaction);
|
||||
unsigned struct_stack_t_sz = sizeof(stack_t);
|
||||
unsigned struct_itimerval_sz = sizeof(struct itimerval);
|
||||
unsigned pthread_t_sz = sizeof(pthread_t);
|
||||
unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
|
||||
unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
|
||||
unsigned pid_t_sz = sizeof(pid_t);
|
||||
unsigned timeval_sz = sizeof(timeval);
|
||||
unsigned uid_t_sz = sizeof(uid_t);
|
||||
unsigned gid_t_sz = sizeof(gid_t);
|
||||
unsigned mbstate_t_sz = sizeof(mbstate_t);
|
||||
unsigned sigset_t_sz = sizeof(sigset_t);
|
||||
unsigned struct_timezone_sz = sizeof(struct timezone);
|
||||
unsigned struct_tms_sz = sizeof(struct tms);
|
||||
unsigned struct_sigevent_sz = sizeof(struct sigevent);
|
||||
unsigned struct_sched_param_sz = sizeof(struct sched_param);
|
||||
unsigned struct_statfs_sz = sizeof(struct statfs);
|
||||
unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
|
||||
unsigned ucontext_t_sz = sizeof(ucontext_t);
|
||||
unsigned struct_timespec_sz = sizeof(struct timespec);
|
||||
#if SANITIZER_SOLARIS32
|
||||
unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
|
||||
#endif
|
||||
unsigned struct_statvfs_sz = sizeof(struct statvfs);
|
||||
|
||||
const uptr sig_ign = (uptr)SIG_IGN;
|
||||
const uptr sig_dfl = (uptr)SIG_DFL;
|
||||
const uptr sig_err = (uptr)SIG_ERR;
|
||||
const uptr sa_siginfo = (uptr)SA_SIGINFO;
|
||||
|
||||
int shmctl_ipc_stat = (int)IPC_STAT;
|
||||
|
||||
unsigned struct_utmp_sz = sizeof(struct utmp);
|
||||
unsigned struct_utmpx_sz = sizeof(struct utmpx);
|
||||
|
||||
int map_fixed = MAP_FIXED;
|
||||
|
||||
int af_inet = (int)AF_INET;
|
||||
int af_inet6 = (int)AF_INET6;
|
||||
|
||||
uptr __sanitizer_in_addr_sz(int af) {
|
||||
if (af == AF_INET)
|
||||
return sizeof(struct in_addr);
|
||||
else if (af == AF_INET6)
|
||||
return sizeof(struct in6_addr);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr));
|
||||
|
||||
int glob_nomatch = GLOB_NOMATCH;
|
||||
|
||||
unsigned path_max = PATH_MAX;
|
||||
|
||||
// ioctl arguments
|
||||
unsigned struct_ifreq_sz = sizeof(struct ifreq);
|
||||
unsigned struct_termios_sz = sizeof(struct termios);
|
||||
unsigned struct_winsize_sz = sizeof(struct winsize);
|
||||
|
||||
unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
|
||||
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
|
||||
|
||||
const unsigned IOCTL_NOT_PRESENT = 0;
|
||||
|
||||
unsigned IOCTL_FIOASYNC = FIOASYNC;
|
||||
unsigned IOCTL_FIOCLEX = FIOCLEX;
|
||||
unsigned IOCTL_FIOGETOWN = FIOGETOWN;
|
||||
unsigned IOCTL_FIONBIO = FIONBIO;
|
||||
unsigned IOCTL_FIONCLEX = FIONCLEX;
|
||||
unsigned IOCTL_FIOSETOWN = FIOSETOWN;
|
||||
unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
|
||||
unsigned IOCTL_SIOCATMARK = SIOCATMARK;
|
||||
unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
|
||||
unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
|
||||
unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
|
||||
unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
|
||||
unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
|
||||
unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
|
||||
unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
|
||||
unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
|
||||
unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
|
||||
unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
|
||||
unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
|
||||
unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
|
||||
unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
|
||||
unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
|
||||
unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
|
||||
unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
|
||||
unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
|
||||
unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
|
||||
unsigned IOCTL_TIOCEXCL = TIOCEXCL;
|
||||
unsigned IOCTL_TIOCGETD = TIOCGETD;
|
||||
unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
|
||||
unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
|
||||
unsigned IOCTL_TIOCMBIC = TIOCMBIC;
|
||||
unsigned IOCTL_TIOCMBIS = TIOCMBIS;
|
||||
unsigned IOCTL_TIOCMGET = TIOCMGET;
|
||||
unsigned IOCTL_TIOCMSET = TIOCMSET;
|
||||
unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
|
||||
unsigned IOCTL_TIOCNXCL = TIOCNXCL;
|
||||
unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
|
||||
unsigned IOCTL_TIOCPKT = TIOCPKT;
|
||||
unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
|
||||
unsigned IOCTL_TIOCSETD = TIOCSETD;
|
||||
unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
|
||||
unsigned IOCTL_TIOCSTI = TIOCSTI;
|
||||
unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
|
||||
|
||||
unsigned IOCTL_MTIOCGET = MTIOCGET;
|
||||
unsigned IOCTL_MTIOCTOP = MTIOCTOP;
|
||||
|
||||
const int si_SEGV_MAPERR = SEGV_MAPERR;
|
||||
const int si_SEGV_ACCERR = SEGV_ACCERR;
|
||||
} // namespace __sanitizer
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
|
||||
|
||||
COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
|
||||
CHECK_TYPE_SIZE(pthread_key_t);
|
||||
|
||||
// There are more undocumented fields in dl_phdr_info that we are not interested
|
||||
// in.
|
||||
COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
|
||||
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
|
||||
|
||||
CHECK_TYPE_SIZE(glob_t);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
|
||||
CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
|
||||
|
||||
CHECK_TYPE_SIZE(addrinfo);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
|
||||
CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
|
||||
|
||||
CHECK_TYPE_SIZE(hostent);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_name);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_length);
|
||||
CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
|
||||
|
||||
CHECK_TYPE_SIZE(iovec);
|
||||
CHECK_SIZE_AND_OFFSET(iovec, iov_base);
|
||||
CHECK_SIZE_AND_OFFSET(iovec, iov_len);
|
||||
|
||||
CHECK_TYPE_SIZE(msghdr);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
|
||||
CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
|
||||
|
||||
CHECK_TYPE_SIZE(cmsghdr);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
|
||||
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_ino);
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_off);
|
||||
CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
|
||||
|
||||
#if SANITIZER_SOLARIS32
|
||||
COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64));
|
||||
CHECK_SIZE_AND_OFFSET(dirent64, d_ino);
|
||||
CHECK_SIZE_AND_OFFSET(dirent64, d_off);
|
||||
CHECK_SIZE_AND_OFFSET(dirent64, d_reclen);
|
||||
#endif
|
||||
|
||||
CHECK_TYPE_SIZE(ifconf);
|
||||
CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
|
||||
CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
|
||||
|
||||
CHECK_TYPE_SIZE(pollfd);
|
||||
CHECK_SIZE_AND_OFFSET(pollfd, fd);
|
||||
CHECK_SIZE_AND_OFFSET(pollfd, events);
|
||||
CHECK_SIZE_AND_OFFSET(pollfd, revents);
|
||||
|
||||
CHECK_TYPE_SIZE(nfds_t);
|
||||
|
||||
CHECK_TYPE_SIZE(sigset_t);
|
||||
|
||||
COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
|
||||
// Can't write checks for sa_handler and sa_sigaction due to them being
|
||||
// preprocessor macros.
|
||||
CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
|
||||
CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags);
|
||||
|
||||
CHECK_TYPE_SIZE(wordexp_t);
|
||||
CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
|
||||
CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
|
||||
CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
|
||||
|
||||
CHECK_TYPE_SIZE(tm);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_sec);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_min);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_hour);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_mday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_mon);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_year);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_wday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_yday);
|
||||
CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
|
||||
|
||||
CHECK_TYPE_SIZE(ether_addr);
|
||||
|
||||
CHECK_TYPE_SIZE(ipc_perm);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, key);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
|
||||
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);
|
||||
CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
|
||||
|
||||
CHECK_TYPE_SIZE(shmid_ds);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
|
||||
CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
|
||||
|
||||
CHECK_TYPE_SIZE(clock_t);
|
||||
|
||||
CHECK_TYPE_SIZE(ifaddrs);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
|
||||
// Compare against the union, because we can't reach into the union in a
|
||||
// compliant way.
|
||||
#ifdef ifa_dstaddr
|
||||
#undef ifa_dstaddr
|
||||
#endif
|
||||
COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)nullptr)->ifa_dstaddr) ==
|
||||
sizeof(((ifaddrs *)nullptr)->ifa_ifu));
|
||||
COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) ==
|
||||
offsetof(ifaddrs, ifa_ifu));
|
||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
|
||||
|
||||
CHECK_TYPE_SIZE(timeb);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, time);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, millitm);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, timezone);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, dstflag);
|
||||
|
||||
CHECK_TYPE_SIZE(passwd);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_name);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
|
||||
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
|
||||
|
||||
CHECK_TYPE_SIZE(group);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_name);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_passwd);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_gid);
|
||||
CHECK_SIZE_AND_OFFSET(group, gr_mem);
|
||||
|
||||
CHECK_TYPE_SIZE(XDR);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_op);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_ops);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_public);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_private);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_base);
|
||||
CHECK_SIZE_AND_OFFSET(XDR, x_handy);
|
||||
COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE);
|
||||
COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE);
|
||||
COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE);
|
||||
|
||||
CHECK_TYPE_SIZE(sem_t);
|
||||
|
||||
#endif // SANITIZER_SOLARIS
|
||||
495
lib/tsan/sanitizer_common/sanitizer_platform_limits_solaris.h
Normal file
495
lib/tsan/sanitizer_common/sanitizer_platform_limits_solaris.h
Normal file
@@ -0,0 +1,495 @@
|
||||
//===-- sanitizer_platform_limits_solaris.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of Sanitizer common code.
|
||||
//
|
||||
// Sizes and layouts of platform-specific Solaris data structures.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_PLATFORM_LIMITS_SOLARIS_H
|
||||
#define SANITIZER_PLATFORM_LIMITS_SOLARIS_H
|
||||
|
||||
#if SANITIZER_SOLARIS
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
extern unsigned struct_utsname_sz;
|
||||
extern unsigned struct_stat_sz;
|
||||
extern unsigned struct_stat64_sz;
|
||||
extern unsigned struct_rusage_sz;
|
||||
extern unsigned siginfo_t_sz;
|
||||
extern unsigned struct_itimerval_sz;
|
||||
extern unsigned pthread_t_sz;
|
||||
extern unsigned pthread_mutex_t_sz;
|
||||
extern unsigned pthread_cond_t_sz;
|
||||
extern unsigned pid_t_sz;
|
||||
extern unsigned timeval_sz;
|
||||
extern unsigned uid_t_sz;
|
||||
extern unsigned gid_t_sz;
|
||||
extern unsigned mbstate_t_sz;
|
||||
extern unsigned struct_timezone_sz;
|
||||
extern unsigned struct_tms_sz;
|
||||
extern unsigned struct_itimerspec_sz;
|
||||
extern unsigned struct_sigevent_sz;
|
||||
extern unsigned struct_stack_t_sz;
|
||||
extern unsigned struct_sched_param_sz;
|
||||
extern unsigned struct_statfs64_sz;
|
||||
extern unsigned struct_statfs_sz;
|
||||
extern unsigned struct_sockaddr_sz;
|
||||
extern unsigned ucontext_t_sz;
|
||||
|
||||
extern unsigned struct_timespec_sz;
|
||||
extern unsigned struct_rlimit_sz;
|
||||
extern unsigned struct_utimbuf_sz;
|
||||
|
||||
struct __sanitizer_sem_t {
|
||||
//u64 data[6];
|
||||
u32 sem_count;
|
||||
u16 sem_type;
|
||||
u16 sem_magic;
|
||||
u64 sem_pad1[3];
|
||||
u64 sem_pad2[2];
|
||||
};
|
||||
|
||||
struct __sanitizer_ipc_perm {
|
||||
unsigned int uid; // uid_t
|
||||
unsigned int gid; // gid_t
|
||||
unsigned int cuid; // uid_t
|
||||
unsigned int cgid; // gid_t
|
||||
unsigned int mode; // mode_t
|
||||
unsigned int seq; // uint_t
|
||||
int key; // key_t
|
||||
#if !defined(_LP64)
|
||||
int pad[4];
|
||||
#endif
|
||||
};
|
||||
|
||||
struct __sanitizer_shmid_ds {
|
||||
__sanitizer_ipc_perm shm_perm;
|
||||
unsigned long shm_segsz; // size_t
|
||||
unsigned long shm_flags; // uintptr_t
|
||||
unsigned short shm_lkcnt; // ushort_t
|
||||
int shm_lpid; // pid_t
|
||||
int shm_cpid; // pid_t
|
||||
unsigned long shm_nattch; // shmatt_t
|
||||
unsigned long shm_cnattch; // ulong_t
|
||||
#if defined(_LP64)
|
||||
long shm_atime; // time_t
|
||||
long shm_dtime;
|
||||
long shm_ctime;
|
||||
void *shm_amp;
|
||||
u64 shm_gransize; // uint64_t
|
||||
u64 shm_allocated; // uint64_t
|
||||
u64 shm_pad4[1]; // int64_t
|
||||
#else
|
||||
long shm_atime; // time_t
|
||||
int shm_pad1; // int32_t
|
||||
long shm_dtime; // time_t
|
||||
int shm_pad2; // int32_t
|
||||
long shm_ctime; // time_t
|
||||
void *shm_amp;
|
||||
u64 shm_gransize; // uint64_t
|
||||
u64 shm_allocated; // uint64_t
|
||||
#endif
|
||||
};
|
||||
|
||||
extern unsigned struct_statvfs_sz;
|
||||
#if SANITIZER_SOLARIS32
|
||||
extern unsigned struct_statvfs64_sz;
|
||||
#endif
|
||||
|
||||
struct __sanitizer_iovec {
|
||||
void *iov_base;
|
||||
uptr iov_len;
|
||||
};
|
||||
|
||||
struct __sanitizer_ifaddrs {
|
||||
struct __sanitizer_ifaddrs *ifa_next;
|
||||
char *ifa_name;
|
||||
u64 ifa_flags; // uint64_t
|
||||
void *ifa_addr; // (struct sockaddr *)
|
||||
void *ifa_netmask; // (struct sockaddr *)
|
||||
// This is a union on Linux.
|
||||
# ifdef ifa_dstaddr
|
||||
# undef ifa_dstaddr
|
||||
# endif
|
||||
void *ifa_dstaddr; // (struct sockaddr *)
|
||||
void *ifa_data;
|
||||
};
|
||||
|
||||
typedef unsigned __sanitizer_pthread_key_t;
|
||||
|
||||
struct __sanitizer_XDR {
|
||||
int x_op;
|
||||
void *x_ops;
|
||||
uptr x_public;
|
||||
uptr x_private;
|
||||
uptr x_base;
|
||||
unsigned x_handy;
|
||||
};
|
||||
|
||||
const int __sanitizer_XDR_ENCODE = 0;
|
||||
const int __sanitizer_XDR_DECODE = 1;
|
||||
const int __sanitizer_XDR_FREE = 2;
|
||||
|
||||
struct __sanitizer_passwd {
|
||||
char *pw_name;
|
||||
char *pw_passwd;
|
||||
unsigned int pw_uid; // uid_t
|
||||
unsigned int pw_gid; // gid_t
|
||||
char *pw_age;
|
||||
char *pw_comment;
|
||||
char *pw_gecos;
|
||||
char *pw_dir;
|
||||
char *pw_shell;
|
||||
};
|
||||
|
||||
struct __sanitizer_group {
|
||||
char *gr_name;
|
||||
char *gr_passwd;
|
||||
int gr_gid;
|
||||
char **gr_mem;
|
||||
};
|
||||
|
||||
typedef long __sanitizer_time_t;
|
||||
|
||||
typedef long __sanitizer_suseconds_t;
|
||||
|
||||
struct __sanitizer_timeval {
|
||||
__sanitizer_time_t tv_sec;
|
||||
__sanitizer_suseconds_t tv_usec;
|
||||
};
|
||||
|
||||
struct __sanitizer_itimerval {
|
||||
struct __sanitizer_timeval it_interval;
|
||||
struct __sanitizer_timeval it_value;
|
||||
};
|
||||
|
||||
struct __sanitizer_timeb {
|
||||
__sanitizer_time_t time;
|
||||
unsigned short millitm;
|
||||
short timezone;
|
||||
short dstflag;
|
||||
};
|
||||
|
||||
struct __sanitizer_ether_addr {
|
||||
u8 octet[6];
|
||||
};
|
||||
|
||||
struct __sanitizer_tm {
|
||||
int tm_sec;
|
||||
int tm_min;
|
||||
int tm_hour;
|
||||
int tm_mday;
|
||||
int tm_mon;
|
||||
int tm_year;
|
||||
int tm_wday;
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
};
|
||||
|
||||
struct __sanitizer_msghdr {
|
||||
void *msg_name;
|
||||
unsigned msg_namelen;
|
||||
struct __sanitizer_iovec *msg_iov;
|
||||
unsigned msg_iovlen;
|
||||
void *msg_control;
|
||||
unsigned msg_controllen;
|
||||
int msg_flags;
|
||||
};
|
||||
struct __sanitizer_cmsghdr {
|
||||
unsigned cmsg_len;
|
||||
int cmsg_level;
|
||||
int cmsg_type;
|
||||
};
|
||||
|
||||
#if SANITIZER_SOLARIS && (defined(_LP64) || _FILE_OFFSET_BITS == 64)
|
||||
struct __sanitizer_dirent {
|
||||
unsigned long long d_ino;
|
||||
long long d_off;
|
||||
unsigned short d_reclen;
|
||||
// more fields that we don't care about
|
||||
};
|
||||
#else
|
||||
struct __sanitizer_dirent {
|
||||
unsigned long d_ino;
|
||||
long d_off;
|
||||
unsigned short d_reclen;
|
||||
// more fields that we don't care about
|
||||
};
|
||||
#endif
|
||||
|
||||
struct __sanitizer_dirent64 {
|
||||
unsigned long long d_ino;
|
||||
unsigned long long d_off;
|
||||
unsigned short d_reclen;
|
||||
// more fields that we don't care about
|
||||
};
|
||||
|
||||
typedef long __sanitizer_clock_t;
|
||||
typedef int __sanitizer_clockid_t;
|
||||
|
||||
// This thing depends on the platform. We are only interested in the upper
|
||||
// limit. Verified with a compiler assert in .cpp.
|
||||
union __sanitizer_pthread_attr_t {
|
||||
char size[128];
|
||||
void *align;
|
||||
};
|
||||
|
||||
struct __sanitizer_sigset_t {
|
||||
// uint32_t * 4
|
||||
unsigned int __bits[4];
|
||||
};
|
||||
|
||||
struct __sanitizer_siginfo {
|
||||
// The size is determined by looking at sizeof of real siginfo_t on linux.
|
||||
u64 opaque[128 / sizeof(u64)];
|
||||
};
|
||||
|
||||
using __sanitizer_sighandler_ptr = void (*)(int sig);
|
||||
using __sanitizer_sigactionhandler_ptr =
|
||||
void (*)(int sig, __sanitizer_siginfo *siginfo, void *uctx);
|
||||
|
||||
struct __sanitizer_sigaction {
|
||||
int sa_flags;
|
||||
union {
|
||||
__sanitizer_sigactionhandler_ptr sigaction;
|
||||
__sanitizer_sighandler_ptr handler;
|
||||
};
|
||||
__sanitizer_sigset_t sa_mask;
|
||||
#if !defined(_LP64)
|
||||
int sa_resv[2];
|
||||
#endif
|
||||
};
|
||||
|
||||
struct __sanitizer_kernel_sigset_t {
|
||||
u8 sig[8];
|
||||
};
|
||||
|
||||
struct __sanitizer_kernel_sigaction_t {
|
||||
union {
|
||||
void (*handler)(int signo);
|
||||
void (*sigaction)(int signo, __sanitizer_siginfo *info, void *ctx);
|
||||
};
|
||||
unsigned long sa_flags;
|
||||
void (*sa_restorer)(void);
|
||||
__sanitizer_kernel_sigset_t sa_mask;
|
||||
};
|
||||
|
||||
extern const uptr sig_ign;
|
||||
extern const uptr sig_dfl;
|
||||
extern const uptr sig_err;
|
||||
extern const uptr sa_siginfo;
|
||||
|
||||
extern int af_inet;
|
||||
extern int af_inet6;
|
||||
uptr __sanitizer_in_addr_sz(int af);
|
||||
|
||||
struct __sanitizer_dl_phdr_info {
|
||||
uptr dlpi_addr;
|
||||
const char *dlpi_name;
|
||||
const void *dlpi_phdr;
|
||||
short dlpi_phnum;
|
||||
};
|
||||
|
||||
extern unsigned struct_ElfW_Phdr_sz;
|
||||
|
||||
struct __sanitizer_addrinfo {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
#if defined(__sparcv9)
|
||||
int _ai_pad;
|
||||
#endif
|
||||
unsigned ai_addrlen;
|
||||
char *ai_canonname;
|
||||
void *ai_addr;
|
||||
struct __sanitizer_addrinfo *ai_next;
|
||||
};
|
||||
|
||||
struct __sanitizer_hostent {
|
||||
char *h_name;
|
||||
char **h_aliases;
|
||||
int h_addrtype;
|
||||
int h_length;
|
||||
char **h_addr_list;
|
||||
};
|
||||
|
||||
struct __sanitizer_pollfd {
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
|
||||
typedef unsigned long __sanitizer_nfds_t;
|
||||
|
||||
struct __sanitizer_glob_t {
|
||||
uptr gl_pathc;
|
||||
char **gl_pathv;
|
||||
uptr gl_offs;
|
||||
char **gl_pathp;
|
||||
int gl_pathn;
|
||||
};
|
||||
|
||||
extern int glob_nomatch;
|
||||
extern int glob_altdirfunc;
|
||||
|
||||
extern unsigned path_max;
|
||||
|
||||
struct __sanitizer_wordexp_t {
|
||||
uptr we_wordc;
|
||||
char **we_wordv;
|
||||
uptr we_offs;
|
||||
char **we_wordp;
|
||||
int we_wordn;
|
||||
};
|
||||
|
||||
typedef void __sanitizer_FILE;
|
||||
#define SANITIZER_HAS_STRUCT_FILE 0
|
||||
|
||||
// This simplifies generic code
|
||||
#define struct_shminfo_sz -1
|
||||
#define struct_shm_info_sz -1
|
||||
#define shmctl_shm_stat -1
|
||||
#define shmctl_ipc_info -1
|
||||
#define shmctl_shm_info -1
|
||||
|
||||
extern int shmctl_ipc_stat;
|
||||
|
||||
extern unsigned struct_utmp_sz;
|
||||
extern unsigned struct_utmpx_sz;
|
||||
|
||||
extern int map_fixed;
|
||||
|
||||
// ioctl arguments
|
||||
struct __sanitizer_ifconf {
|
||||
int ifc_len;
|
||||
union {
|
||||
void *ifcu_req;
|
||||
} ifc_ifcu;
|
||||
};
|
||||
|
||||
// <sys/ioccom.h>
|
||||
#define IOC_NRBITS 8
|
||||
#define IOC_TYPEBITS 8
|
||||
#define IOC_SIZEBITS 12
|
||||
#define IOC_DIRBITS 4
|
||||
#undef IOC_NONE
|
||||
#define IOC_NONE 2U // IOC_VOID
|
||||
#define IOC_READ 4U // IOC_OUT
|
||||
#define IOC_WRITE 8U // IOC_IN
|
||||
|
||||
#define IOC_NRMASK ((1 << IOC_NRBITS) - 1)
|
||||
#define IOC_TYPEMASK ((1 << IOC_TYPEBITS) - 1)
|
||||
#define IOC_SIZEMASK ((1 << IOC_SIZEBITS) - 1)
|
||||
#define IOC_DIRMASK ((1 << IOC_DIRBITS) - 1)
|
||||
#define IOC_NRSHIFT 0
|
||||
#define IOC_TYPESHIFT (IOC_NRSHIFT + IOC_NRBITS)
|
||||
#define IOC_SIZESHIFT (IOC_TYPESHIFT + IOC_TYPEBITS)
|
||||
#define IOC_DIRSHIFT (IOC_SIZESHIFT + IOC_SIZEBITS)
|
||||
|
||||
#define IOC_DIR(nr) (((nr) >> IOC_DIRSHIFT) & IOC_DIRMASK)
|
||||
#define IOC_TYPE(nr) (((nr) >> IOC_TYPESHIFT) & IOC_TYPEMASK)
|
||||
#define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK)
|
||||
|
||||
#if defined(__sparc__)
|
||||
// In sparc the 14 bits SIZE field overlaps with the
|
||||
// least significant bit of DIR, so either IOC_READ or
|
||||
// IOC_WRITE shall be 1 in order to get a non-zero SIZE.
|
||||
#define IOC_SIZE(nr) \
|
||||
((((((nr) >> 29) & 0x7) & (4U | 2U)) == 0) ? 0 : (((nr) >> 16) & 0x3fff))
|
||||
#else
|
||||
#define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK)
|
||||
#endif
|
||||
|
||||
extern unsigned struct_ifreq_sz;
|
||||
extern unsigned struct_termios_sz;
|
||||
extern unsigned struct_winsize_sz;
|
||||
|
||||
extern unsigned struct_sioc_sg_req_sz;
|
||||
extern unsigned struct_sioc_vif_req_sz;
|
||||
|
||||
// ioctl request identifiers
|
||||
|
||||
// A special value to mark ioctls that are not present on the target platform,
|
||||
// when it can not be determined without including any system headers.
|
||||
extern const unsigned IOCTL_NOT_PRESENT;
|
||||
|
||||
extern unsigned IOCTL_FIOASYNC;
|
||||
extern unsigned IOCTL_FIOCLEX;
|
||||
extern unsigned IOCTL_FIOGETOWN;
|
||||
extern unsigned IOCTL_FIONBIO;
|
||||
extern unsigned IOCTL_FIONCLEX;
|
||||
extern unsigned IOCTL_FIOSETOWN;
|
||||
extern unsigned IOCTL_SIOCADDMULTI;
|
||||
extern unsigned IOCTL_SIOCATMARK;
|
||||
extern unsigned IOCTL_SIOCDELMULTI;
|
||||
extern unsigned IOCTL_SIOCGIFADDR;
|
||||
extern unsigned IOCTL_SIOCGIFBRDADDR;
|
||||
extern unsigned IOCTL_SIOCGIFCONF;
|
||||
extern unsigned IOCTL_SIOCGIFDSTADDR;
|
||||
extern unsigned IOCTL_SIOCGIFFLAGS;
|
||||
extern unsigned IOCTL_SIOCGIFMETRIC;
|
||||
extern unsigned IOCTL_SIOCGIFMTU;
|
||||
extern unsigned IOCTL_SIOCGIFNETMASK;
|
||||
extern unsigned IOCTL_SIOCGPGRP;
|
||||
extern unsigned IOCTL_SIOCSIFADDR;
|
||||
extern unsigned IOCTL_SIOCSIFBRDADDR;
|
||||
extern unsigned IOCTL_SIOCSIFDSTADDR;
|
||||
extern unsigned IOCTL_SIOCSIFFLAGS;
|
||||
extern unsigned IOCTL_SIOCSIFMETRIC;
|
||||
extern unsigned IOCTL_SIOCSIFMTU;
|
||||
extern unsigned IOCTL_SIOCSIFNETMASK;
|
||||
extern unsigned IOCTL_SIOCSPGRP;
|
||||
extern unsigned IOCTL_TIOCEXCL;
|
||||
extern unsigned IOCTL_TIOCGETD;
|
||||
extern unsigned IOCTL_TIOCGPGRP;
|
||||
extern unsigned IOCTL_TIOCGWINSZ;
|
||||
extern unsigned IOCTL_TIOCMBIC;
|
||||
extern unsigned IOCTL_TIOCMBIS;
|
||||
extern unsigned IOCTL_TIOCMGET;
|
||||
extern unsigned IOCTL_TIOCMSET;
|
||||
extern unsigned IOCTL_TIOCNOTTY;
|
||||
extern unsigned IOCTL_TIOCNXCL;
|
||||
extern unsigned IOCTL_TIOCOUTQ;
|
||||
extern unsigned IOCTL_TIOCPKT;
|
||||
extern unsigned IOCTL_TIOCSCTTY;
|
||||
extern unsigned IOCTL_TIOCSETD;
|
||||
extern unsigned IOCTL_TIOCSPGRP;
|
||||
extern unsigned IOCTL_TIOCSTI;
|
||||
extern unsigned IOCTL_TIOCSWINSZ;
|
||||
extern unsigned IOCTL_MTIOCGET;
|
||||
extern unsigned IOCTL_MTIOCTOP;
|
||||
|
||||
extern const int si_SEGV_MAPERR;
|
||||
extern const int si_SEGV_ACCERR;
|
||||
} // namespace __sanitizer
|
||||
|
||||
#define CHECK_TYPE_SIZE(TYPE) \
|
||||
COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE))
|
||||
|
||||
#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \
|
||||
COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *) NULL)->MEMBER) == \
|
||||
sizeof(((CLASS *) NULL)->MEMBER)); \
|
||||
COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \
|
||||
offsetof(CLASS, MEMBER))
|
||||
|
||||
// For sigaction, which is a function and struct at the same time,
|
||||
// and thus requires explicit "struct" in sizeof() expression.
|
||||
#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \
|
||||
COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *) NULL)->MEMBER) == \
|
||||
sizeof(((struct CLASS *) NULL)->MEMBER)); \
|
||||
COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \
|
||||
offsetof(struct CLASS, MEMBER))
|
||||
|
||||
#endif // SANITIZER_SOLARIS
|
||||
|
||||
#endif
|
||||
398
lib/tsan/sanitizer_common/sanitizer_posix.cpp
Normal file
398
lib/tsan/sanitizer_common/sanitizer_posix.cpp
Normal file
@@ -0,0 +1,398 @@
|
||||
//===-- sanitizer_posix.cpp -----------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries and implements POSIX-specific functions from
|
||||
// sanitizer_posix.h.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
#if SANITIZER_POSIX
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_file.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_posix.h"
|
||||
#include "sanitizer_procmaps.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#if SANITIZER_FREEBSD
|
||||
// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
|
||||
// that, it was never implemented. So just define it to zero.
|
||||
#undef MAP_NORESERVE
|
||||
#define MAP_NORESERVE 0
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// ------------- sanitizer_common.h
|
||||
uptr GetMmapGranularity() {
|
||||
return GetPageSize();
|
||||
}
|
||||
|
||||
void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
|
||||
size = RoundUpTo(size, GetPageSizeCached());
|
||||
uptr res = MmapNamed(nullptr, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON, mem_type);
|
||||
int reserrno;
|
||||
if (UNLIKELY(internal_iserror(res, &reserrno)))
|
||||
ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno, raw_report);
|
||||
IncreaseTotalMmap(size);
|
||||
return (void *)res;
|
||||
}
|
||||
|
||||
void UnmapOrDie(void *addr, uptr size) {
|
||||
if (!addr || !size) return;
|
||||
uptr res = internal_munmap(addr, size);
|
||||
if (UNLIKELY(internal_iserror(res))) {
|
||||
Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
|
||||
SanitizerToolName, size, size, addr);
|
||||
CHECK("unable to unmap" && 0);
|
||||
}
|
||||
DecreaseTotalMmap(size);
|
||||
}
|
||||
|
||||
void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
|
||||
size = RoundUpTo(size, GetPageSizeCached());
|
||||
uptr res = MmapNamed(nullptr, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON, mem_type);
|
||||
int reserrno;
|
||||
if (UNLIKELY(internal_iserror(res, &reserrno))) {
|
||||
if (reserrno == ENOMEM)
|
||||
return nullptr;
|
||||
ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
|
||||
}
|
||||
IncreaseTotalMmap(size);
|
||||
return (void *)res;
|
||||
}
|
||||
|
||||
// We want to map a chunk of address space aligned to 'alignment'.
|
||||
// We do it by mapping a bit more and then unmapping redundant pieces.
|
||||
// We probably can do it with fewer syscalls in some OS-dependent way.
|
||||
void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
|
||||
const char *mem_type) {
|
||||
CHECK(IsPowerOfTwo(size));
|
||||
CHECK(IsPowerOfTwo(alignment));
|
||||
uptr map_size = size + alignment;
|
||||
uptr map_res = (uptr)MmapOrDieOnFatalError(map_size, mem_type);
|
||||
if (UNLIKELY(!map_res))
|
||||
return nullptr;
|
||||
uptr map_end = map_res + map_size;
|
||||
uptr res = map_res;
|
||||
if (!IsAligned(res, alignment)) {
|
||||
res = (map_res + alignment - 1) & ~(alignment - 1);
|
||||
UnmapOrDie((void*)map_res, res - map_res);
|
||||
}
|
||||
uptr end = res + size;
|
||||
if (end != map_end)
|
||||
UnmapOrDie((void*)end, map_end - end);
|
||||
return (void*)res;
|
||||
}
|
||||
|
||||
void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
|
||||
size = RoundUpTo(size, GetPageSizeCached());
|
||||
uptr p = MmapNamed(nullptr, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, mem_type);
|
||||
int reserrno;
|
||||
if (UNLIKELY(internal_iserror(p, &reserrno)))
|
||||
ReportMmapFailureAndDie(size, mem_type, "allocate noreserve", reserrno);
|
||||
IncreaseTotalMmap(size);
|
||||
return (void *)p;
|
||||
}
|
||||
|
||||
static void *MmapFixedImpl(uptr fixed_addr, uptr size, bool tolerate_enomem,
|
||||
const char *name) {
|
||||
size = RoundUpTo(size, GetPageSizeCached());
|
||||
fixed_addr = RoundDownTo(fixed_addr, GetPageSizeCached());
|
||||
uptr p = MmapNamed((void *)fixed_addr, size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED, name);
|
||||
int reserrno;
|
||||
if (UNLIKELY(internal_iserror(p, &reserrno))) {
|
||||
if (tolerate_enomem && reserrno == ENOMEM)
|
||||
return nullptr;
|
||||
char mem_type[40];
|
||||
internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
|
||||
fixed_addr);
|
||||
ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
|
||||
}
|
||||
IncreaseTotalMmap(size);
|
||||
return (void *)p;
|
||||
}
|
||||
|
||||
void *MmapFixedOrDie(uptr fixed_addr, uptr size, const char *name) {
|
||||
return MmapFixedImpl(fixed_addr, size, false /*tolerate_enomem*/, name);
|
||||
}
|
||||
|
||||
void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size, const char *name) {
|
||||
return MmapFixedImpl(fixed_addr, size, true /*tolerate_enomem*/, name);
|
||||
}
|
||||
|
||||
bool MprotectNoAccess(uptr addr, uptr size) {
|
||||
return 0 == internal_mprotect((void*)addr, size, PROT_NONE);
|
||||
}
|
||||
|
||||
bool MprotectReadOnly(uptr addr, uptr size) {
|
||||
return 0 == internal_mprotect((void *)addr, size, PROT_READ);
|
||||
}
|
||||
|
||||
#if !SANITIZER_MAC
|
||||
void MprotectMallocZones(void *addr, int prot) {}
|
||||
#endif
|
||||
|
||||
fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
|
||||
if (ShouldMockFailureToOpen(filename))
|
||||
return kInvalidFd;
|
||||
int flags;
|
||||
switch (mode) {
|
||||
case RdOnly: flags = O_RDONLY; break;
|
||||
case WrOnly: flags = O_WRONLY | O_CREAT | O_TRUNC; break;
|
||||
case RdWr: flags = O_RDWR | O_CREAT; break;
|
||||
}
|
||||
fd_t res = internal_open(filename, flags, 0660);
|
||||
if (internal_iserror(res, errno_p))
|
||||
return kInvalidFd;
|
||||
return ReserveStandardFds(res);
|
||||
}
|
||||
|
||||
void CloseFile(fd_t fd) {
|
||||
internal_close(fd);
|
||||
}
|
||||
|
||||
bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
|
||||
error_t *error_p) {
|
||||
uptr res = internal_read(fd, buff, buff_size);
|
||||
if (internal_iserror(res, error_p))
|
||||
return false;
|
||||
if (bytes_read)
|
||||
*bytes_read = res;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
|
||||
error_t *error_p) {
|
||||
uptr res = internal_write(fd, buff, buff_size);
|
||||
if (internal_iserror(res, error_p))
|
||||
return false;
|
||||
if (bytes_written)
|
||||
*bytes_written = res;
|
||||
return true;
|
||||
}
|
||||
|
||||
void *MapFileToMemory(const char *file_name, uptr *buff_size) {
|
||||
fd_t fd = OpenFile(file_name, RdOnly);
|
||||
CHECK(fd != kInvalidFd);
|
||||
uptr fsize = internal_filesize(fd);
|
||||
CHECK_NE(fsize, (uptr)-1);
|
||||
CHECK_GT(fsize, 0);
|
||||
*buff_size = RoundUpTo(fsize, GetPageSizeCached());
|
||||
uptr map = internal_mmap(nullptr, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
return internal_iserror(map) ? nullptr : (void *)map;
|
||||
}
|
||||
|
||||
void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
|
||||
uptr flags = MAP_SHARED;
|
||||
if (addr) flags |= MAP_FIXED;
|
||||
uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
|
||||
int mmap_errno = 0;
|
||||
if (internal_iserror(p, &mmap_errno)) {
|
||||
Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n",
|
||||
fd, (long long)offset, size, p, mmap_errno);
|
||||
return nullptr;
|
||||
}
|
||||
return (void *)p;
|
||||
}
|
||||
|
||||
static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
|
||||
uptr start2, uptr end2) {
|
||||
CHECK(start1 <= end1);
|
||||
CHECK(start2 <= end2);
|
||||
return (end1 < start2) || (end2 < start1);
|
||||
}
|
||||
|
||||
// FIXME: this is thread-unsafe, but should not cause problems most of the time.
|
||||
// When the shadow is mapped only a single thread usually exists (plus maybe
|
||||
// several worker threads on Mac, which aren't expected to map big chunks of
|
||||
// memory).
|
||||
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
|
||||
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
|
||||
if (proc_maps.Error())
|
||||
return true; // and hope for the best
|
||||
MemoryMappedSegment segment;
|
||||
while (proc_maps.Next(&segment)) {
|
||||
if (segment.start == segment.end) continue; // Empty range.
|
||||
CHECK_NE(0, segment.end);
|
||||
if (!IntervalsAreSeparate(segment.start, segment.end - 1, range_start,
|
||||
range_end))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpProcessMap() {
|
||||
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
|
||||
const sptr kBufSize = 4095;
|
||||
char *filename = (char*)MmapOrDie(kBufSize, __func__);
|
||||
MemoryMappedSegment segment(filename, kBufSize);
|
||||
Report("Process memory map follows:\n");
|
||||
while (proc_maps.Next(&segment)) {
|
||||
Printf("\t%p-%p\t%s\n", (void *)segment.start, (void *)segment.end,
|
||||
segment.filename);
|
||||
}
|
||||
Report("End of process memory map.\n");
|
||||
UnmapOrDie(filename, kBufSize);
|
||||
}
|
||||
|
||||
const char *GetPwd() {
|
||||
return GetEnv("PWD");
|
||||
}
|
||||
|
||||
bool IsPathSeparator(const char c) {
|
||||
return c == '/';
|
||||
}
|
||||
|
||||
bool IsAbsolutePath(const char *path) {
|
||||
return path != nullptr && IsPathSeparator(path[0]);
|
||||
}
|
||||
|
||||
void ReportFile::Write(const char *buffer, uptr length) {
|
||||
SpinMutexLock l(mu);
|
||||
ReopenIfNecessary();
|
||||
internal_write(fd, buffer, length);
|
||||
}
|
||||
|
||||
bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
|
||||
MemoryMappingLayout proc_maps(/*cache_enabled*/false);
|
||||
InternalScopedString buff(kMaxPathLength);
|
||||
MemoryMappedSegment segment(buff.data(), kMaxPathLength);
|
||||
while (proc_maps.Next(&segment)) {
|
||||
if (segment.IsExecutable() &&
|
||||
internal_strcmp(module, segment.filename) == 0) {
|
||||
*start = segment.start;
|
||||
*end = segment.end;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uptr SignalContext::GetAddress() const {
|
||||
auto si = static_cast<const siginfo_t *>(siginfo);
|
||||
return (uptr)si->si_addr;
|
||||
}
|
||||
|
||||
bool SignalContext::IsMemoryAccess() const {
|
||||
auto si = static_cast<const siginfo_t *>(siginfo);
|
||||
return si->si_signo == SIGSEGV;
|
||||
}
|
||||
|
||||
int SignalContext::GetType() const {
|
||||
return static_cast<const siginfo_t *>(siginfo)->si_signo;
|
||||
}
|
||||
|
||||
const char *SignalContext::Describe() const {
|
||||
switch (GetType()) {
|
||||
case SIGFPE:
|
||||
return "FPE";
|
||||
case SIGILL:
|
||||
return "ILL";
|
||||
case SIGABRT:
|
||||
return "ABRT";
|
||||
case SIGSEGV:
|
||||
return "SEGV";
|
||||
case SIGBUS:
|
||||
return "BUS";
|
||||
case SIGTRAP:
|
||||
return "TRAP";
|
||||
}
|
||||
return "UNKNOWN SIGNAL";
|
||||
}
|
||||
|
||||
fd_t ReserveStandardFds(fd_t fd) {
|
||||
CHECK_GE(fd, 0);
|
||||
if (fd > 2)
|
||||
return fd;
|
||||
bool used[3];
|
||||
internal_memset(used, 0, sizeof(used));
|
||||
while (fd <= 2) {
|
||||
used[fd] = true;
|
||||
fd = internal_dup(fd);
|
||||
}
|
||||
for (int i = 0; i <= 2; ++i)
|
||||
if (used[i])
|
||||
internal_close(i);
|
||||
return fd;
|
||||
}
|
||||
|
||||
bool ShouldMockFailureToOpen(const char *path) {
|
||||
return common_flags()->test_only_emulate_no_memorymap &&
|
||||
internal_strncmp(path, "/proc/", 6) == 0;
|
||||
}
|
||||
|
||||
#if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO
|
||||
int GetNamedMappingFd(const char *name, uptr size, int *flags) {
|
||||
if (!common_flags()->decorate_proc_maps || !name)
|
||||
return -1;
|
||||
char shmname[200];
|
||||
CHECK(internal_strlen(name) < sizeof(shmname) - 10);
|
||||
internal_snprintf(shmname, sizeof(shmname), "/dev/shm/%zu [%s]",
|
||||
internal_getpid(), name);
|
||||
int o_cloexec = 0;
|
||||
#if defined(O_CLOEXEC)
|
||||
o_cloexec = O_CLOEXEC;
|
||||
#endif
|
||||
int fd = ReserveStandardFds(
|
||||
internal_open(shmname, O_RDWR | O_CREAT | O_TRUNC | o_cloexec, S_IRWXU));
|
||||
CHECK_GE(fd, 0);
|
||||
if (!o_cloexec) {
|
||||
int res = fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
CHECK_EQ(0, res);
|
||||
}
|
||||
int res = internal_ftruncate(fd, size);
|
||||
CHECK_EQ(0, res);
|
||||
res = internal_unlink(shmname);
|
||||
CHECK_EQ(0, res);
|
||||
*flags &= ~(MAP_ANON | MAP_ANONYMOUS);
|
||||
return fd;
|
||||
}
|
||||
#else
|
||||
int GetNamedMappingFd(const char *name, uptr size, int *flags) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
#define PR_SET_VMA 0x53564d41
|
||||
#define PR_SET_VMA_ANON_NAME 0
|
||||
void DecorateMapping(uptr addr, uptr size, const char *name) {
|
||||
if (!common_flags()->decorate_proc_maps || !name)
|
||||
return;
|
||||
internal_prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, addr, size, (uptr)name);
|
||||
}
|
||||
#else
|
||||
void DecorateMapping(uptr addr, uptr size, const char *name) {
|
||||
}
|
||||
#endif
|
||||
|
||||
uptr MmapNamed(void *addr, uptr length, int prot, int flags, const char *name) {
|
||||
int fd = GetNamedMappingFd(name, length, &flags);
|
||||
uptr res = internal_mmap(addr, length, prot, flags, fd, 0);
|
||||
if (!internal_iserror(res))
|
||||
DecorateMapping(res, length, name);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_POSIX
|
||||
125
lib/tsan/sanitizer_common/sanitizer_posix.h
Normal file
125
lib/tsan/sanitizer_common/sanitizer_posix.h
Normal file
@@ -0,0 +1,125 @@
|
||||
//===-- sanitizer_posix.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries and declares some useful POSIX-specific functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_POSIX_H
|
||||
#define SANITIZER_POSIX_H
|
||||
|
||||
// ----------- ATTENTION -------------
|
||||
// This header should NOT include any other headers from sanitizer runtime.
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_platform_limits_freebsd.h"
|
||||
#include "sanitizer_platform_limits_netbsd.h"
|
||||
#include "sanitizer_platform_limits_openbsd.h"
|
||||
#include "sanitizer_platform_limits_posix.h"
|
||||
#include "sanitizer_platform_limits_solaris.h"
|
||||
|
||||
#if !SANITIZER_POSIX
|
||||
// Make it hard to accidentally use any of functions declared in this file:
|
||||
#error This file should only be included on POSIX
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// I/O
|
||||
// Don't use directly, use __sanitizer::OpenFile() instead.
|
||||
uptr internal_open(const char *filename, int flags);
|
||||
uptr internal_open(const char *filename, int flags, u32 mode);
|
||||
uptr internal_close(fd_t fd);
|
||||
|
||||
uptr internal_read(fd_t fd, void *buf, uptr count);
|
||||
uptr internal_write(fd_t fd, const void *buf, uptr count);
|
||||
|
||||
// Memory
|
||||
uptr internal_mmap(void *addr, uptr length, int prot, int flags,
|
||||
int fd, u64 offset);
|
||||
uptr internal_munmap(void *addr, uptr length);
|
||||
int internal_mprotect(void *addr, uptr length, int prot);
|
||||
|
||||
// OS
|
||||
uptr internal_filesize(fd_t fd); // -1 on error.
|
||||
uptr internal_stat(const char *path, void *buf);
|
||||
uptr internal_lstat(const char *path, void *buf);
|
||||
uptr internal_fstat(fd_t fd, void *buf);
|
||||
uptr internal_dup(int oldfd);
|
||||
uptr internal_dup2(int oldfd, int newfd);
|
||||
uptr internal_readlink(const char *path, char *buf, uptr bufsize);
|
||||
uptr internal_unlink(const char *path);
|
||||
uptr internal_rename(const char *oldpath, const char *newpath);
|
||||
uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
|
||||
|
||||
#if SANITIZER_NETBSD
|
||||
uptr internal_ptrace(int request, int pid, void *addr, int data);
|
||||
#else
|
||||
uptr internal_ptrace(int request, int pid, void *addr, void *data);
|
||||
#endif
|
||||
uptr internal_waitpid(int pid, int *status, int options);
|
||||
|
||||
int internal_fork();
|
||||
fd_t internal_spawn(const char *argv[], const char *envp[], pid_t *pid);
|
||||
|
||||
int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
|
||||
uptr *oldlenp, const void *newp, uptr newlen);
|
||||
int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
|
||||
const void *newp, uptr newlen);
|
||||
|
||||
// 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
|
||||
|
||||
int my_pthread_attr_getstack(void *attr, void **addr, uptr *size);
|
||||
|
||||
// A routine named real_sigaction() must be implemented by each sanitizer in
|
||||
// order for internal_sigaction() to bypass interceptors.
|
||||
int internal_sigaction(int signum, const void *act, void *oldact);
|
||||
void internal_sigfillset(__sanitizer_sigset_t *set);
|
||||
void internal_sigemptyset(__sanitizer_sigset_t *set);
|
||||
bool internal_sigismember(__sanitizer_sigset_t *set, int signum);
|
||||
|
||||
uptr internal_execve(const char *filename, char *const argv[],
|
||||
char *const envp[]);
|
||||
|
||||
bool IsStateDetached(int state);
|
||||
|
||||
// Move the fd out of {0, 1, 2} range.
|
||||
fd_t ReserveStandardFds(fd_t fd);
|
||||
|
||||
bool ShouldMockFailureToOpen(const char *path);
|
||||
|
||||
// Create a non-file mapping with a given /proc/self/maps name.
|
||||
uptr MmapNamed(void *addr, uptr length, int prot, int flags, const char *name);
|
||||
|
||||
// Platforms should implement at most one of these.
|
||||
// 1. Provide a pre-decorated file descriptor to use instead of an anonymous
|
||||
// mapping.
|
||||
int GetNamedMappingFd(const char *name, uptr size, int *flags);
|
||||
// 2. Add name to an existing anonymous mapping. The caller must keep *name
|
||||
// alive at least as long as the mapping exists.
|
||||
void DecorateMapping(uptr addr, uptr size, const char *name);
|
||||
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_POSIX_H
|
||||
358
lib/tsan/sanitizer_common/sanitizer_printf.cpp
Normal file
358
lib/tsan/sanitizer_common/sanitizer_printf.cpp
Normal file
@@ -0,0 +1,358 @@
|
||||
//===-- sanitizer_printf.cpp ----------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer.
|
||||
//
|
||||
// Internal printf function, used inside run-time libraries.
|
||||
// We can't use libc printf because we intercept some of the functions used
|
||||
// inside it.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_libc.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 && \
|
||||
!defined(va_copy)
|
||||
# define va_copy(dst, src) ((dst) = (src))
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
static int AppendChar(char **buff, const char *buff_end, char c) {
|
||||
if (*buff < buff_end) {
|
||||
**buff = c;
|
||||
(*buff)++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Appends number in a given base to buffer. If its length is less than
|
||||
// |minimal_num_length|, it is padded with leading zeroes or spaces, depending
|
||||
// on the value of |pad_with_zero|.
|
||||
static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
|
||||
u8 base, u8 minimal_num_length, bool pad_with_zero,
|
||||
bool negative, bool uppercase) {
|
||||
uptr const kMaxLen = 30;
|
||||
RAW_CHECK(base == 10 || base == 16);
|
||||
RAW_CHECK(base == 10 || !negative);
|
||||
RAW_CHECK(absolute_value || !negative);
|
||||
RAW_CHECK(minimal_num_length < kMaxLen);
|
||||
int result = 0;
|
||||
if (negative && minimal_num_length)
|
||||
--minimal_num_length;
|
||||
if (negative && pad_with_zero)
|
||||
result += AppendChar(buff, buff_end, '-');
|
||||
uptr num_buffer[kMaxLen];
|
||||
int pos = 0;
|
||||
do {
|
||||
RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow");
|
||||
num_buffer[pos++] = absolute_value % base;
|
||||
absolute_value /= base;
|
||||
} while (absolute_value > 0);
|
||||
if (pos < minimal_num_length) {
|
||||
// Make sure compiler doesn't insert call to memset here.
|
||||
internal_memset(&num_buffer[pos], 0,
|
||||
sizeof(num_buffer[0]) * (minimal_num_length - pos));
|
||||
pos = minimal_num_length;
|
||||
}
|
||||
RAW_CHECK(pos > 0);
|
||||
pos--;
|
||||
for (; pos >= 0 && num_buffer[pos] == 0; pos--) {
|
||||
char c = (pad_with_zero || pos == 0) ? '0' : ' ';
|
||||
result += AppendChar(buff, buff_end, c);
|
||||
}
|
||||
if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-');
|
||||
for (; pos >= 0; pos--) {
|
||||
char digit = static_cast<char>(num_buffer[pos]);
|
||||
digit = (digit < 10) ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10;
|
||||
result += AppendChar(buff, buff_end, digit);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base,
|
||||
u8 minimal_num_length, bool pad_with_zero,
|
||||
bool uppercase) {
|
||||
return AppendNumber(buff, buff_end, num, base, minimal_num_length,
|
||||
pad_with_zero, false /* negative */, uppercase);
|
||||
}
|
||||
|
||||
static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num,
|
||||
u8 minimal_num_length, bool pad_with_zero) {
|
||||
bool negative = (num < 0);
|
||||
return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10,
|
||||
minimal_num_length, pad_with_zero, negative,
|
||||
false /* uppercase */);
|
||||
}
|
||||
|
||||
|
||||
// Use the fact that explicitly requesting 0 width (%0s) results in UB and
|
||||
// interpret width == 0 as "no width requested":
|
||||
// width == 0 - no width requested
|
||||
// width < 0 - left-justify s within and pad it to -width chars, if necessary
|
||||
// width > 0 - right-justify s, not implemented yet
|
||||
static int AppendString(char **buff, const char *buff_end, int width,
|
||||
int max_chars, const char *s) {
|
||||
if (!s)
|
||||
s = "<null>";
|
||||
int result = 0;
|
||||
for (; *s; s++) {
|
||||
if (max_chars >= 0 && result >= max_chars)
|
||||
break;
|
||||
result += AppendChar(buff, buff_end, *s);
|
||||
}
|
||||
// Only the left justified strings are supported.
|
||||
while (width < -result)
|
||||
result += AppendChar(buff, buff_end, ' ');
|
||||
return result;
|
||||
}
|
||||
|
||||
static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
|
||||
int result = 0;
|
||||
result += AppendString(buff, buff_end, 0, -1, "0x");
|
||||
result += AppendUnsigned(buff, buff_end, ptr_value, 16,
|
||||
SANITIZER_POINTER_FORMAT_LENGTH,
|
||||
true /* pad_with_zero */, false /* uppercase */);
|
||||
return result;
|
||||
}
|
||||
|
||||
int VSNPrintf(char *buff, int buff_length,
|
||||
const char *format, va_list args) {
|
||||
static const char *kPrintfFormatsHelp =
|
||||
"Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
|
||||
"%[-]([0-9]*)?(\\.\\*)?s; %c\n";
|
||||
RAW_CHECK(format);
|
||||
RAW_CHECK(buff_length > 0);
|
||||
const char *buff_end = &buff[buff_length - 1];
|
||||
const char *cur = format;
|
||||
int result = 0;
|
||||
for (; *cur; cur++) {
|
||||
if (*cur != '%') {
|
||||
result += AppendChar(&buff, buff_end, *cur);
|
||||
continue;
|
||||
}
|
||||
cur++;
|
||||
bool left_justified = *cur == '-';
|
||||
if (left_justified)
|
||||
cur++;
|
||||
bool have_width = (*cur >= '0' && *cur <= '9');
|
||||
bool pad_with_zero = (*cur == '0');
|
||||
int width = 0;
|
||||
if (have_width) {
|
||||
while (*cur >= '0' && *cur <= '9') {
|
||||
width = width * 10 + *cur++ - '0';
|
||||
}
|
||||
}
|
||||
bool have_precision = (cur[0] == '.' && cur[1] == '*');
|
||||
int precision = -1;
|
||||
if (have_precision) {
|
||||
cur += 2;
|
||||
precision = va_arg(args, int);
|
||||
}
|
||||
bool have_z = (*cur == 'z');
|
||||
cur += have_z;
|
||||
bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l');
|
||||
cur += have_ll * 2;
|
||||
s64 dval;
|
||||
u64 uval;
|
||||
const bool have_length = have_z || have_ll;
|
||||
const bool have_flags = have_width || have_length;
|
||||
// At the moment only %s supports precision and left-justification.
|
||||
CHECK(!((precision >= 0 || left_justified) && *cur != 's'));
|
||||
switch (*cur) {
|
||||
case 'd': {
|
||||
dval = have_ll ? va_arg(args, s64)
|
||||
: have_z ? va_arg(args, sptr)
|
||||
: va_arg(args, int);
|
||||
result += AppendSignedDecimal(&buff, buff_end, dval, width,
|
||||
pad_with_zero);
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X': {
|
||||
uval = have_ll ? va_arg(args, u64)
|
||||
: have_z ? va_arg(args, uptr)
|
||||
: va_arg(args, unsigned);
|
||||
bool uppercase = (*cur == 'X');
|
||||
result += AppendUnsigned(&buff, buff_end, uval, (*cur == 'u') ? 10 : 16,
|
||||
width, pad_with_zero, uppercase);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
|
||||
result += AppendPointer(&buff, buff_end, va_arg(args, uptr));
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
RAW_CHECK_MSG(!have_length, kPrintfFormatsHelp);
|
||||
// Only left-justified width is supported.
|
||||
CHECK(!have_width || left_justified);
|
||||
result += AppendString(&buff, buff_end, left_justified ? -width : width,
|
||||
precision, va_arg(args, char*));
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
|
||||
result += AppendChar(&buff, buff_end, va_arg(args, int));
|
||||
break;
|
||||
}
|
||||
case '%' : {
|
||||
RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
|
||||
result += AppendChar(&buff, buff_end, '%');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
RAW_CHECK_MSG(false, kPrintfFormatsHelp);
|
||||
}
|
||||
}
|
||||
}
|
||||
RAW_CHECK(buff <= buff_end);
|
||||
AppendChar(&buff, buff_end + 1, '\0');
|
||||
return result;
|
||||
}
|
||||
|
||||
static void (*PrintfAndReportCallback)(const char *);
|
||||
void SetPrintfAndReportCallback(void (*callback)(const char *)) {
|
||||
PrintfAndReportCallback = callback;
|
||||
}
|
||||
|
||||
// Can be overriden in frontend.
|
||||
#if SANITIZER_GO && defined(TSAN_EXTERNAL_HOOKS)
|
||||
// Implementation must be defined in frontend.
|
||||
extern "C" void __sanitizer_on_print(const char *str);
|
||||
#else
|
||||
SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_on_print, const char *str) {
|
||||
(void)str;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void CallPrintfAndReportCallback(const char *str) {
|
||||
__sanitizer_on_print(str);
|
||||
if (PrintfAndReportCallback)
|
||||
PrintfAndReportCallback(str);
|
||||
}
|
||||
|
||||
static void NOINLINE SharedPrintfCodeNoBuffer(bool append_pid,
|
||||
char *local_buffer,
|
||||
int buffer_size,
|
||||
const char *format,
|
||||
va_list args) {
|
||||
va_list args2;
|
||||
va_copy(args2, args);
|
||||
const int kLen = 16 * 1024;
|
||||
int needed_length;
|
||||
char *buffer = local_buffer;
|
||||
// First try to print a message using a local buffer, and then fall back to
|
||||
// mmaped buffer.
|
||||
for (int use_mmap = 0; use_mmap < 2; use_mmap++) {
|
||||
if (use_mmap) {
|
||||
va_end(args);
|
||||
va_copy(args, args2);
|
||||
buffer = (char*)MmapOrDie(kLen, "Report");
|
||||
buffer_size = kLen;
|
||||
}
|
||||
needed_length = 0;
|
||||
// Check that data fits into the current buffer.
|
||||
# define CHECK_NEEDED_LENGTH \
|
||||
if (needed_length >= buffer_size) { \
|
||||
if (!use_mmap) continue; \
|
||||
RAW_CHECK_MSG(needed_length < kLen, \
|
||||
"Buffer in Report is too short!\n"); \
|
||||
}
|
||||
// Fuchsia's logging infrastructure always keeps track of the logging
|
||||
// process, thread, and timestamp, so never prepend such information.
|
||||
if (!SANITIZER_FUCHSIA && append_pid) {
|
||||
int pid = internal_getpid();
|
||||
const char *exe_name = GetProcessName();
|
||||
if (common_flags()->log_exe_name && exe_name) {
|
||||
needed_length += internal_snprintf(buffer, buffer_size,
|
||||
"==%s", exe_name);
|
||||
CHECK_NEEDED_LENGTH
|
||||
}
|
||||
needed_length += internal_snprintf(
|
||||
buffer + needed_length, buffer_size - needed_length, "==%d==", pid);
|
||||
CHECK_NEEDED_LENGTH
|
||||
}
|
||||
needed_length += VSNPrintf(buffer + needed_length,
|
||||
buffer_size - needed_length, format, args);
|
||||
CHECK_NEEDED_LENGTH
|
||||
// If the message fit into the buffer, print it and exit.
|
||||
break;
|
||||
# undef CHECK_NEEDED_LENGTH
|
||||
}
|
||||
RawWrite(buffer);
|
||||
|
||||
// Remove color sequences from the message.
|
||||
RemoveANSIEscapeSequencesFromString(buffer);
|
||||
CallPrintfAndReportCallback(buffer);
|
||||
LogMessageOnPrintf(buffer);
|
||||
|
||||
// If we had mapped any memory, clean up.
|
||||
if (buffer != local_buffer)
|
||||
UnmapOrDie((void *)buffer, buffer_size);
|
||||
va_end(args2);
|
||||
}
|
||||
|
||||
static void NOINLINE SharedPrintfCode(bool append_pid, const char *format,
|
||||
va_list args) {
|
||||
// |local_buffer| is small enough not to overflow the stack and/or violate
|
||||
// the stack limit enforced by TSan (-Wframe-larger-than=512). On the other
|
||||
// hand, the bigger the buffer is, the more the chance the error report will
|
||||
// fit into it.
|
||||
char local_buffer[400];
|
||||
SharedPrintfCodeNoBuffer(append_pid, local_buffer, ARRAY_SIZE(local_buffer),
|
||||
format, args);
|
||||
}
|
||||
|
||||
FORMAT(1, 2)
|
||||
void Printf(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
SharedPrintfCode(false, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Like Printf, but prints the current PID before the output string.
|
||||
FORMAT(1, 2)
|
||||
void Report(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
SharedPrintfCode(true, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Writes at most "length" symbols to "buffer" (including trailing '\0').
|
||||
// Returns the number of symbols that should have been written to buffer
|
||||
// (not including trailing '\0'). Thus, the string is truncated
|
||||
// iff return value is not less than "length".
|
||||
FORMAT(3, 4)
|
||||
int internal_snprintf(char *buffer, uptr length, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
int needed_length = VSNPrintf(buffer, length, format, args);
|
||||
va_end(args);
|
||||
return needed_length;
|
||||
}
|
||||
|
||||
FORMAT(2, 3)
|
||||
void InternalScopedString::append(const char *format, ...) {
|
||||
CHECK_LT(length_, size());
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
VSNPrintf(data() + length_, size() - length_, format, args);
|
||||
va_end(args);
|
||||
length_ += internal_strlen(data() + length_);
|
||||
CHECK_LT(length_, size());
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user